Skip to main content

Uniform CI β€” the shared hard gate

How every bewith-dev TS/JS repo runs the same CI quality gate (lint + test + typecheck) from one shared reusable workflow, instead of each repo copy-pasting its own. This is the CI half of the platform's hard-gate goal; the coding rules it runs live in coding-standards.md and @bewith-dev/eslint-plugin.

Status​

Current state (org scan, 2026-06-01)​

Before this workflow there was no shared reusable CI gate β€” every repo rolled its own pr-validation / pr-tests, and coverage was uneven:

Repolinttesttypechecknotes
backend-servicesβœ…βœ…βŒpr-tests.yml (lint/test/build, sharded)
management-webappβœ…βœ…βŒsplit across pr-validation + tests.yml (Jest + Playwright)
organization-dashboardβœ…βœ…βŒpr-validation.yml
support-tool-frontendβœ…βœ…βŒpr-validation.yml
sqs-consumerβœ…βŒβŒlint + build only
design-systemβœ…βŒβŒlint + build
with-calendarβœ…βŒβŒlint + build
facility-rentals❌❌❌deploy-only (Docker build on tag)
cognito-lambdas❌❌❌build/deploy only
bewith-consumer-frontend❌❌❌no .github/workflows at all
migrations-tool❌❌❌ops workflows only β€” PRs ungated
loki-cloudwatch-forwarderβŒβŒβŒβ€”
Kubernetes-deployments❌❌❌ops/GitOps only

Findings:

  • No cross-repo reuse. The only workflow_call reusables (backend-services build-*/deploy-*) are self-consumed deploy orchestration, not a shared CI gate. aws-ecs-task-definition-creator is a composite action, not a reusable workflow.
  • tsc --noEmit typecheck runs nowhere β€” type errors are only caught implicitly by next build / the build step.
  • Four repos have no PR gate at all (including the modern bewith-consumer-frontend).
  • Version drift in @bewith-dev/eslint-plugin (1.9.1 β†’ 2.1.1), and bewith-consumer-frontend doesn't depend on it.

Target design​

One reusable workflow, hosted org-wide, consumed by a tiny caller in each repo, required by branch protection.

  1. Host it in bewith-dev/bewith-docs at .github/workflows/reusable-ci.yml, on: workflow_call β€” one shared home for the platform. A GitHub reusable workflow can be uses:-referenced from any repo; the org .github repo is not required (it is special only for the org profile, default community-health files, and starter templates β€” a reusable workflow is none of those). Consumers pin a dedicated ci-vN tag, so bewith-docs content releases that don't move that tag never affect them. One-time human step: set bewith-docs β†’ Settings β†’ Actions β†’ General β†’ Access = "Accessible from repositories in the organization", and confirm the org "Allow actions and reusable workflows" policy permits it.

  2. The reusable workflow β€” parameterized, fail-fast, package-manager-aware. Inputs: node-version (default 20.x), package-manager (npm|pnpm|yarn, default npm), run-tests (default true). One ci job, sequential steps:

    # bewith-dev/bewith-docs/.github/workflows/reusable-ci.yml β€” abridged; full file in this repo
    on:
    workflow_call:
    inputs:
    node-version: { type: string, default: '20.x' }
    package-manager: { type: string, default: npm } # npm | pnpm | yarn
    run-tests: { type: boolean, default: true }
    jobs:
    ci:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - if: ${{ inputs.package-manager == 'pnpm' }}
    uses: pnpm/action-setup@v4 # pnpm must exist before setup-node's cache
    - uses: actions/setup-node@v4
    with:
    node-version: '${{ inputs.node-version }}'
    cache: '${{ inputs.package-manager }}'
    registry-url: 'https://npm.pkg.github.com'
    - run: <pm> install # npm ci / pnpm install --frozen-lockfile / yarn --frozen-lockfile
    env: { NODE_AUTH_TOKEN: '${{ secrets.DEPLOY_TOKEN || secrets.GITHUB_TOKEN }}' }
    - run: <pm> run lint # eslint via @bewith-dev/eslint-plugin
    - if: ${{ hashFiles('tsconfig.json') != '' }}
    run: npx --no-install tsc --noEmit # the typecheck nobody runs today; skipped for pure-JS repos
    - if: ${{ inputs.run-tests }}
    run: <pm> run test

    Each repo must expose lint and test package scripts and a tsc --noEmit-able tsconfig.json. The lint script should fail on errors (eslint . or a lint:errors-style script aliased to lint).

  3. The per-repo caller (replaces each repo's hand-rolled pr-validation) β€” templated at templates/consumer-repo/.github/workflows/ci.yml:

    # <repo>/.github/workflows/ci.yml
    name: CI
    on: { pull_request: { branches: [master, main] } }
    jobs:
    ci:
    uses: bewith-dev/bewith-docs/.github/workflows/reusable-ci.yml@ci-v1
    secrets: inherit
    with: { node-version: '20.x', package-manager: pnpm }
  4. Org branch protection requires the CI / ci check β†’ a repo cannot merge a red PR. This is the actual hard gate; the reusable workflow is just how the check gets there uniformly.

  5. Auto-bump the shared eslint-plugin β€” add Renovate (or Dependabot) org-wide to open a PR in every repo when @bewith-dev/eslint-plugin publishes a new version. Closes the version-drift gap and the "PR after a bump" ask without manual chasing. Pin the reusable workflow by @v1 tag and move the tag on release.

Adoption sweep (existing repos)​

The template covers new repos; existing repos get retrofitted. Per repo:

  1. Ensure @bewith-dev/eslint-plugin is a devDependency at the current version and the eslint config extends it.
  2. Ensure package.json has lint:errors, test, and a tsc --noEmit-able tsconfig.
  3. Add the caller ci.yml (above); delete the bespoke pr-validation/pr-tests.
  4. Turn on branch protection requiring CI / ci.

Sweep checklist (from the scan):

  • Add CI from scratch: bewith-consumer-frontend, migrations-tool, loki-cloudwatch-forwarder, facility-rentals, cognito-lambdas.
  • Add the missing test step: sqs-consumer, design-system, with-calendar.
  • Add tsc --noEmit: all repos (runs nowhere today).
  • Onboard bewith-consumer-frontend to @bewith-dev/eslint-plugin (it doesn't depend on it).
  • Resolve version drift via Renovate (1.9.1 β†’ latest across all).
  • PHP repos (superco-consumer, superco-management) are out of scope for this (no eslint) β€” they need a separate PHPCS/PHPStan reusable workflow as a parallel track.

Scope note​

This runbook is TS/JS. PHP needs its own reusable workflow (PHPCS/PHPStan), owned by legacy-php-guide β€” but the Yii2 repos are deprecation targets, so invest minimally there.

See also​

  • coding-standards.md Β§Enforcing across repos β€” the strategy this runbook implements.
  • enforcement-tiers.md β€” where this CI gate sits among the platform's enforcement layers.
  • The uniform-CI ClickUp task tracks the build-out.