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-scanandactionlintare insynth.needs— a failure in either blocks the entire pipelinesynthrequires all build/test/lint/typecheck jobs to pass before running CDK synthci-completeis a sentinel job (if: always()) that exits 1 ifsynthdid not succeed. Downstreamworkflow_runtriggers (bump-version.yml) watch this job's conclusion, not the individual job results- CDK synth uses pre-seeded
cdk.context.jsonvalues (AZs, hosted zone) so it can synth without AWS credentials
Jobs:
| Job | What it does |
|---|---|
secrets-scan | Runs gitleaks against commits in the push/PR range |
actionlint | Lints all .github/workflows/*.yml files |
check-forbidden-strings | Scans for hardcoded secrets patterns via scripts/ci.check-forbidden-strings.sh |
test | All unit + integration tests across all packages (requires Postgres service container) |
lint | ESLint across api, shared, billing, media |
typecheck | tsc --noEmit across all packages |
build-shared | Builds packages/shared (prerequisite for all other builds) |
build-{api,billing,media,smoke,infra,app} | Parallel builds; each re-builds shared first |
synth | CDK synth with pre-seeded context — validates the full infrastructure graph |
ci-complete | Sentinel: exits 1 if synth failed/was skipped; always runs |
Bump Version — bump-version.yml
Trigger: workflow_run on ci.yml → ci-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 +
--shato move an existing tag to a different commit - Output:
tagged_versionis written toGITHUB_OUTPUTand 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:
- Validate tag format (
v0.0.X) — exits 1 on malformed tags - Authenticate npm against CodeArtifact via OIDC
- Stamp versions:
node scripts/set-package-versions.js <version>updates all 7package.jsonfiles - Build packages in dependency order:
sharedfirst, thenapi,billing,media,infra,app,smokein parallel (background processes withwait) - Publish:
npm publish --workspaces - Write
$GITHUB_STEP_SUMMARYwith 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.
| Input | Default | Description |
|---|---|---|
project_prefix | — | CDK stack prefix (e.g. autolastic-app) |
aws_region | us-east-1 | AWS region |
infra_path | infra | Path to infra package |
platform_version | — | Platform version being deployed |
event_name | — | github.event_name from caller |
commit_sha | — | Commit SHA to report |
workflow_run_conclusion | — | Conclusion 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.
| Input | Default | Description |
|---|---|---|
project_prefix | — | CDK stack prefix |
aws_region | us-east-1 | AWS region |
app_path | app | Path to app package |
platform_version | — | Platform version being deployed |
event_name | — | github.event_name from caller |
commit_sha | — | Commit SHA to report |
workflow_run_conclusion | — | Conclusion 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.
| Input | Values | Description |
|---|---|---|
scope | app, infra, both | What to redeploy |
platform_version | — | Informational — the version being rolled back to |
scope: app— runsrollback-apponly (rollback-infrais skipped)scope: infra— runsrollback-infraonlyscope: both— runsrollback-infrafirst, thenrollback-appsequentially
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 basepush— commits included in the pushworkflow_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:
- Rotate the exposed credential immediately
- Remove it from git history:
git filter-repo --path <file> --invert-paths - 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:
| File | Method |
|---|---|
app/package.json | npm dep semver |
infra/package.json | npm dep semver |
smoke/package.json | npm dep semver |
.github/workflows/deploy-infra.yml | global sed (updates both platform_version: and @v refs) |
.github/workflows/deploy-app.yml | global sed |
.github/workflows/release.yml | global sed |
.github/workflows/rollback.yml | global 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.