@metabase/rde 0.1.15 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +51 -57
  2. package/dist/cli.mjs +917 -719
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @metabase/rde
2
2
 
3
- `rde` prepares a Mac or Linux machine for data-engineering work with Metabase and coding agents. One command, `rde init`, sets up everything a working setup needs: the `mb` CLI, the `rde` skill for your coding agents, a running Metabase Enterprise instance (in Docker, or a jar run with the machine's Java), an admin user, an API key wired into `mb`, a Postgres sample database with transforms switched on, your license, the databases you want to work with, and a git repository connected through Metabase's remote sync. It decides what it can on its own and asks only for what it cannot know: your license token, a connection string for your own database, and with a license, where the git repository lives. Every step checks whether it is already done, so the wizard can be interrupted at any prompt and picked up again with the same command.
3
+ `rde` prepares a Mac or Linux machine for data-engineering work with Metabase and coding agents. One command, `rde init`, sets up everything a working setup needs: the `mb` CLI, the `rde` skill for your coding agents, a running Metabase Enterprise instance (in Docker, or a jar run with the machine's Java), an admin user, an API key wired into `mb`, transforms switched on, your license, a database of your own for transforms to write to, and a git repository connected through Metabase's remote sync. It decides what it can on its own and asks only for what it cannot know: your license token, a connection string for your own database (`init` goes no further without one), and with a license, where the git repository lives. Every step checks whether it is already done, so the wizard can be interrupted at any prompt and picked up again with the same command.
4
4
 
5
5
  After `init`, the rest is lifecycle: `rde start`, `rde stop`, `rde status`, `rde logs`, `rde open`, and `rde db add` when another database comes along. The instance lives under `~/.rde`, listens on `127.0.0.1` only, and is used through `mb` and through the agent skill.
6
6
 
@@ -36,7 +36,6 @@ The wizard shows its checklist first (`✓` done, `○` to do, `↑` done but be
36
36
  ○ Create the admin user
37
37
  ○ Connect the Metabase CLI
38
38
  ○ Enable transforms
39
- ○ Connect a sample database
40
39
  ○ Activate your license
41
40
  – Connect a git repository
42
41
  ○ Connect your databases
@@ -45,11 +44,11 @@ The wizard shows its checklist first (`✓` done, `○` to do, `↑` done but be
45
44
  ● Running npm install -g @metabase/cli@alpha-release-65-integration-branch
46
45
  ```
47
46
 
48
- The steps up to the license run without a question, save one: `mb` is installed with the package manager that installed `rde`, the skill goes to the coding agents you pick from the ones found on the machine (at a terminal; without one, to every agent found), Metabase runs in Docker when Docker is there (the jar with your Java otherwise), on the first free port from 23000 up that no other container or instance claims, with an admin user whose password is generated and kept in `~/.rde`, `mb` is logged in under its `default` profile (the one question here comes when `default` already points at another Metabase), and a Postgres sample database comes up in a second container, connected to Metabase as a place transforms can write. Each of those choices is printed as it is made and can be overridden with a flag. The prompts that remain are the license token (Enter skips it), with a license that includes remote sync where the git repository lives and which remote it uses, and your own databases (Enter skips them).
47
+ The steps up to the license run without a question, save one: `mb` is installed with the package manager that installed `rde`, the skill goes to the coding agents you pick from the ones found on the machine (at a terminal; without one, to every agent found), Metabase runs in Docker when Docker is there (the jar with your Java otherwise), on the first free port from 23000 up that no other container or instance claims, with an admin user whose password is generated and kept in `~/.rde`, `mb` is logged in under its `default` profile (the one question here comes when `default` already points at another Metabase), and transforms are switched on. Each of those choices is printed as it is made and can be overridden with a flag. The prompts that remain are the license token (Enter skips it), with a license that includes remote sync where the git repository lives and which remote it uses, and your own database, which `init` needs.
49
48
 
50
- The databases step asks once. At a terminal, without `--yes` or `--skip-databases`, and while none of your own databases is connected, it lists what is connected (the sample database) and asks for a connection string: paste one and `rde` reads the engine from it (or asks which, when the string fits several, or shows the engine list when it fits none), then shows the connection for review, tests it, adds it, and offers another. Enter skips it, and the skip is remembered, so the next `rde init` does not ask again; `rde init --only databases` and `rde db add` still do. With `--yes` or without a terminal it asks nothing and the sample database stands in; `--database` connects yours ahead of time.
49
+ The databases step is the one `init` cannot do without: transforms write their tables to a database of your own, so `init` goes no further until one is connected, and it has to be connected as a user that can create tables. A database already on the instance counts, whether `rde` connected it or you did in Metabase's admin settings. At a terminal, without `--yes`, while there is none, the step says so and asks for a connection string: paste one and `rde` reads the engine from it (or asks which, when the string fits several, or shows the engine list when it fits none), then shows the connection for review, tests it, adds it, and offers another. Enter goes to the engine list, which offers no way to finish until a database is connected; Ctrl-C stops the run, and the next `rde init` asks again. `--database`, or the answers file's `databases` section, names it ahead of time. With `--yes` or without a terminal and nothing named, the step fails with exit code 2 and names both. Metabase starts with `MB_LOAD_SAMPLE_CONTENT=false`, so its bundled H2 sample database, which cannot be a transform target, and the example content built on it are never created.
51
50
 
52
- It runs after the git step because content imported from a repository names its databases, and Metabase only finds them by name. Every database the content names that this instance lacks becomes a stub: a placeholder with that name and no connection, which keeps the content importable and which Metabase leaves out of its own database lists. When there are stubs, the step asks for them instead of the connection string: a checklist of their names, none picked, where you pick the few you work on (a repository can name dozens, most of them someone else's or out of reach from your machine). Each one picked asks for its connection string (Enter goes to the engine list, whose last choice leaves that one unconnected), then review, test, and connect: the stub becomes the real database under the same name and id, so the content on it works and its sync starts. Picking none skips the step like Enter does. The ones left over are named at the end of the step and in the summary, and `rde db add` and `rde db list` show them until they are connected. A database named ahead of time (`--database`, an answers file, `rde db add --url --name`) whose name is a stub's connects that stub too, never a second database of the same name.
51
+ It runs after the git step because content imported from a repository names its databases, and Metabase only finds them by name. Every database the content names that this instance lacks becomes a stub: a placeholder with that name and no connection, which keeps the content importable and which Metabase leaves out of its own database lists. When there are stubs, the step asks for them instead of the connection string: a checklist of their names, none picked, where you pick the few you work on (a repository can name dozens, most of them someone else's or out of reach from your machine). Each one picked asks for its connection string (Enter goes to the engine list, whose last choice leaves that one unconnected), then review, test, and connect: the stub becomes the real database under the same name and id, so the content on it works and its sync starts. Picking none goes on to the connection-string question while no database is connected. The ones left over are named at the end of the step and in the summary, and `rde db add` and `rde db list` show them until they are connected. A database named ahead of time (`--database`, an answers file, `rde db add --url --name`) whose name is a stub's connects that stub too, never a second database of the same name.
53
52
 
54
53
  The git step reads `–` until a license with remote sync is active; it is checked again when its turn comes, so a run that activates a license connects the repository in the same pass.
55
54
 
@@ -63,11 +62,11 @@ The git step reads `–` until a license with remote sync is active; it is check
63
62
  | Create the admin user | `admin@metabase.local` with a generated password that `rde credentials` shows, or your own with `--admin prompted`, see [Credentials](#credentials) |
64
63
  | Connect the Metabase CLI | Creates an API key named `rde` and logs `mb` in with it under the profile `default`, so plain `mb` uses the instance; when `default` already points at another Metabase it asks first, see [The `mb` profile](#the-mb-profile); a key Metabase rejects is replaced |
65
64
  | Enable transforms | Turns transforms on, unless someone already set them either way: turned off in the admin settings, they stay off on every later `rde init`, and the admin settings turn them back on |
66
- | Connect a sample database | Runs Metabase's Postgres sample data (`metabase/qa-databases:postgres-sample-15`) in a container named `rde-sampledb-default` on the first free port from 25432 (or one Docker picks, when another program or a concurrent `rde init` takes that port first), and connects it as `Sample Database`. It replaces the H2 sample database Metabase would otherwise bundle, which cannot be a transform target. Needs Docker, so a jar instance on a machine without Docker has no sample database |
67
- | Activate your license | Optional. Paste a token, or press Enter to skip; remote sync needs one with the `remote-sync` feature |
65
+ | Activate your license | Optional. Paste a token, or press Enter to skip. The token unlocks remote sync (the `remote-sync` feature, which the git step needs), data apps (`data-apps`), and transform tests (`transforms-testing`); skipping the license goes without all three, and an activated token that lacks some is named with the ones it lacks. A token Metabase refuses is asked for again at a terminal |
68
66
  | Connect a git repository | Only with a license. Initializes or reuses a repository and points Metabase's remote sync at it, with transforms included in the sync (the Transforms row under Collections to sync), and imports the content it already holds |
69
- | Connect your databases | At a terminal, until you connect one of your own or skip it: pick the databases the imported content uses from a checklist, or, with none, paste a connection string (Enter skips); review, test, add; repeat. `--database` names them ahead of time; with `--yes` or without a terminal nothing is asked. `rde db add` starts from the same checklist, then the engine list, where the fields can also be filled one by one |
70
- | Summary | URL, login email, the `mb` command for the instance, databases, repository, and the command that opens each detected agent |
67
+ | Choose what syncs to git | Only once a repository is connected. Creates the Library (when the license has the `library` feature) and an `Analytics` collection, unless the repository already brought synced top-level collections in, and marks them for sync. Before it changes anything it asks whether to push them to the branch (default yes); without the push, or when the branch has commits Metabase has not imported, it prints the export to run instead |
68
+ | Connect your databases | Required: `init` goes no further until a database of your own is connected, as a user that can create the tables transforms write. At a terminal: pick the databases the imported content uses from a checklist, or, with none, paste a connection string (Enter goes to the engine list); review, test, add; repeat. `--database` names them ahead of time; with `--yes` or without a terminal and none named, the step fails naming `--database` and the answers file. `rde db add` starts from the same checklist, then the engine list, where the fields can also be filled one by one |
69
+ | Summary | URL, login email, the `mb` command for the instance, databases, repository, the export still to run when Metabase has unexported changes, and the command that opens each detected agent |
71
70
 
72
71
  The second run on the same machine detects every step as done and prints the summary only:
73
72
 
@@ -81,43 +80,30 @@ The second run on the same machine detects every step as done and prints the sum
81
80
  ✓ Create the admin user
82
81
  ✓ Connect the Metabase CLI
83
82
  ✓ Enable transforms
84
- ✓ Connect a sample database
85
83
  – Activate your license
86
84
  – Connect a git repository
87
85
  ✓ Connect your databases
88
86
  ○ Summary
89
87
  │
90
- ◇ Your environment ─────────────────────────────────────────────────────────────────╮
91
- │ │
92
- │ Metabase: http://localhost:23000 │
93
- │ Login: admin@metabase.local (rde credentials shows the password) │
94
- │ Metabase CLI: mb <command> │
95
- │ Databases: │
96
- │ Sample Database (postgres, sync complete) │
97
- │ warehouse (postgres, syncing; `rde status` shows when it finishes) │
98
- │ Sample database: psql "postgres://metabase:metasample123@localhost:25432/sample" │
99
- │ Remote sync: not configured (no license) │
100
- │ rde skill: installed for Claude Code │
101
- │ Start working: │
102
- │ claude (Claude Code) │
103
- │ Lifecycle: rde status (--json for scripts), rde stop, rde start │
104
- │ │
105
- ├────────────────────────────────────────────────────────────────────────────────────╯
88
+ ◇ Your environment ─────────────────────────────────────────────────────╮
89
+ │ │
90
+ │ Metabase: http://localhost:23000 │
91
+ │ Login: admin@metabase.local (rde credentials shows the password) │
92
+ │ Metabase CLI: mb <command> │
93
+ │ Databases: │
94
+ │ warehouse (postgres, syncing; `rde status` shows when it finishes) │
95
+ │ Remote sync: not configured (no license) │
96
+ │ rde skill: installed for Claude Code │
97
+ │ Start working: │
98
+ │ claude (Claude Code) │
99
+ │ Lifecycle: rde status (--json for scripts), rde stop, rde start │
100
+ │ │
101
+ ├────────────────────────────────────────────────────────────────────────╯
106
102
  │
107
103
  └ rde init finished
108
104
  ```
109
105
 
110
- Ctrl-C at any prompt stops the run; `rde init` again continues from the first step that is not done. A run on an instance that already exists says so first (`Resuming instance "default" (Metabase http://localhost:23000, repository /Users/you/rde-content); steps already done are skipped.`, and that `rde uninstall` comes first to start over). A flag or answers-file section for a step that does not run, because it is already done, does not apply (a license skipped before), or is left out by `--only`, is ignored with a warning naming the flag and the step and how to get it done instead: `--database ignored: "Connect your databases" is already done. Connect another with rde db add --url <connection string>.` A skip flag for a step that is already done asks nothing of it and is not warned about. A step that fails ends the run with exit code 1 (2 for a missing answer) and a last line naming the step and the command that resumes there: `rde init stopped at "Connect a sample database": <reason>. Resume with rde init --from sample-database.` `rde init --from license` re-runs from a step on (a later step that does not apply, such as the git step without a license that has remote sync, stays skipped), `rde init --only databases` runs one step.
111
-
112
- ## The sample database
113
-
114
- Every instance gets a second container, `rde-sampledb-default`, running `metabase/qa-databases:postgres-sample-15`: the Postgres edition of Metabase's own sample data, the same tables and rows the bundled H2 sample database has (`orders`, `products`, `people`, `reviews`, `accounts`, `invoices`, `feedback`, `analytic_events`), with the user `metabase`, the password `metasample123`, and the database `sample`. `init` publishes it on `127.0.0.1` at the first free port from 25432 up (5432 is usually your own Postgres, and 15432 a common stand-in for it), connects it to Metabase as `Sample Database`, and sets `transforms-enabled`, so transforms have somewhere to write on the first run: the H2 sample database refuses to be a transform target, this one creates the target schema on demand. The `metabase` user is the cluster superuser, so any schema name works. Metabase is started with `MB_LOAD_SAMPLE_CONTENT=false`, so the H2 sample database and the example content built on it are never created and the Postgres one is the only `Sample Database`.
115
-
116
- ```sh
117
- psql "postgres://metabase:metasample123@localhost:25432/sample" # the summary and `rde status` show the port
118
- ```
119
-
120
- `rde start` and `rde stop` bring the container up and down with Metabase, `rde status` shows its state and sync, `rde doctor` checks it, and `rde uninstall` removes it (with `--remove-image`, its image too). The data lives inside the container: `stop`/`start` keep it, and a container that is removed comes back with the pristine sample data on the next `rde init`. The jar runtime gets the sample database as well whenever Docker is available; on a machine without Docker the step is `–` and there is no sample database at all (the H2 one is switched off for every instance).
106
+ Ctrl-C at any prompt stops the run; `rde init` again continues from the first step that is not done. A run on an instance that already exists says so first (`Resuming instance "default" (Metabase http://localhost:23000, repository /Users/you/rde-content); steps already done are skipped.`, and that `rde uninstall` comes first to start over). A flag or answers-file section for a step that does not run, because it is already done, does not apply (a license skipped before), or is left out by `--only`, is ignored with a warning naming the flag and the step and how to get it done instead: `--database ignored: "Connect your databases" is already done. Connect another with rde db add --url <connection string>.` A skip flag for a step that is already done asks nothing of it and is not warned about. A step that fails ends the run with exit code 1 (2 for a missing answer) and a last line naming the step and the command that resumes there: `rde init stopped at "Connect your databases": <reason>. Resume with rde init --from databases.` `rde init --from license` re-runs from a step on (a later step that does not apply, such as the git step without a license that has remote sync, stays skipped), `rde init --only databases` runs one step.
121
107
 
122
108
  ## Credentials
123
109
 
@@ -133,8 +119,6 @@ In both variants `rde` creates an API key named `rde` in the Administrators grou
133
119
 
134
120
  An instance whose state file held the public `admin@metabase.com` pair keeps working: the password and the key move to `secrets.json` the first time any `rde` command reads the state, and `rde doctor` marks the admin password `legacy-default`, because anyone who knows the default can log in. When the instance holds nothing you need, `rde uninstall` then `rde init` recreates it with a generated password.
135
121
 
136
- The sample database's own credentials (`metabase` / `metasample123`) are baked into its image and are not a secret.
137
-
138
122
  ## What lands in `~/.rde`, and what never does
139
123
 
140
124
  ```
@@ -142,7 +126,7 @@ The sample database's own credentials (`metabase` / `metasample123`) are baked i
142
126
  config.json { schemaVersion, defaultInstance }
143
127
  instances/default/ mode 0700
144
128
  state.json mode 0600, no secrets: runtime, url, Metabase version, admin email and variant,
145
- mb profile, license features, databases, sample database, git remote, step records
129
+ mb profile, license features, databases, git remote, step records
146
130
  secrets.json mode 0600: { schemaVersion, admin: { email, password } | null, apiKey }
147
131
  data/ mounted into the container as /rde, or the jar's working directory
148
132
  metabase.db/metabase.db.mv.db the H2 application database
@@ -156,7 +140,7 @@ The sample database's own credentials (`metabase` / `metasample123`) are baked i
156
140
 
157
141
  Three things are never written to disk or to a log by `rde`: the license token (it goes to Metabase, which keeps it in its own application database), a password you typed for a prompted admin user, and a git access token for a hosted remote (Metabase encrypts it on its side). The `mb` profile lives where `mb` keeps its profiles, under `$XDG_CONFIG_HOME` or `~/.config`.
158
142
 
159
- The instance listens on `127.0.0.1` only (the container's published port, or the jar's `MB_JETTY_HOST`), so it is reachable from this machine and from nothing else on the network; the sample database's container is published the same way. Two containers belong to the instance: `rde-metabase-default` (Metabase, with `data/` mounted) and `rde-sampledb-default` (Postgres, no mount). Those are the names under `~/.rde`; with `RDE_HOME` set to another directory both names end in a tag derived from that directory, such as `rde-metabase-default-3fa9c1d2`, so two homes never share a container.
143
+ The instance listens on `127.0.0.1` only (the container's published port, or the jar's `MB_JETTY_HOST`), so it is reachable from this machine and from nothing else on the network. Its container is `rde-metabase-default`, with `data/` mounted. That is the name under `~/.rde`; with `RDE_HOME` set to another directory the name ends in a tag derived from that directory, such as `rde-metabase-default-3fa9c1d2`, so two homes never share a container.
160
144
 
161
145
  ### Docker or jar
162
146
 
@@ -166,9 +150,9 @@ Docker is used whenever it is available: the image is self-contained, and the co
166
150
 
167
151
  | Command | What it does |
168
152
  | --- | --- |
169
- | `rde start` | Starts the sample database and the instance, and waits until Metabase is healthy; `--port <n>` moves the instance to another port first (see [The instance's port is taken](#troubleshooting)) |
170
- | `rde stop` | Stops both; the data stays |
171
- | `rde status` | The login email, the `mb` profile, the init checklist, whether setup is complete (or the first step `init` did not finish, with the `rde init --from` command that resumes there), runtime state, health, databases with their sync state, the sample database's container and connection string, remote sync state and the content repository's working directory (`contentRepository`); `--json` for scripts and agents (`mbProfile`, `adminEmail`, `setup`, never a secret); exit 0 whatever it reports |
153
+ | `rde start` | Starts the instance, and waits until Metabase is healthy; `--port <n>` moves the instance to another port first (see [The instance's port is taken](#troubleshooting)) |
154
+ | `rde stop` | Stops the instance; the data stays |
155
+ | `rde status` | The login email, the `mb` profile, the init checklist, whether setup is complete (or the first step `init` did not finish, with the `rde init --from` command that resumes there), runtime state, health, databases with their sync state, remote sync state with the collections it syncs, and the content repository's working directory (`contentRepository`); `--json` for scripts and agents (`mbProfile`, `adminEmail`, `setup`, never a secret); exit 0 whatever it reports |
172
156
  | `rde credentials` | The Metabase URL, admin email, and password; `--json` for scripts and agents; `--api-key` for the API key alone |
173
157
  | `rde logs` | The container's log, or the jar's `metabase.log`; `-f` keeps streaming |
174
158
  | `rde open` | Opens the instance in the browser |
@@ -201,7 +185,11 @@ With a license that includes remote sync, the step after the license connects a
201
185
  - **Local remote inside `~/.rde`** (default): a bare repository at `instances/default/data/content.git`, mounted into the container and reached as `file:///rde/content.git`. No account, no token. Your working directory gets it as `origin` (or as `rde-remote` when `origin` already points elsewhere).
202
186
  - **Hosted remote**: a GitHub, GitLab, or Bitbucket `https://` URL plus a token with read and write access. `rde` tests the connection through Metabase before saving anything, then clones the repository into the working directory (an existing repository gets the URL as `origin` instead); an empty hosted repository gets the first commit pushed to it. The token is handed to Metabase and, for the clone and that first push, to git in its environment; it never lands in the clone's config, the remote URL, or a credential helper, so a later `git pull` uses your own git credentials.
203
187
 
204
- A fresh repository gets a first commit with a `README.md` and a `.gitignore` that keeps out what a data app built in it must not push: `.env`, `.env.local`, `.env.*.local`, `node_modules/`, `.vite/`, the rde skill's `.scratch/`, and `.DS_Store` (a data app's built `dist/` stays tracked, since that bundle is what Metabase serves); a `.gitignore` already there keeps its lines and gets the missing ones, and a repository that already has commits is left as it is. A repository that already holds Metabase content is imported on the spot; each database it names that this instance lacks becomes a stub, which the databases step right after offers to connect. Afterwards:
188
+ A fresh repository gets a first commit with a `README.md` and a `.gitignore` that keeps out what a data app built in it must not push: `.env`, `.env.local`, `.env.*.local`, `node_modules/`, `.vite/`, the rde skill's `.scratch/`, and `.DS_Store` (a data app's built `dist/` stays tracked, since that bundle is what Metabase serves); a `.gitignore` already there keeps its lines and gets the missing ones, and a repository that already has commits is left as it is. A repository that already holds Metabase content is imported on the spot; each database it names that this instance lacks becomes a stub, which the databases step offers to connect.
189
+
190
+ Remote sync carries only what is marked for it, so the step after the repository decides what that is: the Library (published tables with their measures and segments in Data, metrics in Metrics), and one top-level collection for dashboards, questions, models, and documents; transforms already sync through the repository step. The collection is `Analytics`, created for the purpose (beside any `Analytics` of the user's, which stays out of git), unless the import brought top-level collections the repository already syncs, in which case those are used and nothing is created; the Library is created only when the import did not bring one, since a second Library would conflict with the repository's on the next import. Anything filed outside these collections stays on this instance. The step refuses to run after an import that did not finish cleanly, and names the import to run first. While remote sync is read-only, the repository decides what syncs: `rde init` skips the step, and `rde init --only sync-scope` stops and asks for read-write in Admin. `rde init --only sync-scope` runs it again; it creates nothing that is already there.
191
+
192
+ Marking the scope is itself a change Metabase has not exported, and until it is exported a plain `mb git-sync import` refuses ("There are unsaved changes in the Remote Sync collection"). So the step asks, before it creates or marks anything, whether to push the scope to the branch, and on yes runs `mb git-sync export` and checks that Metabase is in sync afterwards. It asks because the branch may be one others share. On no, or when the push fails, it prints `mb git-sync export -b <branch>` and the summary repeats it while Metabase has unexported changes. When the branch has commits Metabase has not imported, it asks nothing and pushes nothing: a plain export would end in a conflict, which counts the remote commit as synced, so it names `mb git-sync export-preflight` and `mb git-sync export -b <branch> --merge` instead. Afterwards:
205
193
 
206
194
  ```sh
207
195
  cd <your working directory>
@@ -212,7 +200,7 @@ git pull # after an export, to see the YAML files
212
200
 
213
201
  ## Running `init` without a terminal
214
202
 
215
- Every prompt can be answered ahead of time, by a flag or by a JSON answers file, so an agent or a CI job can run `rde init` unattended. The choices `init` makes on its own need no answer, so a run without a terminal only has to cover the license, the databases, and the git repository. When stdin is not a terminal, the first prompt nothing answers fails with exit code 2 and a message naming the step, the prompt, and the flag or answers key that would have answered it; nothing hangs. A text prompt with a default (a display name) takes its default, as Enter would.
203
+ Every prompt can be answered ahead of time, by a flag or by a JSON answers file, so an agent or a CI job can run `rde init` unattended. The choices `init` makes on its own need no answer, so a run without a terminal only has to cover the license, the databases, the git repository, and whether to push the sync scope. When stdin is not a terminal, the first prompt nothing answers fails with exit code 2 and a message naming the step, the prompt, and the flag or answers key that would have answered it; nothing hangs. A text prompt with a default (a display name) takes its default, as Enter would.
216
204
 
217
205
  ### Flags
218
206
 
@@ -225,13 +213,13 @@ rde init --skip-license \
225
213
  | Flag | Answers |
226
214
  | --- | --- |
227
215
  | `--agents claude-code,codex` / `--skip-agents` | Which agents get the skill, instead of asking at a terminal or taking the ones found on the machine |
228
- | `--runtime docker\|jar`, `--image`, `--jar`, `--port` | How Metabase runs, the Docker image or the jar (`latest`, a release tag, or a `.jar` path), and the host port, instead of Docker when available, `metabase/metabase-enterprise-head:latest`, the latest release, and the first free port from 23000 that nothing else claims; `--image` implies `docker` and `--jar` implies `jar`; the sample database always runs in Docker |
216
+ | `--runtime docker\|jar`, `--image`, `--jar`, `--port` | How Metabase runs, the Docker image or the jar (`latest`, a release tag, or a `.jar` path), and the host port, instead of Docker when available, `metabase/metabase-enterprise-head:latest`, the latest release, and the first free port from 23000 that nothing else claims; `--image` implies `docker` and `--jar` implies `jar` |
229
217
  | `--admin generated\|prompted`, `--admin-email`, `--admin-password` | The admin user, `generated` unless given; an email or password implies `prompted` |
230
218
  | `--license-token` / `--skip-license` | The license |
231
- | `--database <connection string>` (repeatable) / `--skip-databases` | Your own databases, the engine read from the string; `<engine>=<connection string>` names it when the string fits several (`druid=jdbc:avatica:…`); `--skip-databases` connects none of them, and the sample database is still connected |
219
+ | `--database <connection string>` (repeatable) | Your own databases, the engine read from the string; `<engine>=<connection string>` names it when the string fits several (`druid=jdbc:avatica:…`). Without a terminal, one of them or the answers file's `databases` section is required unless the instance already has a database |
232
220
  | `--git-dir`, `--git-remote local\|hosted`, `--git-url`, `--git-token` / `--skip-git` | The content repository; a remote or URL without `--git-dir` means the current directory |
233
221
  | `--mb-profile default\|rde` | The `mb` profile when `default` already points at another Metabase; `default` (replace it) unless given |
234
- | `--yes` | Every confirmation, and the recommended choice of a question (replacing `default`); skips the question about your own databases, and the sample database is still connected. A driver's toggle (SSL, an SSH tunnel) is a setting, not a confirmation, and keeps the driver's default |
222
+ | `--yes` | Every confirmation, and the recommended choice of a question (replacing `default`); your own database then comes from `--database` or the answers file, as without a terminal. A driver's toggle (SSL, an SSH tunnel) is a setting, not a confirmation, and keeps the driver's default |
235
223
 
236
224
  A secret flag (`--admin-password`, `--license-token`, `--git-token`) takes `-` to read one line from stdin, the way `mb auth login` takes its key, or a literal `$VAR` (single-quoted, so the shell leaves it alone) to read the environment; either keeps the value out of your shell history and process listings. Only one flag can read stdin.
237
225
 
@@ -254,11 +242,12 @@ rde init --license-token '$RDE_LICENSE_TOKEN' …
254
242
  { "engine": "postgres", "uri": "$RDE_DATABASE_1_URI" },
255
243
  { "engine": "bigquery-cloud-sdk", "fields": { "project-id": "acme", "service-account-json": "/keys/acme.json" }, "name": "Warehouse" }
256
244
  ],
257
- "git-sync": { "location": "new", "path": "~/analytics", "remote": "local-bare" }
245
+ "git-sync": { "location": "new", "path": "~/analytics", "remote": "local-bare" },
246
+ "sync-scope": { "export": true }
258
247
  }
259
248
  ```
260
249
 
261
- `"runtime"` takes `"kind": "docker"` or `"jar"`, which `"image"` and `"jar"` imply on their own (`"runtime": { "jar": "latest" }` runs the latest release with Java). `"license": { "token": null }` skips the license, `"databases": []` connects none, `"git-sync": { "location": "skip" }` leaves the repository for later. A database is pasted as a `uri` or entered with `fields`, keyed by the driver's field names as the admin form shows them; a file field takes a path. Anything a `fields` entry leaves out is asked in the terminal, or named in the error without one. A field that engine does not ask with the others given, such as one behind the admin form's advanced options, is named in a warning and ignored.
250
+ `"runtime"` takes `"kind": "docker"` or `"jar"`, which `"image"` and `"jar"` imply on their own (`"runtime": { "jar": "latest" }` runs the latest release with Java). `"license": { "token": null }` skips the license and `"git-sync": { "location": "skip" }` leaves the repository for later. `"sync-scope": { "export": false }` leaves the first export of the sync scope to you. `"databases"` lists at least one database: an empty list is refused, since `init` needs one; leave the section out to be asked at a terminal. A database is pasted as a `uri` or entered with `fields`, keyed by the driver's field names as the admin form shows them; a file field takes a path. Anything a `fields` entry leaves out is asked in the terminal, or named in the error without one. A field that engine does not ask with the others given, such as one behind the admin form's advanced options, is named in a warning and ignored.
262
251
 
263
252
  ### Recording a run
264
253
 
@@ -282,22 +271,29 @@ Downgrades are refused: a target whose tag names an older release than the runni
282
271
 
283
272
  If Metabase does not become healthy on the new version, nothing is rolled back automatically, since its migrations may already have run; the command prints the last log lines, the backup path, and the steps to go back: `rde stop`, replace `data/` with the backup, set `runtime.image` (or `runtime.source`) back to the previous value in `state.json`, for Docker `docker rm -f rde-metabase-default` and `rde init --only runtime`, then `rde start`. Backups are plain directories; delete the ones you no longer need. `rde uninstall` removes them with the instance directory.
284
273
 
274
+ Ctrl-C during an upgrade says how far it got and what comes next: before the backup is whole the instance is on the version it had and the next `rde upgrade` begins again (a partial copy can go); once the switch to the new version has started, `rde init` reinstalls the instance; while Metabase starts on the new version, the next `rde upgrade` waits for it and finds it upgraded.
275
+
285
276
  ## Resetting
286
277
 
287
- `rde uninstall` returns the machine to the state before `rde init`: it stops and removes the Metabase container, or stops the jar's process, stops and removes the sample database container, forgets the `mb` profile it logged in (the one the state file records; never a profile pointing at another Metabase), and deletes the stored credentials and `~/.rde/instances/default` along with the `config.json` entry. It shows the list first and asks once; `--yes` skips the question. Your working directory and its git history are never touched, nor are the `mb` CLI, Docker, and Java themselves.
278
+ `rde uninstall` returns the machine to the state before `rde init`: it stops and removes the Metabase container, or stops the jar's process, forgets the `mb` profile it logged in (the one the state file records; never a profile pointing at another Metabase), and deletes the stored credentials and `~/.rde/instances/default` along with the `config.json` entry. It shows the list first and asks once; `--yes` skips the question. Your working directory and its git history are never touched, nor are the `mb` CLI, Docker, and Java themselves.
279
+
280
+ With the local `content.git` remote, that repository lives in the instance directory, so commits Metabase exported there and you never pulled exist nowhere else. `rde uninstall` compares it with your working directory first, without fetching into it, and lists the commits your working directory lacks along with the `git -C <workdir> pull` that brings them in; a working directory that is gone or no longer a git repository counts as lacking all of them. At a terminal the question still comes, under that list. With `--yes`, or without a terminal, the command stops with exit code 2 instead, unless `--force` is passed.
288
281
 
289
282
  ```sh
290
283
  rde uninstall # container or process, mb profile, ~/.rde/instances/default
291
284
  rde uninstall --keep-data # container or process and profile only; the data and secrets.json stay, and `rde init` reinstalls from them
292
285
  rde uninstall --remove-skill # also the rde skills from your coding agents
293
286
  rde uninstall --remove-image # also the Docker images or the downloaded jar (otherwise they stay for the next init)
287
+ rde uninstall --yes --force # also deletes commits in content.git that your working directory has not pulled
294
288
  ```
295
289
 
296
- Running it twice is a no-op. It also works with no `~/.rde` at all, so a leftover `rde-metabase-default` or `rde-sampledb-default` container, a Metabase process recorded in a stray pid file, or an `mb` profile from a half-finished run is cleaned up the same way. A container labelled with another `RDE_HOME` is left alone, and the warning names that home. Anything it could not remove is listed at the end and the command exits with 1.
290
+ A Postgres `rde-sampledb-default` container left by an older rde, which ran a sample database next to each instance, is removed by `rde uninstall`, and by `rde stop` and `rde upgrade` as well.
291
+
292
+ Running it twice is a no-op. It also works with no `~/.rde` at all, so a leftover `rde-metabase-default` container, a Metabase process recorded in a stray pid file, or an `mb` profile from a half-finished run is cleaned up the same way. A container labelled with another `RDE_HOME` is left alone, and the warning names that home. Anything it could not remove is listed at the end and the command exits with 1.
297
293
 
298
294
  ## Troubleshooting
299
295
 
300
- Start with `rde doctor`. It checks the machine (platform, Node, git, Docker, Java, every `mb` on `PATH`), the instance (the container or the jar process, port, health, Metabase version, admin user, admin password, whether Metabase accepts the API key, the secrets file's modes, no secret left in `state.json`, `mb` profile and the URL it points at, license, databases, the sample database's container, port, and Metabase record, remote sync), the `MB_PROFILE`, `MB_URL`, and `MB_API_KEY` variables when set, the other Metabases `mb` knows, the skill for each detected coding agent, and every copy of the skill, one row each with ✓, ✗, or – and a hint under every ✗. A row marked `!` is a note: nothing fails, but it changes where `mb` goes. Of Docker and Java, the one the instance does not use is `–` when missing; the one it uses is ✗.
296
+ Start with `rde doctor`. It checks the machine (platform, Node, git, Docker, Java, every `mb` on `PATH`), the instance (the container or the jar process, port, health, Metabase version, admin user, admin password, whether Metabase accepts the API key, the secrets file's modes, no secret left in `state.json`, `mb` profile and the URL it points at, license, databases, remote sync), the `MB_PROFILE`, `MB_URL`, and `MB_API_KEY` variables when set, the other Metabases `mb` knows, the skill for each detected coding agent, and every copy of the skill, one row each with ✓, ✗, or – and a hint under every ✗. A row marked `!` is a note: nothing fails, but it changes where `mb` goes. Of Docker and Java, the one the instance does not use is `–` when missing; the one it uses is ✗.
301
297
 
302
298
  ```
303
299
  ✓ container rde-metabase-default stopped (metabase/metabase-enterprise-head:latest)
@@ -332,8 +328,6 @@ Start Docker (or `colima start`), or install Java, and run `rde init` again. `rd
332
328
 
333
329
  **The instance is not on port 23000.** The runtime step takes the first free port from 23000 up and says so: `Port 23000 is in use or claimed; using 23001`. rde's ports are the standard ones plus 20000 (Metabase's 3000, Postgres's 5432), a block no common local tool uses and that Docker and the kernel never pick from on their own. A port counts as claimed when any Docker container, running or stopped, from this rde home or another, is created to publish it, or when another instance of this home uses it, because that container or instance takes it back the moment it starts. `rde status` and the summary show the URL; `--port` pins a port of your choice and refuses a busy or claimed one, naming who claims it. A container that exists with a different port or image is reported with the `docker rm -f` command that clears it.
334
330
 
335
- **The sample database is not on port 25432.** The same rule: `Port 25432 is in use or claimed; using 25433 for the sample database`. The summary, `rde status`, and the `psql` line carry the port that was taken, and Metabase's connection uses it too. A sample database container that was removed by hand comes back on the next `rde init` (`rde doctor` points at it); if another program took its port meanwhile, it moves to the next free one and `init` says which Metabase database still points at the old port.
336
-
337
331
  **The instance's port is taken.** While an instance is stopped, another program can take its port. `rde start` then refuses rather than talk to that program: `port 23000 is in use by another program, so Metabase cannot start on it; stop that program and run \`rde start\` again, or move this instance with \`rde start --port 23001\``, and `rde status` says `another program answers at http://localhost:23000` instead of reporting it healthy. Stopping the other program is enough when it is temporary. `rde start --port <n>` moves the instance for good: Metabase stops, the container is created again on the new port from the image it already runs (the data is in `~/.rde` and stays), the state and Metabase's site URL take the new address, and the `mb` profile follows once Metabase answers there (if that start fails, the next `rde start` finishes moving the profile). Anything outside rde that knows the old URL (a bookmark, a data app's config) needs the new one. A container whose port forward Docker lost, which happens when its start once failed on a taken port, is repaired by the next `rde start`, which says so in one line.
338
332
 
339
333
  **The jar's Java is wrong.** The jar runtime needs Java 21 or newer and says which one it found: `Java 17.0.9 at /usr/bin/java is too old`. Point `JAVA_HOME` at a newer runtime, or put one first on `PATH`; the next `rde start` uses it. A pid file left by a reboot is recognized as stale (the pid is free, or runs something else) and ignored.
@@ -356,4 +350,4 @@ Start Docker (or `colima start`), or install Java, and run `rde init` again. `rd
356
350
 
357
351
  **The agent follows an old copy of the skill.** The `rde skill copies` row lists every copy of the skill the `skills` CLI knows, global and in the current directory's project, with where it came from, and fails when one did not come from rde's source (the branch is read from the `skills` lock file; against a local `RDE_SKILL_SOURCE` the copy's `SKILL.md` is compared with the checkout's `skills/rde/SKILL.md`). With several copies it says which one Claude Code started in that directory loads (a personal skill wins over a project one). `rde init --only agents-skills` reinstalls the global copy; restart the agent session afterwards.
358
352
 
359
- **The license token is rejected.** Activation ends with `Metabase reports the token as invalid` (or another status Metabase returned). Check the token, and check that the container can reach `token-check.metabase.com`: activation needs outbound network access. Run `rde init --from license` to try again; press Enter at the prompt to skip the license and carry on without remote sync.
353
+ **The license token is rejected.** Metabase checks the token with `token-check.metabase.com` before it saves it. `The license token was refused: <reason>` means the store turned the token down: check that you pasted the whole token and that its subscription is active. `The instance could not reach token-check.metabase.com to check the license token` means the check never got an answer: the container (or the jar) needs outbound network access to that host. At a terminal the step asks for the token again, and Enter skips the license and carries on without remote sync, data apps, and transform tests; without a terminal the run stops, and `rde init --from license` tries again.