# Commands for the database, settings and translations

These commands change what sits beside your code: the database's
migrations, your local settings, and the wording of your app in each
language.

| Command | Does |
|---|---|
| `jadpo migrate new <purpose>` / `apply` / `status` | Writes, runs and lists migrations |
| `jadpo seed <name>` | Fills the development database from a seed |
| `jadpo config move <constant>` | Makes a constant a setting |
| `jadpo config set <field>` / `check` | Sets or checks your local settings in `.env.local` |
| `jadpo config generate <field>` | Writes a new key into a pipe, never to a terminal |
| `jadpo config rotation` | Counts the sealed rows still under the previous field key |
| `jadpo translations missing` / `unused` / `update` / `accept` | Shows and records the state of each language's wording |
| `jadpo translations builtin` | Lists Jadpo's own messages your app can show |
| `jadpo translations export` / `import` | Exchanges wording with translation services |

| Command | What it does |
|---|---|
| [`jadpo migrate`](/docs/jadpo-migrate.md) | Write and apply migrations. |
| [`jadpo seed`](/docs/jadpo-seed.md) | Fill the development database from a seed. |
| [`jadpo config`](/docs/jadpo-config.md) | Check and set local settings. |
| [`jadpo translations`](/docs/jadpo-translations.md) | Keep translations up to date. |

## `jadpo migrate`

`new` writes the next migration, or finishes the latest once its
decisions are written ([Migrations](/docs/migrations.md)). `apply` and
`status` take the database's URL with `--url`, and `--store` when there
are several. Otherwise they read it from the connection's setting, in the
environment and then `.env.local`.

`new <purpose> --drop <name>` also removes for good a retired field or
entity, named as `changes.jadpo` names it, `Todo.legacy_flag` or `Todo`,
one to a migration ([Removing data](/docs/migrations.md#removing-data)). It
refuses anything not retired, and while the latest migration waits for
its decisions. With nothing to migrate, `new` lists what is still retired.
`apply` lists what each migration it ran removed, and refuses a project
with a removal nobody accepted
([`migration.drop_not_accepted`](/docs/errors/migration.drop_not_accepted.md)).

A missing purpose will be asked for at a prompt, once prompts are built.
Until then the command names a purpose to use.

## `jadpo seed`

`jadpo seed <name>` runs `database/seeds/<name>.jadpo`
([Seeds](/docs/seeds.md)) against the development database. Each
store's database is the one `--url` names, for a project with one store,
else the address its connection's setting gives, else the one a running
`jadpo dev` started. Pending migrations are applied first.

```text
$ jadpo seed first_staff
✓ Applied 1 migration to `main`

✓ Seeded `first_staff` in 154 ms
    main  localhost:5432/library_dev
```

The address is shown without its credentials. A seed that stops shows
where, as a test's failure does: the failure or fault, its code, the
calls that led there, and that what its earlier calls committed stays
(`seed.failed`). A database that isn't on this machine is refused before
anything runs (`seed.production_refused`).

## `jadpo config move`

`jadpo config move <constant>` turns a constant into a setting: a
`config` field of its name and type, with its value as `default:`. It
deletes the constant and rewrites every read to `config.<name>`
([Making a constant a setting](/docs/settings-config-jadpo.md#making-a-constant-a-setting)).

Name the constant as code reads it (`security.max_login_attempts`), or
after its domain. It lists what changed, file by file, then the setting
and its variable. It refuses with `configuration.move_type_unsupported`,
`configuration.move_name_taken` and `configuration.move_read_unsupported`,
every reason at once, and changes nothing. A move whose result wouldn't
check is undone. It takes `--json`.

## `jadpo config set` and `jadpo config check`

`check` lists each setting as `set`, `default`, `missing` or `invalid`,
with its variable, against the environment and then `.env.local`. It
reports each problem as `configuration.value_missing` or
`configuration.value_invalid` and never prints a value. It takes `--json`.

`set` asks for one value at a prompt, hidden for a secret, so the value
never goes on the command line. It refuses `--json` and a missing
terminal. It checks the value against the setting's type, then writes
`.env.local`: only as an ordinary file, through a private temporary file,
and only if the file hasn't changed since it was read. It adds
`.env.local` to `.gitignore`.

`set <field> --generate` writes a new key for a setting of a key type in the
same way, with no prompt and no terminal, and prints nothing, so no transcript
holds it; with `--json`, a document naming the setting and never the value. A
setting that isn't a key is `cli.config_arguments`, and one that already has a
value, in the environment or the file, is `cli.config_generate_refused`.

`generate <field>` writes a new key alone to its output, for a pipe into a
secret manager, and keeps nothing. At a terminal, or with `--json`, it is
`cli.config_generate_refused`.

`rotation` counts each sealed field's rows against the application's
`field_encryption_key:` and its `_PREVIOUS`, read as `check` reads them, in the
connection's database or the one `--url` names: how many hold a value, and how
many of them the current key, the previous key and neither sealed. It ends on
what to do: `FIELD_ENCRYPTION_KEY_PREVIOUS can go`, `Rotation under way`, `No
rotation under way`, or `Rows no key opens`, the only one of them it colours as
a problem. It answers either way, exiting 0, and its `--json` holds
`can_remove_previous`. It never prints a key
([Rotating the key](/docs/encrypted-fields.md#rotating-the-key)). A database it can't
read is `cli.config_rotation_unavailable`.

## `jadpo translations`

Each of these takes `--json` ([Tooling for
translations](/docs/tooling-for-translations.md)).

- `missing [<locale>]` lists, under the language file each is in, the
  wording a locale lacks and what a locale still in progress falls back
  on. It also lists translations made from wording that has since changed,
  and wording not yet accepted.
- `unused` lists the messages no code calls, and the app's wording of
  library packages' text that shows nothing.
- `update` records each translation's source in
  `translations/sources.jadpo_record`.
- `accept <locale>` accepts a locale's changed wording.

`missing` and `unused` succeed whatever they list. `update` and `accept`
stop on syntax errors, write one status line for each record, and say
`still stale` of a translation nobody redid. A misspelt action or locale is
`cli.translations_arguments`, with the nearest name.

`jadpo translations builtin` lists Jadpo's own messages your app can
show, by feature: their fields, the wording Jadpo ships and the locales
that still need it. It lists only the messages of features your app has
on, takes `--json` and succeeds whatever it lists. `missing`, `update` and
`accept` take the root `translations/` folder's wording of these messages
in with the rest ([Jadpo's and packages'
text](/docs/failures-for-client-apps.md#jadpos-and-packages-text)).

`jadpo translations export [<locale>]` and `import <file>` exchange
messages, their descriptions and their wording with translation services,
as XLIFF 2.0 or JSON. Each takes `--json`.

- `export` writes `build/translations/<locale>.xlf` for each translated
  locale, or the default locale's source file. `--format json` and
  `--out <folder>` change the format and the folder.
- `import` checks what comes back as `jadpo check` would. It writes the
  language files and the record, or nothing. It refuses with the language
  files' own rules, at the unit of the file, or with
  `translation.exchange_unit_unknown` or
  `translation.exchange_file_invalid`.

Library packages' text is exchanged under each package's folder name, and
written back as the app's, in the root file.
