Files
logos-dev-boost/.github/workflows/ci.yml
2026-06-05 17:57:47 -04:00

292 lines
12 KiB
YAML

name: CI
on:
push:
branches: [master, main]
pull_request:
branches: [master, main]
# One-time setup for the clickable doc-test report links (publish-report job):
# Repo Settings → Pages → "Build and deployment" → Source: "Deploy from a
# branch", Branch: `gh-pages` / `(root)`. The publish-report job creates the
# gh-pages branch on its first run; GITHUB_TOKEN already has the permissions
# granted below. The doc-tests run on both ubuntu-latest and macos-latest, and
# each run publishes one HTML report per OS to
# https://<owner>.github.io/<repo>/pr-<N>/<os>/ (PRs) or .../main/<os>/
# (pushes), with a landing page at .../pr-<N>/ linking to both. It also
# posts/updates a PR comment with the per-OS links. Fork PRs get a read-only
# token, so for them the Pages push and comment are skipped (the per-OS
# artifacts are still made).
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v27
with:
extra_nix_config: |
experimental-features = nix-command flakes
- uses: cachix/cachix-action@v15
with:
name: logos-co
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- name: Build logos-dev-boost CLI
run: nix build -L
scaffold-tests:
runs-on: ubuntu-latest
needs: build
strategy:
fail-fast: false
matrix:
type:
- module
- ui-qml
- ui-qml-backend
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v27
with:
extra_nix_config: |
experimental-features = nix-command flakes
- uses: cachix/cachix-action@v15
with:
name: logos-co
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
# git is needed inside the scaffolded project so `nix build` can see
# the files (flakes only consider git-tracked files).
- name: Configure git identity for scaffold init
run: |
git config --global user.email "ci@logos.test"
git config --global user.name "Logos CI"
- name: Scaffold + build --type ${{ matrix.type }}
run: ./tests/run-scaffold-tests.sh ${{ matrix.type }}
# Runs the executable module-scaffold doc-test
# (doctests/dev-boost-scaffold-module.test.yaml) end-to-end via the shared
# doctest CLI: scaffolds a crypto_utils module with THIS commit of dev-boost,
# builds it, introspects it with lm, runs its unit tests, calls it through
# logoscore, and asserts on the output.
doctests:
name: dev-boost doc-tests (${{ matrix.os }})
needs: build
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 90
steps:
- uses: actions/checkout@v4
# DeterminateSystems' installer (instead of cachix/install-nix-action)
# because the doc-tests run on macos-latest too, where the cachix installer
# collides with the runner's pre-existing _nixbld build users
# (eDSRecordAlreadyExists). This installer handles that and enables flakes
# by default, so no extra_nix_config is needed.
- uses: DeterminateSystems/nix-installer-action@main
- uses: cachix/cachix-action@v15
with:
name: logos-co
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
# git is needed inside the scaffolded project so `nix build` can see
# the files (flakes only consider git-tracked files).
- name: Configure git identity for scaffold init
run: |
git config --global user.email "ci@logos.test"
git config --global user.name "Logos CI"
# Resolve the commit under test. For pull requests this is the PR's head
# commit (not the synthetic merge commit); for pushes it's the pushed
# commit. Passed to --release-for so the doc-test scaffolds with THIS commit
# of logos-dev-boost instead of the latest published flake.
#
# Fork PRs are the exception: their head commit lives in the fork, not in
# logos-co/logos-dev-boost, so nix could not fetch
# `github:logos-co/logos-dev-boost/<sha>`. We blank the SHA for forks
# (--release-for repo= → pins that repo to latest), so the doc-test still
# runs for fork PRs, just against master.
- name: Resolve commit under test
id: commit
shell: bash
run: |
if [ "${{ github.event_name }}" = "pull_request" ] && \
[ "${{ github.event.pull_request.head.repo.fork }}" = "true" ]; then
echo "sha=" >> "$GITHUB_OUTPUT"
echo "Fork PR detected — doc-test will run against latest master."
else
echo "sha=${{ github.event.pull_request.head.sha || github.sha }}" >> "$GITHUB_OUTPUT"
fi
# --release-for pins the {release} placeholder for logos-dev-boost to the
# commit under test, so `github:logos-co/logos-dev-boost{release}` in the
# spec becomes `.../<sha>`. Every other repo URL still resolves to latest.
# --report writes the two-column HTML report (rendered tutorial + the
# commands actually run and their output) that publish-report deploys.
# --continue-on-fail so the run walks every step and the report is complete;
# the job still fails (non-zero exit) if any step failed.
# All scaffold specs run back-to-back into one combined report (one
# dropdown entry each): the pure C++ module, the module wrapping an
# external C library, the pure QML UI app (driven headlessly), and the
# QML UI app with a process-isolated C++ backend.
- name: Run module-scaffold doc-tests
run: |
nix run github:logos-co/logos-doctest -- run \
doctests/dev-boost-scaffold-module.test.yaml \
doctests/dev-boost-scaffold-external-lib.test.yaml \
doctests/dev-boost-scaffold-ui-qml.test.yaml \
doctests/dev-boost-scaffold-ui-qml-backend.test.yaml \
--verbose \
--continue-on-fail \
--release-for logos-dev-boost=${{ steps.commit.outputs.sha }} \
--report "${{ runner.temp }}/dev-boost-doctest-report.html"
- name: Stage report for upload
if: always()
shell: bash
run: |
mkdir -p report-out
# Name it index.html so the published directory URL renders directly.
if [ -f "${{ runner.temp }}/dev-boost-doctest-report.html" ]; then
cp "${{ runner.temp }}/dev-boost-doctest-report.html" report-out/index.html
else
echo "<h1>No report produced</h1>" > report-out/index.html
fi
- name: Upload doc-test execution report
if: always()
uses: actions/upload-artifact@v4
with:
name: dev-boost-doctest-report-${{ matrix.os }}
path: report-out/index.html
if-no-files-found: warn
- name: Verify markdown generation
run: |
for spec in dev-boost-scaffold-module dev-boost-scaffold-external-lib dev-boost-scaffold-ui-qml dev-boost-scaffold-ui-qml-backend; do
nix run github:logos-co/logos-doctest -- generate \
"doctests/$spec.test.yaml" \
--release-for logos-dev-boost=${{ steps.commit.outputs.sha }} \
-o "/tmp/$spec.md"
test -s "/tmp/$spec.md"
done
echo "Generated markdown successfully"
publish-report:
name: Publish doc-test report to GitHub Pages
needs: doctests
# Run even when the doc-test fails — a failing run is exactly when you want to
# open the report. Skip on forks, where GITHUB_TOKEN can't push or comment.
if: ${{ always() && github.event.pull_request.head.repo.fork != true }}
runs-on: ubuntu-latest
permissions:
contents: write # push to the gh-pages branch
pull-requests: write # post/update the PR comment
# Serialize Pages pushes so two refs can't race on the gh-pages branch.
concurrency:
group: gh-pages-publish
cancel-in-progress: false
steps:
- name: Download all reports
uses: actions/download-artifact@v4
with:
path: artifacts
# No `name:` → downloads every artifact into artifacts/<name>/...
- name: Arrange site directory
id: arrange
shell: bash
run: |
set -euo pipefail
if [ "${{ github.event_name }}" = "pull_request" ]; then
BASE="pr-${{ github.event.pull_request.number }}"
else
BASE="main"
fi
echo "base=$BASE" >> "$GITHUB_OUTPUT"
mkdir -p "site/$BASE"
found=""
for os in ubuntu-latest macos-latest; do
src="artifacts/dev-boost-doctest-report-$os/index.html"
if [ -f "$src" ]; then
mkdir -p "site/$BASE/$os"
cp "$src" "site/$BASE/$os/index.html"
found="$found $os"
fi
done
echo "found=$found" >> "$GITHUB_OUTPUT"
# Landing page for this ref linking to each OS report.
{
echo "<!doctype html><meta charset=utf-8>"
echo "<title>dev-boost doc-test reports — $BASE</title>"
echo "<style>body{font:16px system-ui;margin:40px;max-width:640px}a{color:#2563eb}</style>"
echo "<h1>dev-boost doc-test reports</h1>"
echo "<p><strong>$BASE</strong> · commit <code>${GITHUB_SHA::7}</code></p><ul>"
for os in ubuntu-latest macos-latest; do
if [ -d "site/$BASE/$os" ]; then
echo "<li><a href=\"./$os/\">$os</a></li>"
fi
done
echo "</ul>"
} > "site/$BASE/index.html"
- name: Deploy to gh-pages
if: steps.arrange.outputs.found != ''
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./site
keep_files: true # don't wipe other PRs' directories
commit_message: "Publish dev-boost doc-test report for ${{ steps.arrange.outputs.base }} (${{ github.sha }})"
- name: Comment on PR with report links
if: ${{ github.event_name == 'pull_request' && steps.arrange.outputs.found != '' }}
uses: actions/github-script@v7
with:
script: |
const base = "${{ steps.arrange.outputs.base }}";
const owner = context.repo.owner;
const repo = context.repo.repo;
const root = `https://${owner}.github.io/${repo}/${base}`;
const oses = "${{ steps.arrange.outputs.found }}".trim().split(/\s+/).filter(Boolean);
const links = oses.map(os => `- [\`${os}\` report](${root}/${os}/)`).join("\n");
const marker = "<!-- dev-boost-doctest-report-links -->";
const body =
`${marker}\n` +
`### 📊 dev-boost doc-test report\n\n` +
`The \`crypto_utils\` module scaffolded from this commit, built and run ` +
`end-to-end — rendered alongside the commands actually run and their ` +
`output (updated each run, commit \`${context.sha.slice(0,7)}\`):\n\n` +
`${links}\n\n` +
`_Pages can take a minute to update after the run finishes._`;
const { data: comments } = await github.rest.issues.listComments({
owner, repo, issue_number: context.issue.number, per_page: 100,
});
const existing = comments.find(c => c.body && c.body.includes(marker));
if (existing) {
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body });
} else {
await github.rest.issues.createComment({ owner, repo, issue_number: context.issue.number, body });
}