trestle-deploy
v0.1.0
Published
JS-native Capistrano-style release-directory deployments with Rollbridge integration.
Downloads
21
Maintainers
Readme
Trestle Deploy
Trestle is a JavaScript-native release-directory deployment tool: the useful parts of Capistrano, built for Node/Expo/Velocious projects and designed to hand runtime changes to Rollbridge.
Package: trestle-deploy
CLI: trestle
Config: trestle.config.mjs
Current MVP
This initial scaffold includes:
- ESM config loading and validation
- deterministic deploy plans
- local release-directory deploys for tests/dogfooding
- linked file/dir checks and symlink creation
- deploy lock acquisition/release
- release metadata (
REVISION,TRESTLE_RELEASE.json) - atomic
currentsymlink publishing - safe cleanup
- rollback to previous or selected release
- JSONL deploy reports at
shared/log/trestle-deployments.jsonl - secret redaction helpers
- basic runtime adapter interface with
noneand Rollbridge validate/deploy/status/logs/recover helpers
SSH transport command execution supports both local-copy and remote-git deployments. remote-git prepares each release checkout on the target host, so the target needs git and access to the configured repository.
Full config reference: docs/config-reference.md Migrating from Capistrano: docs/migration-from-capistrano.md
Commands
trestle init
trestle validate production
trestle plan production
trestle plan production --json
trestle deploy production
trestle status production
trestle releases production
trestle releases production --json
trestle rollback production
trestle doctor production
trestle doctor production --json
# Rollbridge runtime helpers for the current release
trestle rollbridge-validate production
trestle rollbridge-status production
trestle rollbridge-logs production
trestle rollbridge-recover production --yesFor local development without installing the package globally:
node src/cli.js init
node src/cli.js validate --config examples/local/trestle.config.mjs local
node src/cli.js plan --config examples/local/trestle.config.mjs local
node src/cli.js deploy --config examples/local/trestle.config.mjs local --yes
# Monorepo example with backend + frontend stages
node src/cli.js validate --config examples/monorepo/trestle.config.mjs local
node src/cli.js plan --config examples/monorepo/trestle.config.mjs localConfig example
Reusable Node package install helpers can be imported from trestle-deploy/tasks when writing trestle.config.mjs:
import {npmInstall, pnpmInstall, checkExpoWebArtifacts, velociousMigrate, onlyIfChanged} from "trestle-deploy/tasks"// trestle.config.mjs
export default {
application: "routergeist",
stages: {
production: {
repo: "[email protected]:kaspernj/routergeist.git",
branch: "master",
deployTo: "/home/dev/routergeist",
strategy: "remote-git",
transport: {type: "ssh"},
hosts: [{host: "server.example", user: "dev", roles: ["app", "web", "db"]}],
keepReleases: 5,
linkedFiles: [
{path: "backend/src/config/secrets.js", required: true}
],
linkedDirs: ["log", "tmp/pids", "tmp/cache", "storage"],
tasks: {
install: [
npmInstall({cwd: "backend", production: true}),
pnpmInstall({cwd: "app", production: true})
],
migrate: [
velociousMigrate({cwd: "backend", env: {NODE_ENV: "production"}})
],
build: [
"cd app && npm run web:export",
checkExpoWebArtifacts({dir: "app/dist"}),
{command: "npm run assets:verify", cwd: "app", env: {NODE_ENV: "production"}}
]
},
runtime: {
type: "rollbridge",
command: "npx rollbridge",
packageDir: "backend",
config: "config/rollbridge.production.mjs",
healthPath: "/health",
daemonLogPath: "/home/dev/routergeist/shared/log/rollbridge.log",
daemonPidPath: "/home/dev/routergeist/shared/tmp/pids/rollbridge.pid",
socketPath: "/tmp/rollbridge-routergeist.sock",
versionPath: "/home/dev/routergeist/shared/tmp/rollbridge-version"
}
}
}
}npmInstall, pnpmInstall, yarnInstall, bunInstall, and nodePackageInstall return normal task entries with the right install command for common Node package managers. They accept cwd, env, optional, production, and lockfile options such as clean: false for npm install instead of npm ci, or frozenLockfile: false for package managers that need explicit CI lockfile opt-outs (--no-frozen-lockfile for pnpm and --no-immutable for Yarn). Yarn production installs are intentionally not guessed; use a custom task command when that project needs a specific Yarn workspace/plugin flow.
checkExpoWebArtifacts returns a task entry that fails if the Expo web build directory does not contain an index.html. It accepts dir (default "dist"), cwd, and optional.
velociousMigrate returns a task entry that runs npx velocious db:migrate. It accepts command (override), cwd, env, and optional.
onlyIfChanged(paths, task) wraps a task entry so it is skipped when none of the given relative paths have changed since the previous deploy. On the first deploy (no previous release), all tasks run regardless.
Task entries can be strings or objects.
command— required shell command to run.cwd— optional release-relative working directory.env— optional environment variables passed to the command.optional— whentrue, the deploy records the task failure and continues.
Task groups run in config order. Required task failures stop the deploy before publish. Deploy failures include whether current changed and recovery hints for the affected stage, such as rerunning a before-publish failure, inspecting a newly created failed release directory, or inspecting trestle status, trestle releases, and trestle rollback after a failure that happened after publish. Existing release-id collisions do not suggest deleting the release directory.
Deployment reports
Every successful deploy or rollback appends one JSON object per line to shared/log/trestle-deployments.jsonl. Deploy events include:
event: "deploy"application,stage,releaseId,revision, andbranchdeployedAtanddurationMspreviousReleasehealthChecks: "passed"- sanitized runtime status
cleanupRemovedrelease IDs
Rollback events include event: "rollback", the target releaseId, rolledBackAt, and previousRelease. Reports intentionally record runtime result summaries, not runtime configuration secrets.
Releasing
Tag pushes trigger the .github/workflows/release.yml workflow, which creates a GitHub Release with auto-generated notes. Bump the version locally and push tags:
npm run release:patch # 0.1.0 → 0.1.1
npm run release:minor # 0.1.1 → 0.2.0
npm run release:major # 0.2.0 → 1.0.0Each command runs npm version (updating package.json and creating a v-prefixed tag) and pushes the commit and tag. The workflow picks up the tag and creates the release.
To also publish to npm on release, add an NPM_TOKEN secret to the repository; the workflow includes the step (currently disabled) to run npm publish.
Runtime adapters
Trestle resolves runtimes through a small adapter registry. Built-in adapters are none and rollbridge; future adapters can register a runtime type without changing the deploy lifecycle core. Runtime adapters implement plan() and deploy(...); optional maintenance methods such as status, logs, and recover can be exposed by adapter-specific commands.
Rollbridge runtime options:
command— shell command used to invoke Rollbridge, defaulting tonpx rollbridge.packageDir— release-relative directory where Rollbridge commands run.config— optional Rollbridge config path passed as--config.daemonLogPath,daemonPidPath, anddaemonStartTimeoutMs— optional daemon flags forwarded torollbridge deploy.versionPath— optional shared file where Trestle records the deployed Rollbridge package version.socketPath— optional Rollbridge socket removed after a version-change shutdown if a stale daemon leaves it behind.
When versionPath is configured, Trestle reads the release's installed Rollbridge package version before deploy. If it differs from the recorded version and the current runtime is responsive, Trestle shuts down the current Rollbridge daemon, removes configured stale daemon artifacts, deploys the new release, and records the new version.
Safety model
Trestle prepares a complete release before flipping current. If a deploy fails before publish, current remains unchanged. Rollbridge runtime deploys are validated and health-checked with rollbridge status before current is updated. Cleanup never removes the current release. Rollback changes current to a previously prepared release and records a deployment event.
