One-off commands
Sometimes you don't want the whole stack, only one command with a service's environment: a migration, a seed script, a REPL, a test run.
bash
sctl exec api -- node src/migrate.ts # a command, in api's directory with api's env
sctl run api seed -- --rows=100 # api's `seed` script, with arguments
cd apps/api && sctl run seed # no target: the current directorysctl exec [target] -- <command>runs a literal command. The--is required.sctl run [target] <script> [-- args]runs apackage.jsonscript or Makefile target, the way a service's dev script is run.
Both run in the target's directory with what a session would give it: its resolved .env files, its PORT and its PUBLIC_ORIGIN. No session needs to be running, and a running one is not touched.
Targets
A target is a service's name or a directory:
bash
sctl exec api -- node src/migrate.ts # the service named api
sctl exec apps/api -- node src/migrate.ts # its directory, the same thing
sctl exec libs/db -- node src/migrate.ts # a directory that isn't a service- A path anywhere inside a service is that service, and the command runs in the service's own directory.
./apiis always the directory. A bareapiis the service.- A directory outside every service gets the workspace's
.envfiles, then its own, and noPORT. - No target means the current directory.
Modes
--mode picks the environment, as for a session:
bash
sctl --mode test run api seed
sctl --mode test exec -- vitestOutput and exit code
The command's output goes to your terminal and nothing else is printed, so both commands work in a pipeline. The exit code is the command's own. Each call also writes a log file.
From a script
exec() and run() do the same from Node, and resolve with the exit code instead of exiting:
ts
import { exec, run } from 'stackctl';
await exec({ target: 'api', command: ['node', 'src/migrate.ts'] });
const { exitCode } = await run({ target: 'api', script: 'seed', mode: 'test' });See the SDK.