CI/CD setup

Automate SteadyCron manifest deployments with GitHub Actions — plan diffs on pull requests, apply on merge.

Running steadycron plan in every pull request and steadycron apply --prune on merge turns your manifest into a GitOps workflow: reviewers see exactly what cron changes a PR introduces, and the main branch is always the source of truth.

Prerequisites

  • A SteadyCron API key (create one in Settings → API keys)
  • Your manifest committed to the repo (see Migrate to IaC)

Step 1 — add the API key as a secret

In your GitHub repository, go to Settings → Secrets and variables → Actions and add:

NameValue
STEADYCRON_API_KEYYour SteadyCron API key (sc_...)

Add any other environment-specific secrets your manifest references (e.g. SLACK_WEBHOOK_URL).

For plan-only pipelines (read operations), you can create a separate read-only API key and use it exclusively in the PR workflow.

Workflow 1 — plan diff on pull request

Post the plan output as a PR comment whenever a manifest file changes:

# .github/workflows/cron-plan.yml
name: Cron plan

on:
  pull_request:
    paths:
      - 'manifests/**'

jobs:
  plan:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    steps:
      - uses: actions/checkout@v4

      - uses: steadycron/action@v1
        with:
          command: plan
          manifest: manifests/production.yaml
          comment-on-pr: 'true'
        env:
          STEADYCRON_API_KEY: ${{ secrets.STEADYCRON_API_KEY }}
          SLACK_WEBHOOK_URL:  ${{ secrets.SLACK_WEBHOOK_URL }}

The action posts a comment like:

SteadyCron plan — manifests/production.yaml

  ~ weekly-digest    update  retries: 2 → 3
  + invoice-cron     create  http  0 17 * * 5

  1 to create · 1 to update · 0 to delete

Workflow 2 — apply on merge

Apply the manifest (with --prune) whenever the main branch is updated:

# .github/workflows/cron-apply.yml
name: Cron apply

on:
  push:
    branches:
      - main
    paths:
      - 'manifests/**'

jobs:
  apply:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: steadycron/action@v1
        with:
          command: apply
          manifest: manifests/production.yaml
          prune: 'true'
        env:
          STEADYCRON_API_KEY: ${{ secrets.STEADYCRON_API_KEY }}
          SLACK_WEBHOOK_URL:  ${{ secrets.SLACK_WEBHOOK_URL }}

Action inputs

InputDescriptionDefault
commandplan, apply, validate, or syncplan
manifestPath to the manifest file or directory
pruneIf true, pass --prune to applyfalse
comment-on-prIf true, post the plan output as a PR commentfalse

All ${ENV_VAR} placeholders in your manifest are read from the job’s environment — pass them via the env: block in your workflow step.

Multiple environments

Manage separate staging and production manifests with a matrix:

jobs:
  apply:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        env: [staging, production]
    steps:
      - uses: actions/checkout@v4
      - uses: steadycron/action@v1
        with:
          command: apply
          manifest: manifests/${{ matrix.env }}.yaml
          prune: 'true'
        env:
          STEADYCRON_API_KEY: ${{ secrets[format('STEADYCRON_API_KEY_{0}', matrix.env)] }}

Least-privilege API keys

For plan-only workflows (PR comments), use a read-only API key that cannot make changes. Create a separate key in the dashboard with read scope only and store it as a separate secret (e.g. STEADYCRON_API_KEY_RO).

This limits the blast radius if the PR workflow secret is ever compromised.

Validate on every push

Add a validate job to catch schema errors before plan runs:

- uses: steadycron/action@v1
  with:
    command: validate
    manifest: manifests/production.yaml
  env:
    STEADYCRON_API_KEY: ${{ secrets.STEADYCRON_API_KEY }}

validate exits non-zero on schema errors and does not require an API key, but passing one allows the server to validate against your account’s current limits.