Reusable GitHub Actions CI for the Coroboros stack.
Drop into any @coroboros/* repo via uses: coroboros/ci/.github/workflows/<name>.yml@v0, or compose around the composite actions under .github/actions/.
Imposed, not proposed. Pipelines expose zero inputs: — same install flags, same publish auth, same security baseline across every Coroboros repo. Consumers wire it in.
Bundled NPM CI.
Consumer requirements:
.node-versionpackage.jsonwithpackageManager: "pnpm@X.Y.Z",scripts.lint,scripts.test.scripts.buildis optional (auto-detected).
pnpm-lock.yaml— required for--frozen-lockfile.
publish
Trigger: tag push
Sequence:
- Checkout
mainwith full history - Verify
mainHEAD matches the tag SHA - Run
check-docs - Run
javascript/base - Pin
package.jsonversion to the tag - Generate
CHANGELOG.mdsection viarelease/generate-changelog - Publish to npm — OIDC + provenance or token-based via
.npmrc(see Security) - Create GitHub Release via
release/github-release - Commit release artifacts back to
mainaschore: release ${tag} - Move rolling major tag
vNto the release commit (skipped on pre-release tags)
Reusable sub-workflow with three parallel scans:
gitleaks— Installsv8.30.1(SHA-256 verified), scans git history with thesecurity/.gitleaks.tomlruleset, fails on detected leaks. Emits SARIF as thegitleaks-reportartifact (30-day retention).dependency-review— PR-only; needs repo's Dependency graph enabled. Fails on high-severity CVE introduced by the dep diff. Usesactions/dependency-review-action@v4.osv-scanner— Scans lockfiles recursively against OSV.dev viagoogle/osv-scanner-action@v2. Fails on any known vulnerability.
Imposed on every Coroboros workflow. Standalone wire-up — see Examples.
Notes — pin via @v0 (rolling major, auto-bumped on each release) or @x.y.z (immutable). Pipelines don't chain via needs:; the only sub-workflow call is security → security.yml.
| Action | Type | Purpose |
|---|---|---|
check-docs |
transverse | Context dump + documentation check. |
javascript/base |
JavaScript | Sets up Node + corepack pnpm, caches the store, writes .npmrc from env, then installs, lints, builds (when present), tests. |
release/generate-changelog |
transverse | SemVer-strict tag guard + generates or reuses the ## vX.Y.Z section in CHANGELOG.md from Conventional Commits. Outputs body. Idempotent. |
release/github-release |
transverse | Creates the GitHub Release for the current tag. Body typically chained from release/generate-changelog (see Examples). |
Develop with Conventional Commits → tag → push. No manual CHANGELOG, no version bump.
Tags follow SemVer strict — 1.2.3, never v1.2.3.
Branch models
main-only — feature branch → PR → squash-merge to main → tag the merge commit → push.
develop + main — PR into develop → tag → release/x.y.z branch → merge to main → main reflects production.
Nobody pushes directly to protected branches (main, develop, release/x.y.z).
Conventional Commits → CHANGELOG
| Commit type | CHANGELOG subsection |
|---|---|
feat |
Features |
fix |
Fixes |
refactor |
Refactor |
perf |
Performance |
docs |
Documentation |
chore / ci / build |
Configuration |
test |
Tests |
style |
Style |
| Other / non-standard | Others |
!: or BREAKING CHANGE: |
Breaking Changes (always first) |
Section format: ## vX.Y.Z - DD/MM/YYYY. Idempotent. Reuses an existing hand-curated section for the tag if present.
Zero inputs on pipelines and on every composite — imposed, not proposed. Configuration flows through secrets: and the caller's vars context.
Secrets (caller's secrets: block)
| name | required | description |
|---|---|---|
NPM_CONFIG_FILE |
✔ | .npmrc content. Written to repo root by javascript/base. ${VAR} references inside are expanded by npm at install time. |
NPM_EXTRA_CONFIG |
Extra .npmrc lines appended after NPM_CONFIG_FILE. A secret — it lands in .npmrc, so it can carry auth material and must stay masked. |
|
NPM_PACKAGE_REGISTRY |
✔ | npm package registry URL. |
NPM_PACKAGE_PROXY_REGISTRY |
Optional npm proxy registry URL. | |
NPM_PACKAGE_REGISTRY_TOKEN |
Required for token-based publish to private registries. Absent → OIDC. |
javascript/base env contract (standalone composition)
| env | required | description |
|---|---|---|
NPM_CONFIG_FILE |
✔ — fail if missing | .npmrc content |
NPM_EXTRA_CONFIG |
Appended after NPM_CONFIG_FILE |
Set both at the caller's workflow- or job-level env:.
release/* composites I/O
release/generate-changelog — no inputs, no secrets. Reads GITHUB_REF_NAME. Requires fetch-depth: 0 for git describe. Output:
| output | description |
|---|---|
body |
CHANGELOG section body — use as release notes. |
release/github-release — input:
| input | required | description |
|---|---|---|
body |
✔ | Release notes body, typically steps.<id>.outputs.body from release/generate-changelog. |
Caller job needs permissions: contents: write. Uses ${{ github.token }} internally via GH_TOKEN.
Supply chain — pnpm install flags
pnpm install --frozen-lockfile --ignore-scripts runs inside javascript/base.
--frozen-lockfile— fails on stale or tamperedpnpm-lock.yaml. Gate against transitive-dependency injection.--ignore-scripts— skips lifecycle scripts (preinstall,install,postinstall) of every dependency. Cuts the postinstall supply-chain vector.
pnpm CLI resolved via corepack from packageManager. No floating version reaches the runner.
Publish — OIDC vs token auth
Auto-detected by NPM_PACKAGE_REGISTRY_TOKEN presence:
- Absent →
pnpm publish --provenance --no-git-checks(OIDC Trusted Publisher + provenance attestation, no long-lived token). - Present →
pnpm publish --no-git-checks(token-based via the.npmrcgenerated byjavascript/base).
Secret isolation
Each workflow_call.secrets: block declares ONLY the secrets the job consumes. No secrets: inherit anywhere.
Action pinning + Dependabot
Third-party actions across workflows + composites are pinned to a commit SHA with an inline # vX comment. Floating refs (@master, @main, @vX) are banned.
Self-CI binaries pinned by version. actionlint and gitleaks install from release tarballs with SHA-256 verification; yamllint via pip install with version pin. No curl | bash.
.github/dependabot.yml opens weekly grouped auto-PRs to bump pinned SHAs across .github/workflows/* and .github/actions/**/action.yml. Consumers should add their own ecosystem entries (e.g., npm).
Canonical gitleaks config
Canonical ruleset at security/.gitleaks.toml in this repo. Stack-specific rules cover Resend, Neon Postgres, PostHog, and GitHub fine-grained PATs on top of the gitleaks defaults.
security.yml sparse-checks the file out of coroboros/ci at runtime — imposed, no consumer override.
javascript-npm-packages.yml wire-up
# consumer-repo/.github/workflows/ci.yml
name: CI
on:
push:
branches: [develop, main]
tags: ['*']
pull_request:
workflow_dispatch:
jobs:
ci:
uses: coroboros/ci/.github/workflows/javascript-npm-packages.yml@v0
permissions:
contents: write # GitHub Release on tag
id-token: write # npm OIDC publish on tag
secrets:
NPM_CONFIG_FILE: ${{ secrets.NPM_CONFIG_FILE }}
NPM_PACKAGE_REGISTRY: ${{ secrets.NPM_PACKAGE_REGISTRY }}
NPM_PACKAGE_PROXY_REGISTRY: ${{ secrets.NPM_PACKAGE_PROXY_REGISTRY }}
NPM_PACKAGE_REGISTRY_TOKEN: ${{ secrets.NPM_PACKAGE_REGISTRY_TOKEN }}security.yml standalone (non-npm repo)
# consumer-repo/.github/workflows/security.yml
name: Security
on:
push:
branches: [develop, main]
pull_request:
schedule:
- cron: '0 0 * * 0' # weekly — catches CVEs published after last push
permissions:
contents: read
jobs:
scan:
uses: coroboros/ci/.github/workflows/security.yml@v0Compose with javascript/base
jobs:
custom:
runs-on: ubuntu-latest
permissions:
contents: read
env:
NPM_CONFIG_FILE: ${{ secrets.NPM_CONFIG_FILE }}
NPM_EXTRA_CONFIG: ${{ secrets.NPM_EXTRA_CONFIG }}
steps:
- uses: actions/checkout@v4
- uses: coroboros/ci/.github/actions/check-docs@v0
- uses: coroboros/ci/.github/actions/javascript/base@v0
- run: pnpm run my-custom-script
shell: bashCompose a custom release pipeline (non-npm artifact)
jobs:
publish:
if: ${{ github.ref_type == 'tag' }}
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
- id: changelog
uses: coroboros/ci/.github/actions/release/generate-changelog@v0
# ...your publish step (docker push, gh release upload, etc.)...
- uses: coroboros/ci/.github/actions/release/github-release@v0
with:
body: ${{ steps.changelog.outputs.body }}All Rights Reserved. See LICENSE.md.