web-ascender/kamal-previews

Preview environments for Kamal-deployed Rails apps. Open a pull request, get a per-PR Kamal app at a unique URL with a freshly cloned staging database. Close the PR, everything tears down. Single-step entry point — drop one step into a single-job workflow: jobs: preview: runs-on: ubuntu-latest permissions: contents: read packages: write pull-requests: write deployments: write steps: - uses: actions/checkout@v4 - uses: web-ascender/github-actions-kamal-previews@v1 with: base-deploy-file: config/deploy.staging.yml base-secrets-file: .kamal/secrets.staging domain-suffix: preview.example.com deploy-host: staging.example.com database-engine: postgres databases: | DATABASE_URL=myapp_staging:myapp_{db_slug} env: # Forwarded from `secrets.*` in the calling workflow: SSH_PRIVATE_KEY: <your DEPLOY_SSH_KEY secret> # Required only if the secrets file shells out to # `bin/rails credentials:fetch` to populate DATABASE_URL: RAILS_MASTER_KEY: <your STAGING_MASTER_KEY secret> Internally dispatches based on the triggering event: - pull_request opened/synchronize/reopened → deploy - pull_request closed → teardown - delete (branch) → teardown - anything else → skip (with notice) Two consumption forms; pick whichever fits: - **This composite action** — single-step, sibling-friendly. Use when your job needs sibling steps around kamal-previews (e.g. opening a VPN tunnel before SSH to a private deploy host, or a custom credential-fetch step). - **Reusable workflow** at `web-ascender/github-actions-kamal-previews/.github/workflows/preview.yml@v1` — multi-job, with deploy and teardown as separate jobs. Use when you don't need sibling steps and want stricter concurrency control. Both forms support `branch-pattern` and `max-active-previews`.

View on GitHub

Trust Signals

Scorecard Score
not yet scored
Maintenance Recency
Stale
License
None
namedescriptionrequireddefault
base-deploy-fileyes
domain-suffixyes
deploy-hostyes
database-enginenonone
databasesMulti-line list of databases to clone (postgres/mysql only). One entry per line, format `ENV_NAME=source_db:target_pattern`. Tokens in the pattern: `{slug}`, `{db_slug}`, `{base_database}` (= source). Behavior depends on the env-name suffix: - `*_URL` (e.g. `DATABASE_URL`, `QUEUE_DATABASE_URL`): the action clones the DB, sources `base-secrets-file` to read the original URL, swaps the dbname path segment for the per-PR target, and injects the rewritten URL into the deployed app via env.secret. Your database.yml, Rails credentials, and existing secrets file stay untouched. Recommended for most Rails apps. - any other suffix (e.g. `*_NAME`): the resolved DB name is written to env.clear and exported to $GITHUB_ENV. Use when your app reads the name directly or you want to handle URL building yourself. Example (Rails 8 multi-DB): DATABASE_URL=myapp_staging:myapp_{db_slug} QUEUE_DATABASE_URL=myapp_staging_queue:myapp_queue_{db_slug} CACHE_DATABASE_URL=myapp_staging_cache:myapp_cache_{db_slug} no""
database-admin-url-varName of the variable in `base-secrets-file` to source as the admin connection URL for cloning. Default: `DATABASE_URL`. Ignored when the runner env already has `DATABASE_ADMIN_URL` set, or when no base secrets file is configured. The fallback chain is: 1. `DATABASE_ADMIN_URL` env var (explicit override). 2. The `<database-admin-url-var>` variable read from `base-secrets-file` (e.g. `bin/rails credentials:fetch ...`). 3. Individual `PG_*` / `MYSQL_*` env vars (last-resort fallback). noDATABASE_URL
sqlite-source-pathno""
sqlite-target-path-patternno""
sqlite-also-cloneno""
base-secrets-fileno""
domain-label-patternno{slug}
service-patternno{base_service}-{slug}
destination-patternno{slug}
env-labelnopreview
env-overridesno""
env-secret-overridesno""
deploy-timeoutno""
builder-contextno""
prefix-stripnofeature/,feat/,fix/,bug/,bugfix/,chore/,hotfix/,release/
image-tagno""
memory-limitno""
cpu-limitno""
branch-patternBash-glob pattern; only branches matching it get previews. Empty = every PR. Teardowns ignore the pattern (they always run).no""
max-active-previewsHard cap on concurrent active previews. The deploy step refuses to start a NEW preview when this many are already active (an existing preview being re-deployed doesn't count against the cap). 0 (default) = unlimited. Counts environments named `preview-*` whose latest deployment status isn't `inactive`/`removed`/`failure`/`error`. no0
ruby-versionno3.3
kamal-versionno""
bundler-cacheRuns `bundle install` (with caching) during setup. Default `true` because the dominant Rails-secrets pattern (`bin/rails credentials:fetch`) needs it. Set to `false` only if you have no Gemfile.lock or your secrets pipeline is bundle-free.notrue
ssh-usernodeploy
ssh-portno22
namedescription
slug
db_slug
destination
proxy_host
url
database_name
mode