name: CI # Signage is a standalone repository: every job installs it with --frozen-lockfile from its own # pnpm-lock.yaml and takes Ekklesia's module-development packages (@ekklesia/*) from Ekklesia's # public package registry (.npmrc; platform ADRs 0053 and 0054). Only the integration job fetches # Core source: one immutable commit through the pinned Gitea pull ref in ci/platform.env, used as the # generic deployment harness and image source, never as a workspace dependency. # # The same trust rules as the platform's own CI (docs/development/security.md there): # * every action is pinned by a full commit SHA; # * every checkout sets `persist-credentials: false`, and nothing in this workflow pushes; # * no repository, organization, or instance secret is referenced; # * there is no `pull_request_target`; # * the self-hosted runner executes trusted, reviewed code only. # # The server is Gitea 1.27, which honours `concurrency` cancellation; `permissions` and # `timeout-minutes` are declared so the workflow is correct wherever they are enforced. # Artifact actions v3 are deliberate: Gitea implements that protocol, while v4 refuses non-GitHub # servers. A tag job uses Gitea's automatic per-job token only after every required job succeeds. on: pull_request: push: branches: - main tags: - "v*" permissions: contents: read concurrency: group: ci-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: lint: name: lint runs-on: ubuntu-latest timeout-minutes: 15 steps: - name: Check out repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: # Nothing here pushes, so the job token is never needed after checkout and must not be left # in .git/config for a later step to read. persist-credentials: false - name: Set up Node.js at the repository's version uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: .node-version # pnpm at exactly the `packageManager` version, and @ekklesia/* from the public registry named in # .npmrc: no token, and no Ekklesia checkout. - name: Install (frozen lockfile) run: | corepack enable corepack install pnpm install --frozen-lockfile - name: Lint run: pnpm lint - name: Check that CI cleans up every stack it starts run: | bash ci/check-cleanup.sh python3 -m unittest ci/test_release_assets.py typecheck: name: typecheck runs-on: ubuntu-latest timeout-minutes: 15 steps: - name: Check out repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: # Nothing here pushes, so the job token is never needed after checkout and must not be left # in .git/config for a later step to read. persist-credentials: false - name: Set up Node.js at the repository's version uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: .node-version # pnpm at exactly the `packageManager` version, and @ekklesia/* from the public registry named in # .npmrc: no token, and no Ekklesia checkout. - name: Install (frozen lockfile) run: | corepack enable corepack install pnpm install --frozen-lockfile - name: Typecheck the server, browser, and browser-test projects run: pnpm typecheck test: name: test runs-on: ubuntu-latest timeout-minutes: 15 steps: - name: Check out repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: # Nothing here pushes, so the job token is never needed after checkout and must not be left # in .git/config for a later step to read. persist-credentials: false - name: Set up Node.js at the repository's version uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: .node-version # pnpm at exactly the `packageManager` version, and @ekklesia/* from the public registry named in # .npmrc: no token, and no Ekklesia checkout. - name: Install (frozen lockfile) run: | corepack enable corepack install pnpm install --frozen-lockfile - name: Unit, browser, and display contract tests run: pnpm test build: name: build runs-on: ubuntu-latest timeout-minutes: 20 steps: - name: Check out repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: # Nothing here pushes, so the job token is never needed after checkout and must not be left # in .git/config for a later step to read. persist-credentials: false - name: Set up Node.js at the repository's version uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: .node-version # pnpm at exactly the `packageManager` version, and @ekklesia/* from the public registry named in # .npmrc: no token, and no Ekklesia checkout. - name: Install (frozen lockfile) run: | corepack enable corepack install pnpm install --frozen-lockfile - name: Build the server and both browser artifacts run: pnpm build # The built package and its migrations, read the way the platform reads them, with only the # published SDK parsers (scripts/validate-module.mjs says what only Ekklesia itself can check). - name: Validate the built module and its migrations run: pnpm validate # Build the canonical pair once. Integration downloads these exact bytes, and a successful tag # run's release job later downloads the same artifact; neither consumer is allowed to repackage. package: name: package runs-on: ubuntu-latest timeout-minutes: 20 outputs: sha256: ${{ steps.canonical.outputs.sha256 }} steps: - name: Check out repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Set up Node.js at the repository's version uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: .node-version - name: Install (frozen lockfile) run: | corepack enable corepack install pnpm install --frozen-lockfile - name: Build the canonical Module Package pair id: canonical run: bash ci/package.sh - name: Hand the canonical pair to its consumers uses: actions/upload-artifact@c6a3b2bd78b3985e4b2f15397fec357f0fd808de # v3.2.2-node20 with: name: signage-module-package path: build/module-package/ if-no-files-found: error retention-days: 30 # The real linux-x64-glibc Signage Module Package inside Ekklesia, end to end. The pinned platform # builds one generic image with no bundled module source, verifies and installs Signage into # persistent module storage, applies its migrations, and drives its split-origin Dashboard and # kiosk display in real browsers. It then checks process restart, container recreation, lifecycle # disable/re-enable, removal, and reinstall against the same database and package. integration: name: integration needs: - test - build - package runs-on: ubuntu-latest timeout-minutes: 45 env: DOCKER_BUILDKIT: "1" EXPECTED_PACKAGE_SHA256: ${{ needs.package.outputs.sha256 }} # Names this job's stacks, so its final step removes exactly these (the platform's # scripts/lib/ci-project.sh). EKKLESIA_CI_JOB_KEY: signage-integration steps: - name: Check out repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Set up Node.js at the repository's version uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: .node-version - name: Install (frozen lockfile) run: | corepack enable corepack install pnpm install --frozen-lockfile - name: Receive the canonical Module Package pair uses: actions/download-artifact@ad191675b41f6a5b46da9a048cb6893812da158b # v3.1.0-node20 with: name: signage-module-package path: build/module-package/ - name: Fetch the pinned Ekklesia integration harness run: bash ci/fetch-platform.sh # A job the runner killed (cancelled, or past the runner's own time limit) never ran its # cleanup. Anything a CI job left on this shared daemon more than three hours ago goes first. - name: Sweep stale CI stacks from the runner run: bash build/platform/scripts/ci-sweep.sh --stale - name: Install and run the prebuilt external Signage package run: bash ci/integration.sh # Whatever happened above, this job's own stacks do not outlive it. If the first step failed, # nothing was started. - name: Remove this job's CI stacks if: always() run: | if [ -f build/platform/scripts/ci-sweep.sh ]; then bash build/platform/scripts/ci-sweep.sh --job fi # Existing Signage releases and tags are created by a person. On a v* tag, attach only the # package pair this run's integration consumed, plus ADR 0055's required corresponding-source # material. No release or tag is created here, and an existing asset is never replaced. release: name: release needs: - lint - typecheck - test - build - package - integration if: startsWith(github.ref, 'refs/tags/v') permissions: contents: write runs-on: ubuntu-latest timeout-minutes: 30 steps: - name: Check out repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Set up Node.js at the repository's version uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: .node-version - name: Install the reviewed release tooling run: | corepack enable corepack install pnpm install --frozen-lockfile - name: Receive the exact pair integration tested uses: actions/download-artifact@ad191675b41f6a5b46da9a048cb6893812da158b # v3.1.0-node20 with: name: signage-module-package path: build/release-assets/ - name: Verify tag, revision, package bytes, and public-distribution material env: EXPECTED_PACKAGE_SHA256: ${{ needs.package.outputs.sha256 }} RELEASE_TAG: ${{ github.ref_name }} RELEASE_REVISION: ${{ github.sha }} run: | python3 ci/release-assets.py verify-package build/release-assets "$RELEASE_TAG" "$RELEASE_REVISION" package="$(find build/release-assets -maxdepth 1 -type f -name 'signage-*-linux-x64-glibc.tar.gz')" pnpm exec ekklesia-corresponding-source acquire \ --package "$package" --cache "$RUNNER_TEMP/signage-corresponding-sources" pnpm exec ekklesia-corresponding-source build \ --package "$package" --cache "$RUNNER_TEMP/signage-corresponding-sources" \ --out build/release-assets source="${package%.tar.gz}-corresponding-source.tar.gz" metadata="${package%.tar.gz}.redistribution.json" pnpm exec ekklesia-corresponding-source verify \ --package "$package" --source "$source" --metadata "$metadata" python3 ci/release-assets.py verify build/release-assets "$RELEASE_TAG" "$RELEASE_REVISION" - name: Attach the exact tested package and its required source material env: EXPECTED_PACKAGE_SHA256: ${{ needs.package.outputs.sha256 }} GITHUB_TOKEN: ${{ github.token }} RELEASE_TAG: ${{ github.ref_name }} RELEASE_REVISION: ${{ github.sha }} run: python3 ci/release-assets.py upload build/release-assets "$RELEASE_TAG" "$RELEASE_REVISION"