Skip to content

CLI Reference

devituz edited this page Jun 3, 2026 · 1 revision

CLI reference

lagodev ships two interchangeable binaries — lago and artisan — built from cmd/lago and cmd/artisan respectively. They share the entire command tree.

go install github.com/devituz/lagodev/cmd/lago@latest      # → lago
# or
go install github.com/devituz/lagodev/cmd/artisan@latest   # → artisan

This guide uses lago; substitute artisan if you prefer.


Project setup

Command Description
lago new <name> Scaffold a brand-new project directory
lago init Write lago.json with the default path layout
lago env:init Write a documented starter .env file
lago env Print the active env (sensitive values redacted)
lago env --show-secrets Show passwords/keys in clear text
lago key:generate Write a fresh APP_KEY (AES-256, base64) to .env

lago new <name>

Scaffolds a complete project:

lago new blog
cd blog
go run .

Drops in main.go, go.mod, lago.json, .env, config/, routes/, and stub package directories — everything needed to start running make:* against it.

lago init

Writes lago.json in the current working directory:

{
  "paths": {
    "models":     "models",
    "migrations": "migrations",
    "factories":  "factories",
    "seeders":    "seeders",
    "tests":      "tests"
  }
}

Adjust to fit your layout — see Configuration.

lago env:init

Writes ./.env with every variable the framework understands, commented out where you don't need to set them.

lago env:init               # uses ./.env
lago env:init --path .env.example
lago env:init --force       # overwrite an existing file

lago env

Prints the active environment. Sensitive values (anything matching PASSWORD, SECRET, KEY, TOKEN) are replaced with ***.

lago env                    # redacted
lago env --show-secrets     # full output

lago key:generate

Generates a 32-byte AES-256 key and writes it to APP_KEY= in the specified .env. Refuses to overwrite a non-empty APP_KEY unless --force is passed.

lago key:generate                # writes to ./.env
lago key:generate --env .env.prod --force
lago key:generate --print-only   # stdout only, no file touched
lago key:generate --show         # write AND print to stdout

Generators

All make:* commands accept --fields=name:type[:modifier] (comma list), --force (overwrite), and --dir (output directory override).

Command Description
lago make:model Generate a model struct (embed orm.Model)
lago make:migration Generate a timestamped migration with up/down
lago make:factory Generate a faker-powered factory
lago make:seeder Generate a seeder registered in init()
lago make:test Generate a test scaffold using lagotest.SQLite
lago make:service Generate a framework-agnostic CRUD service
lago make:controller Generate a REST controller wrapping a service
lago make:crud One-shot: model + migration + factory + seeder + test

lago make:model

lago make:model User                                  # minimal: just the struct
lago make:model Post -m -f -s -t -c \
    --fields="title:string,body:text,published:bool:default(false)"

-mfsc compose. Each flag adds another artifact:

Flag Generates
-m, --migration Migration file
-f, --factory Factory
-s, --seeder Seeder
-t, --test Test scaffold
-c, --controller Service + controller
-a, --all All of the above

lago make:controller

lago make:controller PostController --model=Post

Generates a *web.Context-style REST controller (Index, Show, Store, Update, Destroy) that delegates to a PostService. Pair with --framework=gin to emit a Gin-flavoured variant via the adapters/gin adapter.

lago make:migration

lago make:migration create_users_table
lago make:migration add_role_to_users_table

The runner infers a sensible default table name from the migration's name. Override fields with --fields=...:

lago make:migration create_invoices_table \
    --fields="number:string:unique,total:decimal,issued_at:datetime"

lago make:crud

The fastest path from "I want a Post resource" to a working API:

lago make:crud Post \
    --fields="title:string,body:text,published:bool:default(false)"

Equivalent to make:model -mfsct.

Field spec syntax

name:type[:modifier1][:modifier2]...

Types: string, text, longtext, char, int, bigint, smallint, tinyint, uint, bool, float, double, decimal, json, jsonb, uuid, date, datetime, timestamp, time, binary.

Modifiers: nullable, unique, index, primary, default(VALUE).

Examples:

--fields="email:string:unique"
--fields="age:int:default(18)"
--fields="bio:text:nullable"
--fields="published_at:datetime:nullable"
--fields="role:string:default('user'):index"

The same spec feeds three artifacts simultaneously:

Artifact Result
Model Email string \column:"email"``
Migration t.String("email").Unique()
Factory Email: f.Email() (auto-picks an apt faker call by name+type)

Migrations

lago migrate                                # apply all pending
lago migrate --pretend                      # print SQL, don't execute
lago migrate --path 2026_01_01_000001_create_users_table   # only that one
lago migrate:status                         # tabular: applied / pending
lago migrate:rollback                       # last batch
lago migrate:rollback --step=3              # roll back 3 most recent
lago migrate:reset                          # roll back EVERYTHING
lago migrate:refresh                        # reset + up
lago migrate:fresh                          # DROP all tables, then up
lago migrate:fresh --seed                   # fresh + run seeders
lago migrate:step --n=2                     # apply at most 2 pending

Useful flags across the migrate family:

  • --pretend — render the SQL without executing.
  • --lock-timeout=30s — bound the wait on the advisory lock.
  • --allow-out-of-order — bypass the safety check when squashing.

See Migrations-and-Schema for what the safety checks actually do.

Database

lago db:seed                                # all seeders, dependency-ordered
lago db:seed UserSeeder PostSeeder          # only these (+ their deps)
lago db:seed --class UserSeeder             # Laravel-compatible alias
lago db:seed --transactional                # each seeder in its own tx
lago db:wipe --force                        # DROP every user table
lago db:show                                # driver, host, tz, tables + row counts
lago db:table users                         # columns, types, nullability
lago db                                     # interactive SQL prompt (\q to exit)

db:wipe requires --force so you can't drop production by reflex. db:show is the fastest way to confirm you're connected to the right database — it prints the resolved DSN host + driver alongside the table list.

Custom binary

You can build your own CLI with the same surface plus your commands. The pattern is:

package main

import (
    _ "github.com/devituz/lagodev/drivers/postgres"

    "github.com/devituz/lagodev/cli"
    _ "myapp/db/migrations"
    _ "myapp/db/seeders"
)

func main() {
    app := cli.New(cli.Options{ProjectName: "myapp"})
    app.AddCommand(myCustomCommand())
    app.Execute()
}

cli.Options lets you inject a custom *database.Manager, custom migration/seeder registries, and a custom connector if you don't want the default .env-driven one. This is how you embed lagodev's CLI surface into a larger app's tooling.

See also

Clone this wiki locally