Files
Dario LipicarandClaude Opus 5 dc4635d3c4 ci: use logos-co/setup-nix-cache-action for Nix setup and caching (#83)
Replaces the per-repo installer + cachix pair with the shared action, which
installs Nix with the Logos Attic cache (cache.nix.logos.co) preconfigured and
publishes what the job builds — master to the public cache, every other ref to
ci.

Each converted job also gains

    environment: ${{ github.ref == 'refs/heads/master' && 'public-cache' || '' }}

because ATTIC_TOKEN_PUBLIC only exists inside that environment. Without it the
secret resolves empty on master and publishing is silently skipped — the job
still passes, so the omission would not surface as a failure.

The action installs Nix on every runner, macOS included. Sibling repos used
DeterminateSystems' installer there because cachix/install-nix-action collided
with the runner's pre-existing _nixbld users (eDSRecordAlreadyExists); that no
longer reproduces — logos-delivery-module converted the plain way and its
macos-latest leg passes — so the second installer is not carried over.

One property is deliberately not carried over: the old cachix step ran with
`continue-on-error: true` so a failed cache push could not fail a job whose
tests passed. The action exposes no equivalent, and adding one would also
swallow genuine setup failures now that the same step installs Nix rather than
only publishing at the end.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 23:50:27 -03:00

214 lines
9.1 KiB
YAML

name: Tutorial Tests
# ──────────────────────────────────────────────────────────────────────────────
# One-time setup required for the clickable report links to work:
#
# 1. 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.)
# 2. Nothing else — GITHUB_TOKEN already has the permissions granted below.
#
# Each run publishes the two-column HTML report to:
# https://<owner>.github.io/<repo>/pr-<N>/<os>/ (pull requests)
# https://<owner>.github.io/<repo>/main/<os>/ (pushes to main/master)
# and (for PRs) posts/updates a comment with the links.
#
# Note: pull requests opened from forks get a read-only GITHUB_TOKEN, so the
# Pages push and PR comment are skipped for them — the downloadable artifact is
# still produced. PRs from branches in this repo get the full clickable links.
# ──────────────────────────────────────────────────────────────────────────────
on:
pull_request:
branches: [master, main]
push:
branches: [master, main]
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
tutorial-tests:
name: Tutorial Tests (${{ matrix.os }})
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
# ATTIC_TOKEN_PUBLIC only exists in the public-cache environment; master
# jobs must opt into it to publish to the public cache.
environment: ${{ github.ref == 'refs/heads/master' && 'public-cache' || '' }}
runs-on: ${{ matrix.os }}
timeout-minutes: 90
steps:
- name: Checkout
uses: actions/checkout@v4
# The runner is the shared `doctest` CLI, invoked directly via its flake
- name: Setup Nix and Logos cache
uses: logos-co/setup-nix-cache-action@v1
with:
attic-token-ci: ${{ secrets.ATTIC_TOKEN_CI }}
attic-token-public: ${{ secrets.ATTIC_TOKEN_PUBLIC }}
# (github:logos-co/logos-doctest). The flake bundles Python + PyYAML
# (+ rich), so no pip install step is needed.
- name: Test - UI chain + Composing Modules + Dependency Interfaces (C Library tutorial pulled in via requires)
run: |
# Three leaves, run back-to-back into one report:
# - tutorial-cpp-ui-app: the Part 1 -> 2 -> 3 chain (requires:)
# - tutorial-composing-modules: the calc_aggregator core module
# (requires Part 1 only)
# - tutorial-interface-dependencies: the calc_via_interface module
# binding a calculator interface at runtime (requires Part 1 only)
# --continue-on-fail so the run walks every step and the published
# report is complete. The job still fails (non-zero exit) if any step
# failed; this only changes whether we stop early.
nix run github:logos-co/logos-doctest -- run \
tests/tutorial-cpp-ui-app.test.yaml \
tests/tutorial-composing-modules.test.yaml \
tests/tutorial-interface-dependencies.test.yaml \
--verbose \
--continue-on-fail \
--report "${{ runner.temp }}/tutorial-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 }}/tutorial-report.html" ]; then
cp "${{ runner.temp }}/tutorial-report.html" report-out/index.html
else
echo "<h1>No report produced</h1>" > report-out/index.html
fi
- name: Upload tutorial execution report
if: always()
uses: actions/upload-artifact@v4
with:
name: tutorial-report-${{ matrix.os }}
path: report-out/index.html
if-no-files-found: warn
- name: Verify markdown generation
run: |
nix run github:logos-co/logos-doctest -- generate \
tests/tutorial-wrapping-c-library.test.yaml \
-o /tmp/gen-part1.md
nix run github:logos-co/logos-doctest -- generate \
tests/tutorial-qml-ui-app.test.yaml \
-o /tmp/gen-part2.md
nix run github:logos-co/logos-doctest -- generate \
tests/tutorial-composing-modules.test.yaml \
-o /tmp/gen-composing.md
nix run github:logos-co/logos-doctest -- generate \
tests/tutorial-interface-dependencies.test.yaml \
-o /tmp/gen-interface-deps.md
echo "Generated markdown successfully"
publish-report:
name: Publish report to GitHub Pages
needs: tutorial-tests
# Run even when tests fail — 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/tutorial-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>Tutorial reports — $BASE</title>"
echo "<style>body{font:16px system-ui;margin:40px;max-width:640px}a{color:#2563eb}</style>"
echo "<h1>Tutorial execution 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 tutorial 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 = "<!-- tutorial-report-links -->";
const body =
`${marker}\n` +
`### 📊 Tutorial execution report\n\n` +
`Rendered tutorial 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 });
}