@dodefey/deploy
v0.1.5
Published
A small, gunshi-based CLI app for deploying PM2 managed projects. Configurable with different deploy profiles. Runs test, build, syncs build to server and handles PM2 start/restart
Readme
@dodefey/deploy
A small gunshi-based CLI that deploys a profile-defined build managed by PM2. It runs tests, builds, syncs the client bundle to a remote host via rsync, restarts PM2, and reports client churn (cache reuse vs download impact) so you know the user-facing cost of a deploy. (Nuxt remains the common case, but the build command is now profile-configured.)
Features
- Single command deploy pipeline: tests → build → rsync sync → PM2 restart → churn report.
- Profile-driven config (
profiles.json) with per-environment SSH, paths, build command/args, PM2 app name, and restart mode. - Typed error codes across build, sync, PM2, config, and churn for predictable handling.
- Flexible output modes (
inherit,silent,callbacks) for build/rsync/PM2 stages. - Dry-run mode: run build + churn and perform an rsync
--dry-run; skip PM2 restart and make no remote writes.
Requirements
- Node 20+ (ES2022 target, ESM).
- SSH access to the target host;
rsyncandpm2available on the server. - PM2 ecosystem file on the server (
ecosystem.config.js) inremoteDir.
Installation
npm install
npm run build # produces dist/ for the CLI binaryRun locally without install:
npx @dodefey/deploy --help(After npm run build, the deploy bin points to dist/cli.js.)
Configuration (profiles.json)
Profiles live in your project root (./profiles.json). The CLI looks in the current working directory first; you can point to another file via the DEPLOY_PROFILES_PATH environment variable. Each profile sets connection and PM2 details.
[
{
"name": "prod",
"sshConnectionString": "[email protected]",
"remoteDir": "/var/www/app",
"env": "production",
"pm2AppName": "my-app",
"buildCommand": "npx", // required
"buildArgs": ["nuxt", "build", "--dotenv", ".env.production"], // required
"buildDir": ".output", // optional, defaults to .output
"pm2RestartMode": "startOrReload" // optional, defaults to startOrReload
}
]Validation rules:
- Missing file / invalid JSON / empty array →
CONFIG_PROFILE_FILE_NOT_FOUND. - Unknown profile name →
CONFIG_PROFILE_NOT_FOUND. - Duplicate names →
CONFIG_DUPLICATE_PROFILE. - Empty required fields (including buildCommand/buildArgs) →
CONFIG_PROFILE_INVALID. - Invalid restart mode →
CONFIG_INVALID_RESTART_MODE.
CLI Usage
Main command: deploy
Flags (from src/cli.ts):
--profile, -p <name>(required) Deploy profile fromprofiles.jsonto use.--sshConnectionString, -s <ssh>Override SSH connection string.--remoteDir, -d <path>Override remote app dir.--buildDir, -b <path>Override local build output dir.--env, -e <name>PM2 env.--pm2AppName <name>PM2 app name override.--pm2RestartMode <startOrReload|reboot>PM2 restart mode override.--skipTests, -TSkip vitest before deploy.--skipBuild, -kSkip build; reuse existing output inbuildDir.--dryRun, -nRun build + churn; rsync in--dry-runmode; skip PM2 restart (no remote writes).--verbose, -VInherit stdout/stderr from build/rsync/pm2.--churnOnly, -cCompute churn without build/sync/pm2.
Example:
node dist/cli.js deploy \
--profile prod \
--sshConnectionString [email protected] \
--remoteDir /var/www/app \
--pm2AppName my-app \
--pm2RestartMode startOrReloadDeploy Pipeline (what happens)
- Config: Load profile, apply CLI overrides, validate restart mode.
- Tests:
vitestunless--skipTests. - Build: Run the profile-defined build command (via
runBuild); stdout mode per--verbose. - Sync:
rsynclocal.outputto${remoteDir}/.output(or override), honors--dryRun. - PM2:
pm2 startOrReload(orreboot) app inremoteDir; reports instance count. - Churn: Compute client bundle churn vs previous manifest stored at
${remoteDir}/.deploy/manifest; uploads new manifest unless--dryRun.
Scripts
npm test→ vitestnpm run lint→ eslintnpm run build→ tsc todist/
Output Modes
outputMode is one of inherit (stream to terminal), silent, or callbacks (line handlers) and is used by build, sync, and PM2 modules.
Error Codes (selected)
- Build:
BUILD_COMMAND_NOT_FOUND,BUILD_FAILED - Sync:
SYNC_NO_LOCAL_OUTPUT_DIR,SYNC_SSH_FAILED,SYNC_RSYNC_FAILED - PM2:
PM2_SSH_FAILED,PM2_COMMAND_FAILED,PM2_STATUS_QUERY_FAILED,PM2_APP_NAME_NOT_FOUND - Config:
CONFIG_PROFILE_FILE_NOT_FOUND,CONFIG_PROFILE_NOT_FOUND,CONFIG_PROFILE_INVALID,CONFIG_INVALID_RESTART_MODE - Churn: baseline load/parse failures, fetch failures
Troubleshooting
- No profile found: ensure
./profiles.jsonexists in the directory where you run the CLI (or setDEPLOY_PROFILES_PATH) and that it has at least one profile with unique names. - PM2 app missing: check
pm2AppNamematches the ecosystem config on the server. - rsync errors: verify SSH connectivity and remote write permissions to
${remoteDir}/.output. - Churn baseline missing: first deploy will store a baseline manifest; subsequent runs compare against it.
License
MIT
