181 lines
7.5 KiB
YAML
181 lines
7.5 KiB
YAML
name: Publish Signage Release
|
|
|
|
# Builds, proves, and publishes one Signage release. This is deliberately separate from CI: only an
|
|
# operator's workflow_dispatch on a selected branch can start it. The normal release ref is main.
|
|
# Every local gate, including the full external-package integration and ADR 0055 redistribution
|
|
# verification, finishes before the release/tag is created or an existing release is touched.
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
description: The Signage version to publish, equal to package and module metadata.
|
|
required: true
|
|
type: string
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
# Never cancel a publication part way, and never run two at once. A rerun safely resumes an
|
|
# identical partial upload, but refuses a tag, release, or asset that differs from this run.
|
|
concurrency:
|
|
group: publish-signage-release
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
release-gate:
|
|
name: release gate
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 75
|
|
outputs:
|
|
sha256: ${{ steps.canonical.outputs.sha256 }}
|
|
env:
|
|
DOCKER_BUILDKIT: "1"
|
|
EKKLESIA_CI_JOB_KEY: signage-publish-release
|
|
RELEASE_VERSION: ${{ inputs.version }}
|
|
RELEASE_TAG: v${{ inputs.version }}
|
|
RELEASE_REVISION: ${{ github.sha }}
|
|
steps:
|
|
- name: Check out the operator-selected revision
|
|
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 build and release tooling
|
|
run: |
|
|
corepack enable
|
|
corepack install
|
|
pnpm install --frozen-lockfile
|
|
|
|
# Match Core's manual publisher: releases come only from reviewed main. The source version is
|
|
# checked before the expensive gate, and the selected commit remains the immutable tag target.
|
|
- name: Validate requested version and selected revision
|
|
run: |
|
|
[[ "$GITHUB_REF" == refs/heads/main ]] || {
|
|
echo "publish: select main; got $GITHUB_REF" >&2
|
|
exit 1
|
|
}
|
|
[[ "$RELEASE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || {
|
|
echo "publish: version must be X.Y.Z; got $RELEASE_VERSION" >&2
|
|
exit 1
|
|
}
|
|
[[ "$RELEASE_REVISION" =~ ^[0-9a-f]{40}$ ]]
|
|
[[ "$(git rev-parse HEAD)" == "$RELEASE_REVISION" ]]
|
|
[[ -z "$(git status --porcelain)" ]]
|
|
node --input-type=module -e '
|
|
import { readFileSync } from "node:fs";
|
|
const requested = process.env.RELEASE_VERSION;
|
|
const packageVersion = JSON.parse(readFileSync("package.json", "utf8")).version;
|
|
const moduleVersion = JSON.parse(readFileSync("ekklesia.module.json", "utf8")).version;
|
|
if (packageVersion !== requested || moduleVersion !== requested) {
|
|
console.error(`publish: requested ${requested}, package ${packageVersion}, module ${moduleVersion}`);
|
|
process.exit(1);
|
|
}
|
|
console.log(`publish: source metadata agrees on ${requested}`);
|
|
'
|
|
|
|
# This is the only package build in the workflow. Every later step consumes this directory.
|
|
- name: Build the canonical Module Package pair once
|
|
id: canonical
|
|
run: bash ci/package.sh build/release-assets
|
|
|
|
- name: Verify the canonical Module Package
|
|
env:
|
|
EXPECTED_PACKAGE_SHA256: ${{ steps.canonical.outputs.sha256 }}
|
|
run: |
|
|
pnpm validate
|
|
python3 ci/release-assets.py verify-package \
|
|
build/release-assets "$RELEASE_TAG" "$RELEASE_REVISION"
|
|
|
|
- name: Fetch the pinned Ekklesia integration harness
|
|
run: bash ci/fetch-platform.sh
|
|
|
|
- name: Sweep stale CI stacks from the runner
|
|
run: bash build/platform/scripts/ci-sweep.sh --stale
|
|
|
|
- name: Run full integration with the exact release package
|
|
env:
|
|
EXPECTED_PACKAGE_SHA256: ${{ steps.canonical.outputs.sha256 }}
|
|
run: bash ci/integration.sh "$GITHUB_WORKSPACE/build/release-assets"
|
|
|
|
- name: Remove this publication's CI stacks
|
|
if: always()
|
|
run: |
|
|
if [ -f build/platform/scripts/ci-sweep.sh ]; then
|
|
bash build/platform/scripts/ci-sweep.sh --job
|
|
fi
|
|
|
|
- name: Build and verify required redistribution assets
|
|
env:
|
|
EXPECTED_PACKAGE_SHA256: ${{ steps.canonical.outputs.sha256 }}
|
|
run: |
|
|
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"
|
|
|
|
# The write-authorized job receives only the completely proven bundle. Artifact actions v3
|
|
# are required by Gitea; v4 refuses servers other than github.com.
|
|
- name: Hand the proven release bundle to the publisher
|
|
uses: actions/upload-artifact@c6a3b2bd78b3985e4b2f15397fec357f0fd808de # v3.2.2-node20
|
|
with:
|
|
name: signage-release-assets
|
|
path: build/release-assets/
|
|
if-no-files-found: error
|
|
retention-days: 90
|
|
|
|
publish:
|
|
name: publish
|
|
needs: release-gate
|
|
if: github.ref == 'refs/heads/main'
|
|
permissions:
|
|
contents: write
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
env:
|
|
EXPECTED_PACKAGE_SHA256: ${{ needs.release-gate.outputs.sha256 }}
|
|
RELEASE_VERSION: ${{ inputs.version }}
|
|
RELEASE_TAG: v${{ inputs.version }}
|
|
RELEASE_REVISION: ${{ github.sha }}
|
|
steps:
|
|
# The checked-out helper is the same reviewed revision the gate proved. Credentials are not
|
|
# persisted; the automatic token is exposed only to the final API call below.
|
|
- name: Check out the proven revision
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Receive the proven release bundle
|
|
uses: actions/download-artifact@ad191675b41f6a5b46da9a048cb6893812da158b # v3.1.0-node20
|
|
with:
|
|
name: signage-release-assets
|
|
path: build/release-assets/
|
|
|
|
- name: Recheck the received release bundle
|
|
run: |
|
|
python3 ci/release-assets.py verify \
|
|
build/release-assets "$RELEASE_TAG" "$RELEASE_REVISION"
|
|
|
|
# Only this final step can mutate Gitea. It creates v<version> at the selected commit and a
|
|
# prerelease if absent, then uploads the already-tested bytes. A rerun resumes only identical
|
|
# state. Every asset is read back without Authorization and checked byte for byte.
|
|
- name: Create the release and publish the exact tested assets
|
|
env:
|
|
GITHUB_TOKEN: ${{ github.token }}
|
|
run: |
|
|
python3 ci/release-assets.py publish \
|
|
build/release-assets "$RELEASE_TAG" "$RELEASE_REVISION"
|