brain / .github /workflows /release.yaml
Leon4gr45's picture
Deploy complete BrainAPI source 81ad5ce1c9c1
eb60b6b verified
Raw History Blame Contribute Delete
5.95 kB
name: Publish tagged container
on:
push:
tags: ["v*"]
permissions:
contents: read
checks: read
actions: read
packages: write
id-token: write
jobs:
validate-tag:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.tag.outputs.version }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- id: tag
run: python scripts/release_version.py "$GITHUB_REF_NAME" >> "$GITHUB_OUTPUT"
- name: Require tag version to match project metadata
run: |
project_version=$(python -c 'import tomllib; print(tomllib.load(open("pyproject.toml","rb"))["project"]["version"])')
expected_version="${{ steps.tag.outputs.project_version }}"
test "$project_version" = "$expected_version"
- name: Require the tagged commit to be on main
run: git merge-base --is-ancestor "$GITHUB_SHA" origin/main
verify-required-checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Require successful quality and heavy validation for this commit
uses: actions/github-script@v7
with:
script: |
const required = [
"python",
"benchmark-helpers",
"frontend (console)",
"frontend (tui)",
"rust",
"image-light-smoke",
"heavy-smoke-and-restore",
];
const runs = await github.paginate(github.rest.checks.listForRef, {
owner: context.repo.owner,
repo: context.repo.repo,
ref: context.sha,
per_page: 100,
});
const failed = required.filter((name) =>
!runs.some((run) => run.name === name && run.conclusion === "success")
);
if (failed.length) {
core.setFailed(`Missing successful checks for ${context.sha}: ${failed.join(", ")}`);
}
- name: Download exact-commit validation artifacts
uses: actions/github-script@v7
with:
script: |
const fs = require("fs");
const required = [
["quality.yaml", "light-production-validation", "light.zip"],
["heavy-validation.yaml", "heavy-production-validation", "heavy.zip"],
];
for (const [workflow, artifactName, output] of required) {
const response = await github.rest.actions.listWorkflowRuns({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: workflow,
head_sha: context.sha,
status: "completed",
per_page: 100,
});
const run = response.data.workflow_runs.find(
(item) => item.conclusion === "success"
);
if (!run) {
core.setFailed(`No successful ${workflow} run found for ${context.sha}`);
return;
}
const artifacts = await github.paginate(
github.rest.actions.listWorkflowRunArtifacts,
{
owner: context.repo.owner,
repo: context.repo.repo,
run_id: run.id,
per_page: 100,
}
);
const artifact = artifacts.find(
(item) => item.name === artifactName && !item.expired
);
if (!artifact) {
core.setFailed(`Missing ${artifactName} on workflow run ${run.id}`);
return;
}
const archive = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: artifact.id,
archive_format: "zip",
});
fs.writeFileSync(output, Buffer.from(archive.data));
}
- name: Enforce recorded product, restore, security, and benchmark gates
run: |
mkdir -p gate-input/light gate-input/heavy gate-artifacts
unzip -q light.zip -d gate-input/light
unzip -q heavy.zip -d gate-input/heavy
python scripts/assemble_release_artifacts.py --light gate-input/light --heavy gate-input/heavy --output gate-artifacts
python scripts/check_release_readiness.py gate-artifacts
- uses: actions/upload-artifact@v4
with:
name: release-evidence-${{ github.ref_name }}
path: gate-artifacts/
publish:
runs-on: ubuntu-latest
needs: [validate-tag, verify-required-checks]
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: release-evidence-${{ github.ref_name }}
path: gate-artifacts
- name: Load and verify the gated image
run: |
gzip -dc gate-artifacts/brainapi-image.tar.gz | docker load
expected=$(python -c 'import json; print(json.load(open("gate-artifacts/security.json"))["image_digest"])')
actual=$(docker image inspect brainapi:ci --format '{{.Id}}')
test "$actual" = "$expected"
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- id: publish
name: Publish the unchanged validated image
run: |
target="ghcr.io/lumen-labs/brainapi:$GITHUB_REF_NAME"
docker tag brainapi:ci "$target"
docker push "$target"
digest=$(docker image inspect "$target" --format '{{index .RepoDigests 0}}' | cut -d@ -f2)
test -n "$digest"
echo "$digest" > image-digest.txt
- uses: actions/upload-artifact@v4
with:
name: release-image-digest-${{ needs.validate-tag.outputs.version }}
path: image-digest.txt