@metabase/rde 0.1.16 → 0.1.18

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 +61 -25
  2. package/dist/cli.mjs +1310 -452
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -6,7 +6,7 @@ After `init`, the rest is lifecycle: `rde start`, `rde stop`, `rde status`, `rde
6
6
 
7
7
  ## Requirements
8
8
 
9
- - macOS or Linux
9
+ - macOS or Linux; Windows is not supported, and WSL is untested
10
10
  - Node 20.19 or newer
11
11
  - git
12
12
  - One way to run Metabase, either of:
@@ -22,6 +22,8 @@ npm install -g @metabase/rde
22
22
  rde init
23
23
  ```
24
24
 
25
+ To update rde itself, run `npm i -g @metabase/rde@latest`; `rde doctor` says when a newer release is out. To remove it, run `rde uninstall` first (see [Resetting](#resetting)), then `npm rm -g @metabase/rde`.
26
+
25
27
  ## What `rde init` does
26
28
 
27
29
  The wizard shows its checklist first (`✓` done, `○` to do, `↑` done but behind what is published, `–` not applicable), runs the steps still to do in order, and ends with a summary of your environment. A first run on a machine that has Docker but not yet `mb` starts like this:
@@ -30,9 +32,9 @@ The wizard shows its checklist first (`✓` done, `○` to do, `↑` done but be
30
32
  ┌ rde init
31
33
  ✓ Check this machine
32
34
  ○ Install the Metabase CLI
33
- ○ Install the rde and data-app skills for your coding agents
34
35
  ○ Install Metabase
35
36
  ○ Start Metabase
37
+ ○ Install the rde and data-app skills for your coding agents
36
38
  ○ Create the admin user
37
39
  ○ Connect the Metabase CLI
38
40
  ○ Enable transforms
@@ -41,7 +43,7 @@ The wizard shows its checklist first (`✓` done, `○` to do, `↑` done but be
41
43
  ○ Connect your databases
42
44
  ○ Summary
43
45
  │
44
- ● Running npm install -g @metabase/cli@alpha-release-65-integration-branch
46
+ ● Running npm install -g @metabase/cli@latest
45
47
  ```
46
48
 
47
49
  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.
@@ -50,22 +52,23 @@ The databases step is the one `init` cannot do without: transforms write their t
50
52
 
51
53
  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.
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
+ 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. A license can lapse after it was activated (a token expires, or Metabase stops accepting it); the license step then runs again, and skipping it records that the instance has no license. The git step asks Metabase for the license before it sets anything up, and stops with `rde init --from license` as the fix when the license lacks remote sync.
54
56
 
55
57
  | Step | What it does |
56
58
  | --- | --- |
57
59
  | Check this machine | Platform, Node version, git, and a reachable Docker daemon or a Java 21+ runtime |
58
- | Install the Metabase CLI | `@metabase/cli` (`mb`) at the `alpha-release-65-integration-branch` dist-tag, with the package manager that installed `rde`; an installed `mb` older than what the dist-tag names now is updated. `rde status` and `rde doctor` check only what is installed and ask no registry |
59
- | Install the rde and data-app skills | The data-app skills (`metabase-data-app-setup`, `-routing`, `-actions`, `-semantic-layer`, `-migrate`) from `metabase/metabase/skills#master`, then the `rde` and `rde-data-apps` skills from the `main` branch of `metabase/agent-skills` (the rde skill hands an explicit app request to `rde-data-apps`, which hands the build to the data-app skills), for the coding agents you pick at a terminal, from the ones found on this machine (Claude Code, Codex, Cursor, Gemini CLI, OpenCode, Windsurf). When none is found, the step is skipped and says how to add the skills once an agent is installed; `rde` installs no agent. Without a terminal, or with `--yes`, every agent found gets the skills; `--agents` picks a set ahead of time (the skills are installed for them whether or not they are on the machine yet), `--skip-agents` none; `RDE_SKILL_SOURCE` installs the two rde skills from another URL or a local checkout of `metabase/agent-skills`. Installed skills whose directory changed on its branch since are installed again, without a question when every detected agent already has them all |
60
- | Install Metabase | **Docker** when it is available: pulls `metabase/metabase-enterprise-head:latest` (or `--image`) and creates the container. **Jar** otherwise, or with `--runtime jar`: downloads the latest enterprise release (or the tag or `.jar` path given as `--jar`) into `~/.rde/jars/` and runs it with your Java. Either way on port 23000, or the first free port above it that nothing else claims, or `--port` |
60
+ | Install the Metabase CLI | `@metabase/cli` (`mb`) at the `latest` dist-tag, with the package manager that installed `rde`; an installed `mb` older than what the dist-tag names now is updated. `rde status` and `rde doctor` check only what is installed and ask no registry |
61
+ | Install Metabase | **Docker** when it is available: pulls the major line of the newest Metabase Enterprise release, such as `metabase/metabase-enterprise:v1.65.x` (or `--image`), and creates the container. **Jar** otherwise, or with `--runtime jar`: downloads the newest enterprise release (or the tag or `.jar` path given as `--jar`) into `~/.rde/jars/` and runs it with your Java. [Metabase versions](#metabase-versions) says how the release is found. Either way on port 23000, or the first free port above it that nothing else claims, or `--port` |
61
62
  | Start Metabase | Starts the container or the jar and waits for `/api/health`, showing Metabase's own progress |
63
+ | Install the rde and data-app skills | The data-app skills (`metabase-data-app-setup`, `-routing`, `-actions`, `-semantic-layer`, `-migrate`) from the release branch of the Metabase the instance runs (`metabase/metabase/skills#release-x.65.x` for Metabase 65, `release-x.66.x` for 66, and so on; 65 is the first with them), then the `rde` and `rde-data-apps` skills from the `main` branch of `metabase/agent-skills` (the rde skill hands an explicit app request to `rde-data-apps`, which hands the build to the data-app skills), for the coding agents you pick at a terminal, from the ones found on this machine (Claude Code, Codex, Cursor, Gemini CLI, OpenCode, Windsurf). When none is found, the step is skipped and says how to add the skills once an agent is installed; `rde` installs no agent. Without a terminal, or with `--yes`, every agent found gets the skills; `--agents` picks a set ahead of time (the skills are installed for them whether or not they are on the machine yet), `--skip-agents` none; `RDE_SKILL_SOURCE` installs the two rde skills from another URL or a local checkout of `metabase/agent-skills`. Installed skills whose directory changed on its branch since are installed again, without a question when every detected agent already has them all. The skills are installed globally, not per instance, so every instance on the machine shares one set: instances on different Metabase majors share the data-app branch the last `init` (or `rde upgrade` to another major) installed |
62
64
  | Create the admin user | `admin@metabase.local` with a generated password that `rde credentials` shows, or your own with `--admin prompted`, see [Credentials](#credentials) |
63
65
  | 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 |
64
66
  | 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 |
65
67
  | 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 |
66
68
  | 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
+ | 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 |
67
70
  | 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 |
68
- | Summary | URL, login email, the `mb` command for the instance, databases, repository, and the command that opens each detected agent |
71
+ | 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 |
69
72
 
70
73
  The second run on the same machine detects every step as done and prints the summary only:
71
74
 
@@ -143,7 +146,13 @@ The instance listens on `127.0.0.1` only (the container's published port, or the
143
146
 
144
147
  ### Docker or jar
145
148
 
146
- Docker is used whenever it is available: the image is self-contained, and the container is the thing to look at when something is off. The jar runtime is what `init` falls back to on a machine without Docker, and what `--runtime jar` (or `--jar`) picks on purpose, for running a jar you built yourself. It runs `java -jar` detached from the terminal, with the same data directory layout, the same environment, and the same `127.0.0.1`-only listener; `rde stop` sends it SIGTERM and waits for it to exit; `rde logs` reads `metabase.log`. Java is resolved through `JAVA_HOME`, then `PATH`, at every start, so a version manager's switch is picked up without reconfiguring. Without `--jar` the latest enterprise release is used; `--jar` accepts `latest`, a release tag such as `v1.63.18` (with or without the `v`; `v0.…` is the OSS build), or the path of a `.jar` file. A release is downloaded once into `~/.rde/jars/<tag>/` and reused; a path is run from where it is. A connection string that says `localhost` reaches your own databases directly, since the jar runs on the host; no rewrite to `host.docker.internal` is needed.
149
+ Docker is used whenever it is available: the image is self-contained, and the container is the thing to look at when something is off. The jar runtime is what `init` falls back to on a machine without Docker, and what `--runtime jar` (or `--jar`) picks on purpose, for running a jar you built yourself. It runs `java -jar` detached from the terminal, with the same data directory layout, the same environment, and the same `127.0.0.1`-only listener; `rde stop` sends it SIGTERM and waits for it to exit; `rde logs` reads `metabase.log`. Java is resolved through `JAVA_HOME`, then `PATH`, at every start, so a version manager's switch is picked up without reconfiguring. Without `--jar` the newest enterprise release is used; `--jar` accepts `latest` (that same release), an enterprise release tag such as `v1.65.0` (with or without the `v`; an OSS `v0.…` tag is refused), or the path of a `.jar` file. A release is downloaded once into `~/.rde/jars/<tag>/` and reused; a path is run from where it is. A connection string that says `localhost` reaches your own databases directly, since the jar runs on the host; no rewrite to `host.docker.internal` is needed.
150
+
151
+ ### Metabase versions
152
+
153
+ rde runs Metabase Enterprise 65 or newer. Once Metabase is up, `rde init`, `rde start`, and `rde upgrade` read the version it reports and refuse the OSS edition (a `v0.…` version, which `metabase/metabase` runs), a release before 65, and a build of master (a `-SNAPSHOT` version, which `metabase/metabase-enterprise-head` runs), with exit code 2 and the way to another: `rde upgrade --to <image or release tag>` for this instance, or `rde uninstall`, then `rde init --image <image>` or `rde init --jar <release tag>`, for a new one. The refused instance is left running as it is. `--jar` refuses a `v0.…` tag before anything is downloaded. With `RDE_DEV=1`, for working on rde and Metabase themselves, every version runs.
154
+
155
+ A new instance runs the newest release that Metabase's release feed (`https://static.metabase.com/version-info-ee.json`) lists of Enterprise 65 or newer, betas and release candidates left out. The feed's own `latest`, like the Docker `:latest` tag, moves to a new major only weeks after its release, so rde reads neither. Docker gets the major line of that release, `metabase/metabase-enterprise:v1.<major>.x`, a tag Metabase moves to every build of the major: the state file records the line, and each pull of it brings the newest build. The jar gets the release itself. While the feed lists no release of 65 or newer, `init` stops with exit code 2 before it pulls or downloads anything, and names the ways out: `--image <image>`, `--jar <release tag>`, or `RDE_DEV=1` for the development build. When the feed cannot be read, `init` stops the same way, with the cause and `--image <image>` or `--jar <release tag>` as the way past it. In development mode the image is `metabase/metabase-enterprise-head:latest`, and the jar's `latest` is the feed's own `latest`.
147
156
 
148
157
  ## Everyday commands
149
158
 
@@ -151,7 +160,7 @@ Docker is used whenever it is available: the image is self-contained, and the co
151
160
  | --- | --- |
152
161
  | `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)) |
153
162
  | `rde stop` | Stops the instance; the data stays |
154
- | `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 and the content repository's working directory (`contentRepository`); `--json` for scripts and agents (`mbProfile`, `adminEmail`, `setup`, never a secret); exit 0 whatever it reports |
163
+ | `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 |
155
164
  | `rde credentials` | The Metabase URL, admin email, and password; `--json` for scripts and agents; `--api-key` for the API key alone |
156
165
  | `rde logs` | The container's log, or the jar's `metabase.log`; `-f` keeps streaming |
157
166
  | `rde open` | Opens the instance in the browser |
@@ -184,7 +193,11 @@ With a license that includes remote sync, the step after the license connects a
184
193
  - **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).
185
194
  - **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.
186
195
 
187
- 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:
196
+ 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.
197
+
198
+ 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.
199
+
200
+ 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:
188
201
 
189
202
  ```sh
190
203
  cd <your working directory>
@@ -195,7 +208,7 @@ git pull # after an export, to see the YAML files
195
208
 
196
209
  ## Running `init` without a terminal
197
210
 
198
- 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.
211
+ 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.
199
212
 
200
213
  ### Flags
201
214
 
@@ -208,7 +221,7 @@ rde init --skip-license \
208
221
  | Flag | Answers |
209
222
  | --- | --- |
210
223
  | `--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 |
211
- | `--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` |
224
+ | `--runtime docker\|jar`, `--image`, `--jar`, `--port` | How Metabase runs, the Docker image or the jar (`latest`, an enterprise release tag, or a `.jar` path), and the host port, instead of Docker when available, the major line of the newest release, the newest release, and the first free port from 23000 that nothing else claims; `--image` implies `docker` and `--jar` implies `jar` |
212
225
  | `--admin generated\|prompted`, `--admin-email`, `--admin-password` | The admin user, `generated` unless given; an email or password implies `prompted` |
213
226
  | `--license-token` / `--skip-license` | The license |
214
227
  | `--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 |
@@ -230,18 +243,19 @@ rde init --license-token '$RDE_LICENSE_TOKEN' …
230
243
  ```json
231
244
  {
232
245
  "agents-skills": { "agents": ["claude-code"] },
233
- "runtime": { "image": "metabase/metabase-enterprise:latest", "port": 13000 },
246
+ "runtime": { "image": "metabase/metabase-enterprise:v1.65.x", "port": 13000 },
234
247
  "admin-user": { "variant": "prompted", "email": "me@example.com", "password": "$RDE_ADMIN_PASSWORD" },
235
248
  "license": { "token": "$RDE_LICENSE_TOKEN" },
236
249
  "databases": [
237
250
  { "engine": "postgres", "uri": "$RDE_DATABASE_1_URI" },
238
251
  { "engine": "bigquery-cloud-sdk", "fields": { "project-id": "acme", "service-account-json": "/keys/acme.json" }, "name": "Warehouse" }
239
252
  ],
240
- "git-sync": { "location": "new", "path": "~/analytics", "remote": "local-bare" }
253
+ "git-sync": { "location": "new", "path": "~/analytics", "remote": "local-bare" },
254
+ "sync-scope": { "export": true }
241
255
  }
242
256
  ```
243
257
 
244
- `"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. `"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.
258
+ `"runtime"` takes `"kind": "docker"` or `"jar"`, which `"image"` and `"jar"` imply on their own (`"runtime": { "jar": "latest" }` runs the newest 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.
245
259
 
246
260
  ### Recording a run
247
261
 
@@ -251,17 +265,21 @@ rde init --license-token '$RDE_LICENSE_TOKEN' …
251
265
 
252
266
  `rde upgrade` moves the instance to a newer Metabase without losing its application database. It makes sure the instance is running and reads the version it is on, fetches the target, and stops there with `Already on …` when the target is what the instance already runs. Otherwise it shows the running version, the target, and the backup path, asks once (`--yes` skips the question; without a terminal `--yes` is required), and then stops Metabase, copies the whole `data/` directory to `~/.rde/instances/default/backups/<timestamp>/`, switches the instance to the target, starts it, waits for health, and records the new version. Metabase migrates its application database forward on that first start.
253
267
 
254
- For a Docker instance the target is an image ref (`--to <image ref>`; by default the instance's own image, which for a `:latest` tag means "whatever is latest now"), the pulled image is compared by id with the one the container runs, and the container is removed and created again from the target with the same port, mount, and environment. For a jar instance the target is `latest` (the default), a release tag, or a `.jar` path; a release is downloaded into `~/.rde/jars/` before the question, and the instance's `runtime.source` is switched to it.
268
+ A move to another major version (65 to 66, say) is named before the backup with `This moves Metabase from 65 to 66`, ahead of the same question. A target that names no release shows its version only once it runs, and the plan says so: a major line (`:v1.66.x`) still names its major, so the move is named before the backup all the same, while `:latest` or a jar path names nothing, so the line comes after the start instead. Either way, once the new major runs, `rde upgrade` runs the skills step, because the data-app skills come from the release branch of the Metabase the instance runs; at a terminal it asks which agents get them like `rde init` does, and with `--yes` every agent found gets them.
269
+
270
+ For a Docker instance the target is an image ref (`--to <image ref>`; by default, for an image of `metabase/metabase-enterprise`, `metabase/metabase-enterprise-head`, or `metabase/metabase`, the major line of the newest release, found the way `init` finds it, whatever tag the instance ran before: `:latest`, an older major line, or the same line), the pulled image is compared by id with the one the container runs, and the container is removed and created again from the target with the same port and mount and the environment rde sets now. An image from any other repository (a registry of your own, an image you built) is pulled again under its own ref, and the plan says so. The default target follows the feed rules of [Metabase versions](#metabase-versions): a feed that lists no release of 65 or newer, or cannot be read, stops the upgrade before it starts Metabase or pulls anything. With `RDE_DEV=1` the default target is the instance's own image. When the target is the image already running, a container created under another ref of it (`:latest` that names the same image as the line, say) or one that keeps variables rde sets otherwise now (the staging store of development mode, say) is still created again from that image under the target's ref, with no backup since nothing migrates; `rde doctor` names a container with such variables, with `rde upgrade` as the fix. A build of master (`metabase/metabase-enterprise-head:latest`, the development image) reports a major ahead of every release, so `rde upgrade` refuses it as a downgrade; `rde doctor` names one outside development mode, and the way onto a release is `rde uninstall`, then `rde init`, while `RDE_DEV=1` keeps running it. For a jar instance the target is `latest` (the default, the newest release), a release tag, or a `.jar` path; a release is downloaded into `~/.rde/jars/` before the question, and the instance's `runtime.source` is switched to it.
255
271
 
256
272
  ```sh
257
- rde upgrade # Docker: pull the instance's image again; jar: the latest release
258
- rde upgrade --to metabase/metabase-enterprise:v1.64.0 # Docker: a specific release
259
- rde upgrade --to v1.64.0 # jar: a specific release
273
+ rde upgrade # Docker: the major line of the newest release, pulled again; jar: the newest release
274
+ rde upgrade --to metabase/metabase-enterprise:v1.66.0 # Docker: a specific release
275
+ rde upgrade --to v1.66.0 # jar: a specific release
260
276
  rde upgrade --to ~/metabase/target/uberjar/metabase.jar # jar: a build of your own
261
- rde status # shows `Version: v1.64.0`
277
+ rde status # shows `Version: v1.66.0`
262
278
  ```
263
279
 
264
- Downgrades are refused: a target whose tag names an older release than the running one (`v1.62.0` against a running `v1.63.18`) ends with exit code 2 before anything is fetched, because those migrations only run forward. An older release needs a fresh instance (`rde uninstall`, then `rde init`). A target that carries no release tag (`latest`, a branch image, a jar path) is taken on trust.
280
+ Downgrades are refused: a target whose tag names an older release than the running one (`v1.65.0` against a running `v1.66.2`), or the major line of an older major (`v1.65.x` against the same, or against a build of master that reports `v1.67.0-SNAPSHOT`), ends with exit code 2 before anything is fetched, because those migrations only run forward. An older release needs a fresh instance (`rde uninstall`, then `rde init`). A target that carries no release tag (`latest`, a branch image, a jar path) is taken on trust.
281
+
282
+ A target rde does not run (see [Metabase versions](#metabase-versions)) is refused the same way, before anything is fetched, when its tag names the release. When its version shows only once it runs, the upgrade fails like one that never became healthy, below, with the backup path and the steps to go back. An instance already on such a version still starts for `rde upgrade`, which is how it moves to one rde runs.
265
283
 
266
284
  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.
267
285
 
@@ -276,7 +294,7 @@ With the local `content.git` remote, that repository lives in the instance direc
276
294
  ```sh
277
295
  rde uninstall # container or process, mb profile, ~/.rde/instances/default
278
296
  rde uninstall --keep-data # container or process and profile only; the data and secrets.json stay, and `rde init` reinstalls from them
279
- rde uninstall --remove-skill # also the rde skills from your coding agents
297
+ rde uninstall --remove-skill # also the rde and rde-data-apps skills from your coding agents
280
298
  rde uninstall --remove-image # also the Docker images or the downloaded jar (otherwise they stay for the next init)
281
299
  rde uninstall --yes --force # also deletes commits in content.git that your working directory has not pulled
282
300
  ```
@@ -285,12 +303,22 @@ A Postgres `rde-sampledb-default` container left by an older rde, which ran a sa
285
303
 
286
304
  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.
287
305
 
306
+ ## Environment variables
307
+
308
+ | Variable | What it does |
309
+ | --- | --- |
310
+ | `RDE_HOME` | Where rde keeps its state instead of `~/.rde`; a relative path is resolved against the working directory |
311
+ | `RDE_SKILL_SOURCE` | Development only: where the `rde` and `rde-data-apps` skills are installed from instead of `metabase/agent-skills`, such as a local checkout, so they can be tried before they are pushed |
312
+ | `RDE_DEV` | Development only: `1` runs instances on the enterprise build of master and checks license tokens against the staging store; `bin/rde-dev` sets it |
313
+
314
+ An empty value counts as unset.
315
+
288
316
  ## Troubleshooting
289
317
 
290
- 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 ✗.
318
+ Start with `rde doctor`. It checks the machine (platform, Node, git, Docker, Java, every `mb` on `PATH`, and whether a newer rde is released, the one check that asks the npm registry and gives up after a few seconds), 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, every copy of the skill, and the data-app skills, 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 ✗.
291
319
 
292
320
  ```
293
- ✓ container rde-metabase-default stopped (metabase/metabase-enterprise-head:latest)
321
+ ✓ container rde-metabase-default stopped (metabase/metabase-enterprise:v1.65.x)
294
322
  ✓ port 23000 is free
295
323
  ✗ health unreachable
296
324
  → `rde start`
@@ -330,6 +358,8 @@ Start Docker (or `colima start`), or install Java, and run `rde init` again. `rd
330
358
 
331
359
  **An older `mb` runs first.** Another package manager installed an `mb` earlier, and its directory comes first on `PATH`. The `mb` row lists every `mb` in `PATH` order and fails when the first one is too old, or comes from another package manager while a newer one sits later; the hint names both paths and the command that removes the old one, taken from its path (`bun remove -g @metabase/cli`, `brew uninstall <formula>`, …), or the `PATH` order that puts the new one first. `rde` never edits shell profiles; open a new terminal after the change. `rde init` stops with the same list when the `mb` it installed is still not the one that runs.
332
360
 
361
+ **npm cannot install `mb`.** On a Node whose global directory belongs to root (a system or installer-package Node), `npm install -g @metabase/cli@latest` fails with `EACCES`, and `rde init` stops with `npm cannot write its global directory`. Give npm a prefix you own (`npm config set prefix ~/.npm-global`, then add `~/.npm-global/bin` to `PATH` and open a new terminal), or install Node with a version manager such as nvm, fnm, or mise, and run `rde init` again. Avoid `sudo`: it leaves root-owned files in npm's cache.
362
+
333
363
  **`mb` went to the wrong Metabase.** A plain `mb` uses the `default` profile, or the one `MB_PROFILE` names, and `MB_URL` or `MB_API_KEY` override every profile, the instance's own included. `rde doctor` shows a `!` row for each of those variables that is set, with the `unset` to run, and lists every other profile by host with whether it authenticates and which one plain `mb` uses:
334
364
 
335
365
  ```
@@ -344,4 +374,10 @@ Start Docker (or `colima start`), or install Java, and run `rde init` again. `rd
344
374
 
345
375
  **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.
346
376
 
377
+ **The data-app skills are for another Metabase version.** The `data-app skills` row names the branch of `metabase/metabase` the instance's Metabase version takes them from (`release-x.65.x` for 65, and so on) and fails when a data-app skill is missing or its copy came from another branch, or when `rde-data-apps` did not come from rde's source. The skills are global, so it happens when an instance on another major installed them last, or when the skills step was left out after `rde upgrade` moved to another major. `rde init --only agents-skills` (or `rde doctor --fix`) installs them for this instance's version; restart the agent session afterwards.
378
+
347
379
  **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.
380
+
381
+ ## License
382
+
383
+ License: AGPL-3.0, see `package.json`.