Canopydocs v0.4.7

Template variables

Canopy computes a set of variables per worktree and exposes them two ways: as ${VAR} templates inside provisioned file values, and as $VAR environment variables for setup, migrate, teardown, custom commands and services.

Variable reference#

VariableValueAvailable in
WT_SLUGThe worktree's folder name, lowercased, every non-alphanumeric character turned into _templates + commands
WT_INDEXThe worktree's stable port index (main checkout = 0)templates + commands
WT_DB_NAME<repo slug>_<worktree slug>, the worktree's isolated database nametemplates + commands
WT_<ID>_PORTA service's effective port, by service id, uppercasedtemplates + commands
WT_<NAME>_PORTThe same port, by service name, uppercased with non-alphanumerics turned into _templates + commands
WT_PATHAbsolute path to this worktreecommands
WTM_WORKTREESame as WT_PATHcommands
REPO_PATHAbsolute path to the repository's main checkoutcommands
WTM_REPOSame as REPO_PATHcommands
PORTThis service's own effective porta service's own command
WM_PORT_<ID>Back-compat alias of WT_<ID>_PORTtemplates + commands
WM_WT_SLUGBack-compat alias of WT_SLUGtemplates + commands

Port variable naming#

A service's port is exposed under two names, because ids and names serve different audiences:

service { id: "srv-19", name: "Server", basePort: 3000 }
  →  $WT_SRV_19_PORT      (by id)
  →  $WT_SERVER_PORT      (by name)
  →  $WM_PORT_SRV_19      (back-compat alias of the id form)

The name form exists so a hand-written template can say ${WT_SERVER_PORT} whatever the internal id is. Ids generated by the UI, like svc-19, never matched what anyone would actually type. The name form never overwrites an id-based variable, and if two services produce the same name slug, the first one keeps it.

Worked examples#

Isolate the database#

"keys": { "PG_DB": "${WT_DB_NAME}" }

Repository tooljet, worktree folder fix-history-state, and you get PG_DB=tooljet_fix_history_state.

Give a service its own port#

In the service's command, not in the env:

npm start -- --port $PORT

$PORT is that service's own effective port. Use it whenever the process takes a port flag.

Point one service at another#

"keys": {
  "PORT": "${WT_SERVER_PORT}",
  "VITE_API_URL": "http://localhost:${WT_SERVER_PORT}",
  "TOOLJET_HOST": "http://localhost:${WT_FRONTEND_PORT}"
}

Cross-service references belong in the provisioned file, since both services need to agree on the value and the file is the one thing they both read.

Use the worktree's paths in a command#

# a custom command
cp $REPO_PATH/.env.secrets $WT_PATH/.env.secrets
psql $WT_DB_NAME -c 'select count(*) from users'
echo "provisioning index $WT_INDEX for $WT_SLUG"

How they are computed#

The index is assigned (or looked up) before provisioning runs, so ${WT_INDEX} and every port variable are already right when the first file is written, and it's persisted immediately.

The database name is derived from the repository id and the worktree folder name. Nothing queries Postgres to build it.

Ports apply the override first, then basePort + index × 10. A service with no basePort produces no port variable at all.

Common mistakes#

$PORT only means "this service" inside a service command. There's no "this service" in setup or a custom command, so use $WT_<SERVICE>_PORT there.

Templates use ${…} braces. Commands can use $VAR or ${VAR}, since your shell expands them.

A variable that doesn't exist is left as it is rather than replaced with an empty string, so a typo shows up in the resulting file instead of silently blanking a value.

Documentation for Canopy 0.4.7. Controls marked coming soon are present in the interface but have no implementation behind them yet.