CLI referencia

add seeder

Seeder hozzáadása a projekt központi seeds/ package-éhez, automatikus regisztrációval.

Seedert akkor érdemes elővenni, amikor a projektnek a migrációk lefutása után, de még bármilyen felhasználói művelet előtt kell léteznie valamilyen alapadatnak: egy admin fiók, amivel be lehet lépni, egy fix szerepkör- vagy lookup-lista, amire egy idegen kulcs hivatkozik, demo adat egy staging környezethez. A felhasználói művelet hatására létrejövő üzleti sorok helye egy service, nem ez; a seeder azokra a sorokra való, amikről az app feltételezi, hogy már ott vannak.

go tool gpsystem add seeder <v>

Egy seeder-stubot ír a projekt központi seeds/ package-ébe, és regisztrálja a seeds/register.go-ban. A seeder egy seed.Seeder-t ad vissza: nevet, opcionális Guardot (eldönti, lefusson-e egyáltalán) és a Run törzset. Részletek és minták: Seederek.

go tool gpsystem add seeder adminUser

Mit generál

seeds/admin_user.go
func adminUser(deps Deps) seed.Seeder {
    return seed.Seeder{
        Name: "admin_user",
        Run: func(ctx context.Context) error {
            return nil
        },
    }
}
seeds/register.go
func Register(reg *seed.Registry, deps Deps) {
    reg.Add(adminUser(deps))
    // gpsystem:seeds
}

A Deps (a seeds/register.go-ban, new project-től kezdve minden projektben ott van) a projekt --db választása szerint *pg.DB vagy *bun.DB mezőt hordoz, plusz egy dbx.Transactor-t. A parancs a new project óta minden projektben meglévő seeds/register.go-t várja; ha hiányzik, hibázik, és jelzi, hogy hiányzik.

Egy kidolgozott, több sort érintő példa

A seederek regisztrációs sorrendben futnak (az Add megőrzi a sorrendet), ez pedig számít, mihelyt az egyik seeder sorai egy másik idegen kulcsát adják: egy szerepkörkészletnek léteznie kell, mielőtt az admin user, ami hivatkozik rá, létrejön. Két seeder, ebben a sorrendben hozzáadva:

go tool gpsystem add seeder roles
go tool gpsystem add seeder adminUser
seeds/roles.go
func roles(deps Deps) seed.Seeder {
    names := []string{"admin", "editor", "viewer"}
    return seed.Seeder{
        Name: "roles",
        Run: func(ctx context.Context) error {
            for _, name := range names {
                if _, err := deps.DB.NewInsert().
                    Model(&Role{Name: name}).
                    On("CONFLICT (name) DO NOTHING").
                    Exec(ctx); err != nil {
                    return fmt.Errorf("seed role %q: %w", name, err)
                }
            }
            return nil
        },
    }
}
seeds/admin_user.go
func adminUser(deps Deps) seed.Seeder {
    return seed.Seeder{
        Name: "admin_user",
        Guard: func(ctx context.Context) (bool, error) {
            exists, err := deps.DB.NewSelect().Model((*User)(nil)).
                Where("email = ?", "admin@example.com").Exists(ctx)
            return !exists, err
        },
        Run: func(ctx context.Context) error {
            hash, err := bcrypt.GenerateFromPassword([]byte(mustEnv("SEED_ADMIN_PASSWORD")), bcrypt.DefaultCost)
            if err != nil {
                return err
            }
            _, err = deps.DB.NewInsert().Model(&User{
                Email:        "admin@example.com",
                PasswordHash: string(hash),
                Role:         "admin",
            }).Exec(ctx)
            return err
        },
    }
}
seeds/register.go
func Register(reg *seed.Registry, deps Deps) {
    reg.Add(roles(deps))
    reg.Add(adminUser(deps))
    // gpsystem:seeds
}

A roles az ON CONFLICT DO NOTHING-ot használja, hogy önmagában is idempotens maradjon ismételt futtatásnál; az adminUser Guardja ugyanezt a célt éri el úgy, hogy előbb megnézi, létezik-e már a sor, így a futás tiszta skipként naplózódik egy duplikált kulcs miatti hiba helyett a második migrate up --seed-nél. Mindkettő helyes megoldás: azt válaszd, amelyik jobban illik az adott adathoz.

Futtatás

go run ./cmd/migrate up --seed   # migrál, majd seedel
go run ./cmd/migrate seed        # csak seedel
Az add seeder önmagában csak a stubot írja meg és regisztrálja: az üzleti logikát (mit tölts be, milyen feltétellel) a generált fájlba magadnak kell megírnod. Lásd a Seederek oldalt a Guard, ErrSkip és a multi-tenant WithTenants mintákért.
Copyright © 2026