Skip to main content

CI/CD — autolastic-saas-platform

This document covers every GitHub Actions workflow in the platform repo, how they chain together, and how instance repos consume them via reusable workflow calls.


Pipeline Overview

Platform repo pipeline (push to main)

Push to main


ci.yml
├── secrets-scan (gitleaks — blocks on leaked credentials)
├── actionlint (workflow YAML lint)
├── check-forbidden-strings
├── test (unit + integration, all packages)
├── lint (ESLint, all packages)
├── typecheck (tsc --noEmit, all packages)
├── build-shared
├── build-api / build-billing / build-media / build-smoke / build-infra / build-app (parallel)
└── synth (CDK synth with pre-seeded context)
└── ci-complete ✓ ← sentinel job; downstream workflow_run triggers watch this

ci-complete succeeds


bump-version.yml (auto-tags HEAD with next v0.0.X)


[vX.Y.Z tag pushed]


publish-packages.yml (stamps versions, builds all packages, npm publish to CodeArtifact)

Instance repo pipeline (push to main)

Push to main


ci.yml (instance)
├── check-platform-version (all 10 files reference the same @vX.Y.Z)
├── secrets-scan (gitleaks via reusable)
├── actionlint
├── shell-tests (bats tests/upgrade-platform.bats)
└── typecheck (via reusable-typecheck-instance.yml)

CI workflow completes successfully


deploy-infra.yml (workflow_run: "CI" → completed)
├── change detection (were any infra/ files changed?)
├── read-config (rootDomain from instance.config.ts)
├── deploy-infra (CDK deploy via reusable-deploy-infra.yml)
└── summary (exits 1 if changes were skipped due to CI failure)

Deploy Infra workflow completes successfully


deploy-app.yml (workflow_run: "Deploy Infra (CDK)" → completed)
├── change detection (were any infra/ or app/ files changed?)
├── deploy-app (build + S3 sync + CloudFront invalidation)
├── health-check (curl loop against APP_BASE_URL)
└── summary

Deploy App workflow completes successfully


smoke.yml (workflow_run: "Deploy App (S3 & CloudFront)" → completed)
└── smoke tests (Playwright via reusable-smoke.yml)

Platform CI — ci.yml

Triggers: push to main, pull_request to main, workflow_dispatch, workflow_call

Key behaviours:

  • secrets-scan and actionlint are in synth.needs — a failure in either blocks the entire pipeline
  • synth requires all build/test/lint/typecheck jobs to pass before running CDK synth
  • ci-complete is a sentinel job (if: always()) that exits 1 if synth did not succeed. Downstream workflow_run triggers (bump-version.yml) watch this job's conclusion, not the individual job results
  • CDK synth uses pre-seeded cdk.context.json values (AZs, hosted zone) so it can synth without AWS credentials

Jobs:

JobWhat it does
secrets-scanRuns gitleaks against commits in the push/PR range
actionlintLints all .github/workflows/*.yml files
check-forbidden-stringsScans for hardcoded secrets patterns via scripts/ci.check-forbidden-strings.sh
testAll unit + integration tests across all packages (requires Postgres service container)
lintESLint across api, shared, billing, media
typechecktsc --noEmit across all packages
build-sharedBuilds packages/shared (prerequisite for all other builds)
build-{api,billing,media,smoke,infra,app}Parallel builds; each re-builds shared first
synthCDK synth with pre-seeded context — validates the full infrastructure graph
ci-completeSentinel: exits 1 if synth failed/was skipped; always runs

Bump Version — bump-version.yml

Trigger: workflow_run on ci.ymlci-complete succeeded
Concurrency: bump-version (non-cancellable)

Calls scripts/bump-version.sh to create and push the next v0.0.X tag on the commit that passed CI.

  • Idempotent: if HEAD is already tagged, the script exits 0 with no action
  • Force-move: pass an explicit version + --sha to move an existing tag to a different commit
  • Output: tagged_version is written to GITHUB_OUTPUT and used by the publish job

Publish Packages — publish-packages.yml

Trigger: push of a v* tag
Concurrency: publish-packages (non-cancellable)
Permissions: id-token: write (OIDC → AWS)

Publishes all 7 @autolastic/saas-* packages to the private AWS CodeArtifact registry.

Steps:

  1. Validate tag format (v0.0.X) — exits 1 on malformed tags
  2. Authenticate npm against CodeArtifact via OIDC
  3. Stamp versions: node scripts/set-package-versions.js <version> updates all 7 package.json files
  4. Build packages in dependency order: shared first, then api, billing, media, infra, app, smoke in parallel (background processes with wait)
  5. Publish: npm publish --workspaces
  6. Write $GITHUB_STEP_SUMMARY with published package list

CodeArtifact registry: autolastic/npm-private in the AWS account associated with AWS_ROLE_ARN


Release — release.yml

Trigger: workflow_dispatch
Concurrency: release (non-cancellable)

Manually triggers a full end-to-end deploy on the platform repo itself: infra → app → smoke tests. Used for platform-level infrastructure changes that need to be validated against a real deployment.


Tag Stable — tag-stable.yml

Trigger: workflow_dispatch
Input: version — the vX.Y.Z to promote

Moves the stable tag to the specified version. Instance repos can pin to @stable instead of a specific version if they want automatic promotion to the latest tested release.


Reusable Workflow Catalog

All reusable workflows accept workflow_call and expose typed inputs and secrets.

reusable-deploy-infra-pipeline.yml

Full change detection → CDK deploy → summary pipeline for an instance repo's infra.

InputDefaultDescription
project_prefixCDK stack prefix (e.g. autolastic-app)
aws_regionus-east-1AWS region
infra_pathinfraPath to infra package
platform_versionPlatform version being deployed
event_namegithub.event_name from caller
commit_shaCommit SHA to report
workflow_run_conclusionConclusion of the triggering workflow_run
infra_file_patterns^infra/Regex for change detection

Critical: the summary job exits 1 when changes was skipped on a non-dispatch trigger, propagating CI failures downstream to block deploy-app.

reusable-deploy-app-pipeline.yml

Full change detection → Next.js build → S3 sync → CloudFront invalidation → health check → summary pipeline.

InputDefaultDescription
project_prefixCDK stack prefix
aws_regionus-east-1AWS region
app_pathappPath to app package
platform_versionPlatform version being deployed
event_namegithub.event_name from caller
commit_shaCommit SHA to report
workflow_run_conclusionConclusion of triggering workflow_run
app_file_patterns^(infra|app)/Regex for change detection

Secrets: AWS_ROLE_ARN, APP_BASE_URL

reusable-deploy-infra.yml

Bare CDK deploy step (no change detection, no pipeline wrapper). Used by reusable-rollback.yml.

reusable-deploy-app.yml

Bare Next.js build + S3 deploy step. Used by reusable-rollback.yml.

reusable-rollback.yml

Emergency redeployment at the currently-pinned platform version. Skips CI and smoke tests.

InputValuesDescription
scopeapp, infra, bothWhat to redeploy
platform_versionInformational — the version being rolled back to
  • scope: app — runs rollback-app only (rollback-infra is skipped)
  • scope: infra — runs rollback-infra only
  • scope: both — runs rollback-infra first, then rollback-app sequentially

reusable-smoke.yml

Runs Playwright smoke tests against the deployed instance. Requires CDK_CONTEXT_ROOT_DOMAIN to resolve the app URL.

reusable-typecheck-instance.yml

Authenticates npm against CodeArtifact, runs npm ci, then tsc --noEmit for both infra and app packages in an instance repo.

Use in instance CI:

typecheck:
uses: autolastic/autolastic-saas-platform/.github/workflows/reusable-typecheck-instance.yml@v0.0.24
with:
aws_region: "us-east-1"
infra_path: "infra"
app_path: "app"
secrets:
AWS_ROLE_ARN: ${{ secrets.AWS_ROLE_ARN }}

reusable-secrets-scan.yml

Runs gitleaks to detect accidentally committed credentials.

Scan scope:

  • pull_request — commits on the PR branch since it diverged from base
  • push — commits included in the push
  • workflow_dispatch / workflow_call — full history

Suppressing false positives:

# Inline suppression (on the line in question):
MY_VAR="not-a-real-secret" # gitleaks:allow

# Repo-level suppression (.gitleaks.toml):
[[allowlist]]
description = "Test fixtures"
paths = ["tests/"]

reusable-upgrade-platform.yml

Runs scripts/upgrade-platform.sh inside GitHub Actions to bump the platform version across all tracked files in an instance repo.


Secrets Scanning

The secrets-scan job runs on every CI build in both the platform and instance repos. It uses gitleaks with the default ruleset extended by repo-specific allowlists in .gitleaks.toml.

If gitleaks finds a real secret:

  1. Rotate the exposed credential immediately
  2. Remove it from git history: git filter-repo --path <file> --invert-paths
  3. Force-push the cleaned history

False positives are suppressed via .gitleaks.toml allowlists or inline # gitleaks:allow comments. Do not disable the scan entirely.


Actionlint

All .github/workflows/*.yml files are linted by actionlint on every CI run. It catches:

  • Invalid if: expressions
  • Wrong input types for workflow_call
  • Undefined secrets or contexts
  • Mismatched needs: references

The IDE extension may show false positives for cross-repository reusable workflow references (autolastic/autolastic-saas-platform/...@vX.Y.Z) because it cannot download remote workflow definitions at lint time. These are suppressed in .actionlint.yaml.


Version Consistency

Platform packages follow a single monotonically-increasing version (v0.0.X). Every tag push triggers publish, so version numbers are the same as the git tag.

Bumping a new version:

# In the platform repo — tag and push; CI auto-bumps after green builds
# Or manually:
./scripts/bump-version.sh v0.0.25

# In an instance repo — update all 10 tracked files:
./scripts/upgrade-platform.sh v0.0.25

Version tracking in instance repos: upgrade-platform.sh tracks and updates exactly 10 files:

FileMethod
app/package.jsonnpm dep semver
infra/package.jsonnpm dep semver
smoke/package.jsonnpm dep semver
.github/workflows/deploy-infra.ymlglobal sed (updates both platform_version: and @v refs)
.github/workflows/deploy-app.ymlglobal sed
.github/workflows/release.ymlglobal sed
.github/workflows/rollback.ymlglobal sed
.github/workflows/smoke.yml@v-prefixed sed
.github/workflows/upgrade-platform.yml@v-prefixed sed
.github/workflows/ci.yml@v-prefixed sed

The instance CI's check-platform-version job verifies all 10 are in sync on every PR and push.