# Migrating from Capistrano to Rampway

Rampway is a JS-native alternative to Capistrano for Node projects. This guide maps Capistrano concepts to their Rampway equivalents and walks through a migration.

## Quick reference

| Capistrano | Rampway |
|------------|---------|
| `config/deploy.rb` | `rampway.config.mjs` |
| `config/deploy/production.rb` | stage config in `stages.production` |
| `set :application, "myapp"` | `application: "myapp"` |
| `set :deploy_to, "/path"` | `deployTo: "/path"` |
| `set :keep_releases, 5` | `keepReleases: 5` |
| `set :linked_files, [...]` | `linkedFiles: [...]` |
| `set :linked_dirs, [...]` | `linkedDirs: [...]` |
| `server "host", user: "...", roles: [...]` | `hosts: [{host, user, roles}]` |
| `SSHKit` | `transport: {type: "ssh"}` |
| `after/before` hooks | `tasks` run in config order, or use lifecycle `hooks` for phase-specific callbacks |
| `deploy:starting` … `deploy:finished` | task groups + runtime deploy |
| Explicit custom task against `current` | `operationalTasks` + `rampway production task <exact-name>` |
| `cap production deploy` | `rampway production deploy` |
| `cap -T` / `cap production -T` | `rampway production tasks` |
| `cap production deploy:rollback` | `rampway production rollback` |
| `current` symlink | same |
| `releases/` directory | same |
| `shared/` directory | same |
| `REVISION` file | `REVISION` + `TRESTLE_RELEASE.json` |

## Step-by-step migration

### 1. Install Rampway

Rampway is a dev dependency in your project:

```bash
npm install --save-dev rampway
```

Or run `rampway init` to scaffold a new config.

### 2. Create the config

Run `rampway init` or create `rampway.config.mjs` manually:

```js
export default {
  application: "myapp",
  stages: {
    production: {
      deployTo: "/home/dev/myapp",
      strategy: "local-copy",
      transport: {type: "ssh"},
      hosts: [{host: "server.example", user: "dev", roles: ["app", "web"]}],
      keepReleases: 5,
    }
  }
}
```

### 3. Convert linked files and dirs

Capistrano:

```ruby
set :linked_files, %w[.env backend/src/config/secrets.js]
set :linked_dirs, %w[log tmp/pids tmp/cache storage]
```

Rampway:

```js
linkedFiles: [
  {path: ".env", required: true},
  {path: "backend/src/config/secrets.js"}
],
linkedDirs: ["log", "tmp/pids", "tmp/cache", "storage"],
```

Rampway linked files can be `required` (fail deploy if missing) or optional (silently skipped).

### 4. Convert hooks to task groups

Capistrano hooks run at specific points in the deploy lifecycle:

```ruby
after "deploy:published", "restart"
```

In Rampway, task groups run in config order within the deploy pipeline. No explicit before/after hooks — just order your task groups:

```js
tasks: {
  install: [...],
  migrate: [...],
  build: [...]
}
```

Task groups run after shared links are created, before the runtime handoff and publish. Use task helpers for common Node workflows:

```js
import {npmInstall, velociousMigrate, onlyIfChanged} from "rampway/tasks"

tasks: {
  install: [npmInstall({cwd: "backend", production: true})],
  migrate: [velociousMigrate({cwd: "backend", env: {NODE_ENV: "production"}})]
}
```

### 5. Convert standalone operational tasks

Keep operations that were invoked explicitly in Capistrano out of deploy task groups. Declare each one under the strict `operationalTasks` namespace and invoke its exact configured name:

```js
operationalTasks: {
  "cache:warm": {
    command: "npm run cache:warm",
    cwd: "backend",
    forwardEnv: ["CACHE_WARM_CREDENTIAL"]
  }
}
```

```bash
CACHE_WARM_CREDENTIAL=... rampway production task cache:warm
```

Rampway owns the configured transport, shared deploy lock, active-release resolution, and secret-value redaction. Replace the former caller's direct SSH with the Rampway invocation; do not retain an SSH fallback. Operational tasks are explicit and never run as part of every deploy.

### 6. Convert process management

Capistrano relies on SSH-based process management (puma, sidekiq, etc.) or custom tasks:

```ruby
namespace :deploy do
  task :restart do
    on roles(:app) do
      execute :sudo, :systemctl, :restart, "myapp"
    end
  end
end
```

In Rampway, process management is delegated to a runtime adapter. For Node projects, use Rollbridge:

```js
runtime: {
  type: "rollbridge",
  packageDir: "backend",
  config: "rollbridge.production.mjs",
  daemonLogPath: "/home/dev/myapp/shared/log/rollbridge.log",
  daemonPidPath: "/home/dev/myapp/shared/tmp/pids/rollbridge.pid"
}
```

Rollbridge handles zero-downtime restarts, health checks, and log access. Rampway orchestrates the release lifecycle; Rollbridge owns the process.

Use an explicit, self-contained Rollbridge `config`. The first successful
Rampway deploy snapshots it and records known-good runtime state in `shared/`.
After that cutover, `rampway production rollback --to <capistrano-release>` can
activate a retained pre-Rampway release even when that old checkout has no
Rampway helper or Rollbridge config. Rampway still runs the Rollbridge package
from the configured `packageDir` inside the selected release, but supplies the
trusted config from that release's indexed stable state. Subsequent successful
deploys retain their own snapshots, so rollback does not reuse the active
release's newer package or config contract. Do not create a separate recovery
stage or copy config-SHA/recovery scripts into the application.

Rollback does not rerun install, migration, or other deploy tasks. It activates
the target, runs normal health checks, and moves `current` only when healthy; a
failed attempt restores and checks the prior runtime. Test this exact retained
release case before removing Capistrano. If stable state is absent or does not
match the active release, rollback fails before mutation.

For simple setups without a process manager, use `runtime: {type: "none"}` and handle process restarts yourself via task commands.

### 7. Run the first deploy

```bash
# Validate
rampway production validate

# Plan (dry run)
rampway production plan

# Deploy
rampway production deploy
```

### 8. Verify and cleanup

```bash
rampway production status
rampway production releases
```

After verifying the first deploy works, remove your old Capistrano config files.

Do not delete retained Capistrano releases merely to make Rampway discovery
look uniform. Rampway lists safe legacy release directories but cleanup removes
only releases marked as managed by Rampway. Once the first deploy is healthy,
exercise an exact legacy rollback, verify runtime status and application health,
then return to the new release before retiring old deployment tooling.

## Key differences

- **No Ruby dependency.** Rampway runs on Node.js, matching your JS/TS stack.
- **Task groups replace hooks.** Order task groups instead of registering `after`/`before` callbacks. For Capistrano-style lifecycle callbacks, use `hooks` with named phases like `before_publish` and `after_cleanup`.
- **Operational tasks replace explicitly invoked remote tasks.** Configure an exact name under `operationalTasks`; Rampway executes it against the locked active release and securely forwards allowlisted caller environment values.
- **Runtime adapters replace process management tasks.** Rollbridge or a custom adapter handles zero-downtime handoff.
- **ESM config.** `rampway.config.mjs` uses ES module syntax, not Ruby DSL.
- **Health checks are built in.** Add `healthChecks` to verify deploy readiness before publishing.
- **Path-change gating.** Use `onlyIfChanged()` to skip task groups when nothing changed in their paths.
