Every command bin/eezo and sbt "run <command>" answer, with flags, exit codes and what each
one touches. The outputs on this page were captured from the bookshelf application the
tutorials build; the version string in them changes with every commit.
bin/eezo is a shell script. Four of its commands are its own: new, build, deploy and
upgrade. dev runs the sbt task eezoDev and shows a console built from its output: a spinner for the
compile, the application's boot lines and logs, and none of sbt's narration. Everything else forwards to the application's own
main as sbt "run <command> <args>", so eezo db status and sbt "run db status" are the
same code path, and so is the sbt task eezoStatus.
The application's main is inherited from its entry trait. Each layer adds its commands: the
http layer's two at the top level, and the database layer's eight under one namespace, db, so
that eezo db sync --apply reads as what it touches. help is matched before either, so no
layer can shadow it. No arguments runs the application.
| command | layer | needs | what it does |
|---|---|---|---|
new <name> |
launcher | ~/.eezo/eezo-version |
scaffolds ./<name> |
dev |
sbt task | serves with restart on save, the route listing and the reload client; Ctrl-C stops it | |
routes [--json] |
http layer | the mounted table and boot's warnings | |
db |
database layer | the eight schema commands, listed; db help is the same |
|
db status [--json] |
database layer | a database | the model against the live database |
db sync [--apply] [--force] [--json] |
database layer | a database | the diff, applied directly with --apply |
db freeze <name...> [--accept-all|--skip-destructive] [--json] |
database layer | writes a migration from the snapshot diff | |
db migrate [--apply] [--json] |
database layer | a database | lists pending migrations, applies them, verifies |
db reset [--json] |
database layer | a database | drops every table and recreates from the model |
db drop [--json] |
database layer | a database | drops every table and the ledger |
db dump [--json] |
database layer | prints the model's snapshot | |
db ddl [--json] |
database layer | prints the full DDL, one statement per line | |
build |
launcher | stages target/eezo/stage/ |
|
deploy [--app <name>] [--target fly] |
launcher | the Fly CLI | builds, deploys, migrates, waits for health |
upgrade |
launcher | Maven Central | moves the command line, what new scaffolds, and the project in the current directory to the latest release |
help |
both layers | the command table |
db freeze takes everything after it that isn't a flag as the migration's name, joined with
spaces, so eezo db freeze add isbn to book names the migration add_isbn_to_book. Without a
flag it prompts on each destructive change, [d] to drop, [k] to keep and skip.
The application also answers the eight without the db in front, and help doesn't list that
spelling. It exists for one caller: a fly.toml written by eezo deploy before the namespace,
whose release command says migrate --apply and runs inside the new image. The launcher
refuses the bare form and names the db one.
deploy on a first run, with no fly.toml, runs fly launch, and in a session with no
terminal that needs --app <name>. Before deploying an application with migrations it checks
that a DATABASE_URL or EEZO_DB_URL secret exists. After deploying it polls
/eezo/health for up to ninety seconds.
status, sync, migrate, reset and drop run with a database installed, and so does the
application itself. freeze, dump, ddl, routes and help never touch one: freeze
diffs the model against db/schema.json, and the rest read the model alone. dev on the
umbrella runs the drift check first, and serves with the database down after a warning.
Every layer command takes --json, which renders the same result through the JSON renderer.
Exit codes are the same in both modes. Each body carries a "command" discriminator. A failure
line goes to stderr as {"error": "..."}.
$ eezo db status --json
{
"command": "status",
"inSync": false,
"changes": [
{
"describe": "- book.notes",
"destructive": true,
"risky": false,
"sql": "alter table \"book\" drop column \"notes\""
}
]
}
The shapes, one per command:
| command | fields |
|---|---|
routes |
routes, overridden, shadowed (pairs of earlier, later), orphans; a route is method, path, provenance |
status |
inSync, changes |
sync |
applied, refused, changes, blocked |
freeze |
migration (path or null), resolutions (each a change and a decision: accept, skip, manual) |
migrate |
outcome: tampered with problems; upToDate with drift; pending with pending; applied with applied and drift |
reset |
dropped, ddl |
drop |
dropped |
dump |
schema, the snapshot |
ddl |
statements |
A change is describe, destructive, risky and sql. A pending migration is number,
file and statements.
--json on freeze implies --skip-destructive, because a machine caller must never hang on
a prompt.
| code | meaning |
|---|---|
| 0 | done, or in sync |
| 1 | db status found drift; db sync --apply refused over destructive or risky changes; db migrate found the files tampered, or drift remains after the ledger is settled; a schema error; a failed deploy or build |
| 2 | unknown command, or unknown db command |
The launcher passes the application's exit code through. sbt prints three lines of its own when a forked application exits 1, and the launcher drops exactly those, because that exit code is the command's answer and not a build failure:
[error] Nonzero exit code returned from runner: 1
[error] (Compile / run) Nonzero exit code: 1
[error] Total time: ...
~/.eezo/eezo-version, written by the installer: theversion,scalaVersion,jdkFloorandsbt.versionthatnewwrites into a scaffold. The installer reads all four off Maven Central: the latest release from the artifact's metadata, the Scala version from the release's POM, and the JDK floor and sbt version from two properties the POM carries.upgraderuns the installer again and then rewrites the versions in thebuild.sbtandproject/plugins.sbtof the current directory, when they name an eezo artifact. In a checkout of the repository,.eezo-versionat its root wins, written bysbt publishLocalForExamplewith the locally published version, andsbt.versioncomes fromexamples/project/build.properties;upgraderefuses to run there.SBT_OPTS, to which it appends--enable-native-access=ALL-UNNAMED --sun-misc-unsafe-memory-access=allow, so sbt's own JVM prints no native-access warnings. sbt itself runs with-error -batch, so only compile errors and the application's output reach the terminal. The forked application getsrun / javaOptions, notSBT_OPTS.fly.toml, for the app name and region on deploy.
<name>/
project/build.properties sbt.version
project/plugins.sbt addSbtPlugin("io.eezo" % "sbt-eezo" % <version>)
build.sbt the umbrella dependency, EezoPlugin, run / fork := true
src/main/scala/Main.scala object Main extends EezoApp, Schema.empty, Routes.table()
src/main/scala/app/Index.scala GET /
src/main/scala/models/ empty
.gitignore
Configuration is what the commands read from the environment. Migrations is the files the schema commands write and the ledger they keep.