502971d147
Files changed: - .gitea/workflows/ci.yml - .wikitool-kb.json - AGENTS.md - CHANGES.md - INSTALL.md - README.md - VERSION - instructions/CONTRACT.md - instructions/dev/testing-conventions.md - instructions/german-terminology.md - instructions/kb-profiles.md - instructions/migrations/3.0.0-authoring-conventions.md - instructions/private-instance.md - instructions/setup-instance.md - instructions/wiki-ingest/SKILL.md - instructions/wiki-manage/SKILL.md - kb/CONTRACT.md - kb/CONVENTIONS.md - kb/CONVENTIONS.md.template - kb/comparisons/COLLECTION.md - kb/concepts/COLLECTION.md - kb/entities/COLLECTION.md - kb/sources/COLLECTION.md - tools/CONTRACT.md - tools/README.md - tools/chemenu/commands/dist_cmd.py - tools/chemenu/commands/docs_verify.py - tools/chemenu/commands/doctor.py - tools/chemenu/commands/new_page.py - tools/chemenu/conventions.py - tools/chemenu/kb_collections.py - tools/chemenu/kb_scan.py - tools/chemenu/provenance.py - tools/chemenu/sections.py - tools/chemenu/tests/conftest.py - tools/chemenu/tests/test_conventions.py - tools/chemenu/tests/test_dist_cmd.py - tools/chemenu/tests/test_doctor.py - tools/chemenu/tests/test_new_page.py - tools/chemenu/tests/test_types_cmd.py - types/comparison.md - types/concept.md - types/entity.md - types/source.md - types/type-spec.md
247 lines
11 KiB
YAML
247 lines
11 KiB
YAML
# CI for the wiki stack.
|
|
#
|
|
# One job, stopping at the first failure - the stack has no artifact to build
|
|
# and nothing to deploy, so the pipeline's whole job is "does the machinery
|
|
# still hold together, and does the distribution it produces still work".
|
|
#
|
|
# Runner: `linux-docker` is one of this Gitea instance's three routing labels
|
|
# (alongside `container-builder` and `k3s-deploy`). The job image is named
|
|
# explicitly rather than inherited from the runner's label mapping, which is
|
|
# not documented anywhere: `debian:trixie-slim` is the base the instance's
|
|
# container-build workflows already use, and Trixie's python3 is 3.13, past
|
|
# the 3.11 floor `doctor` enforces.
|
|
#
|
|
# Pinning that image makes `nodejs` this workflow's own responsibility.
|
|
# `actions/checkout` is a JavaScript action, and act_runner runs it with `node`
|
|
# *inside the job container* - a slim Debian has none, and the job dies with
|
|
# exit 127 before any step of ours runs. The shape below (apt `nodejs` first,
|
|
# then `checkout@v7`) is the one proven on this instance by
|
|
# torben/gitea-mcp@ci-build, workflow `ci-build.yaml`, runs 42-45.
|
|
#
|
|
# Triggers: content commits are excluded. `publish` touches kb/, raw/ and work/
|
|
# and never the stack, so running the suite for them would be pure noise. The
|
|
# exclusions are deliberately literal rather than a `!**/CONTRACT.md` negation,
|
|
# whose support in Gitea's filter matching is unverified: every pattern here
|
|
# names content, so anything unanticipated still triggers CI. The list is
|
|
# repeated rather than shared through a YAML anchor for the same reason -
|
|
# GitHub's parser rejects anchors outright, and Gitea's is not documented to
|
|
# accept them. `kb/CONTRACT.md` is absent on purpose: it is a stack file that
|
|
# happens to live under a content directory, and it must keep its CI.
|
|
#
|
|
# That the filter works is now observed, not assumed (Gitea issue #11): commit
|
|
# f916376 published only kb/ and raw/ paths and produced no run at all, while
|
|
# the stack commits on either side of it (adfa220, 40adbb7) each produced two.
|
|
# Gitea evaluates these patterns the way GitHub does. Do not re-derive this.
|
|
|
|
name: CI
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths-ignore:
|
|
- 'kb/*/**'
|
|
- 'kb/index.md'
|
|
- 'kb/log.md'
|
|
- 'kb/provenance.md'
|
|
- 'raw/*/**'
|
|
- 'work/*/**'
|
|
- 'reports/*/**'
|
|
pull_request:
|
|
branches: [main]
|
|
paths-ignore:
|
|
- 'kb/*/**'
|
|
- 'kb/index.md'
|
|
- 'kb/log.md'
|
|
- 'kb/provenance.md'
|
|
- 'raw/*/**'
|
|
- 'work/*/**'
|
|
- 'reports/*/**'
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
verify:
|
|
runs-on: linux-docker
|
|
container:
|
|
image: debian:trixie-slim
|
|
env:
|
|
# Scope the Iteration Budget Gate to this run instead of letting it fall
|
|
# back to the parent PID, and keep the trace out of the checkout so the
|
|
# working tree stays clean for the ignore-rule checks.
|
|
WIKITOOL_SESSION_ID: ci-${{ github.run_id }}
|
|
WIKI_TRACE_DIR: /tmp/wikitool-trace
|
|
DIST_DIR: /tmp/dist
|
|
|
|
steps:
|
|
- name: System dependencies
|
|
# `nodejs` is not for us - it is what act_runner needs to execute the
|
|
# JavaScript action in the next step. It has to be installed before the
|
|
# checkout, which is why this step comes first.
|
|
run: |
|
|
set -eu
|
|
apt-get update -qq
|
|
apt-get install -y --no-install-recommends \
|
|
python3 python3-venv git nodejs ripgrep ca-certificates
|
|
rm -rf /var/lib/apt/lists/*
|
|
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
# The version gate diffs against the pushed range's base, so the
|
|
# shallow default clone is not enough.
|
|
fetch-depth: 0
|
|
|
|
- name: Tool environment
|
|
run: |
|
|
set -eu
|
|
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
|
python3 -m venv tools/.venv
|
|
tools/.venv/bin/pip install --quiet --upgrade pip
|
|
tools/.venv/bin/pip install --quiet -r tools/requirements.txt
|
|
# pytest-cov is CI-only: tools/requirements.txt describes what an
|
|
# *instance* needs at runtime and ships with `dist export`, and an
|
|
# instance does not measure this suite. Installed beside pytest for
|
|
# the same reason pytest itself is.
|
|
tools/.venv/bin/pip install --quiet pytest pytest-cov
|
|
# The MCP server's dependency is optional for an instance but not for
|
|
# CI: its tests skip without it, and a skipped golden test is exactly
|
|
# how the server's output and the CLI's would drift apart unnoticed.
|
|
tools/.venv/bin/pip install --quiet -r tools/requirements-mcp.txt
|
|
|
|
- name: Tests
|
|
# Not run with WIKI_TRACE=0: two telemetry tests assert that a trace is
|
|
# written, and disabling the emitter globally fails them. The suite
|
|
# redirects WIKI_TRACE_DIR per test on its own.
|
|
#
|
|
# One run, not two. This job used to be the only place the suite met a
|
|
# machine with no global git configuration, which is how Gitea #8 was
|
|
# found - two tests that silently read the developer's `git config
|
|
# user.name`. That hole is now closed in the suite itself: the autouse
|
|
# `hermetic_environment` fixture gives every test an empty HOME, no
|
|
# git configuration and none of the tool's own environment, so this
|
|
# container is no longer a special environment worth a second run.
|
|
# See instructions/dev/testing-conventions.md.
|
|
#
|
|
# Coverage is reported, not enforced: there is deliberately no
|
|
# `--cov-fail-under` yet (Gitea #10). The threshold gets set in its own
|
|
# later commit, with the measured number as its justification - one
|
|
# picked before the number is either too low to bite or too high to
|
|
# survive the next honest commit, and the second kind gets lowered
|
|
# instead of earned. Config: tools/.coveragerc.
|
|
run: |
|
|
set -eu
|
|
cd tools
|
|
.venv/bin/python -m pytest -q \
|
|
--cov --cov-report=term --cov-report=xml --cov-report=html
|
|
|
|
- name: Coverage report
|
|
# `always()`: a red suite is exactly when the per-module numbers are
|
|
# worth reading, and the upload must not disappear with the failure.
|
|
# v3, not v4 - v4 is restricted on this Gitea instance; v3 is what is
|
|
# proven here (torben/gitea-mcp@ci-build, ci-build.yaml, runs
|
|
# 42-45).
|
|
if: always()
|
|
uses: actions/upload-artifact@v3
|
|
with:
|
|
name: coverage-${{ github.run_id }}
|
|
path: |
|
|
tools/coverage.xml
|
|
tools/htmlcov/
|
|
retention-days: 14
|
|
|
|
- name: Verify the development tree
|
|
run: |
|
|
set -eu
|
|
tools/wikitool docs verify
|
|
tools/wikitool instructions verify
|
|
tools/wikitool lint --fail-on-error
|
|
|
|
- name: Version gate
|
|
# A stack change with no version bump cannot be released, because the
|
|
# release would carry a change nobody named. Scoped to what
|
|
# `dist export` actually ships as behaviour - prose docs and these
|
|
# workflows are not in it, and a typo fix should not force a bump.
|
|
env:
|
|
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
|
BEFORE_SHA: ${{ github.event.before }}
|
|
run: |
|
|
set -eu
|
|
base="${BASE_SHA:-${BEFORE_SHA:-}}"
|
|
case "$base" in
|
|
""|0000000000000000000000000000000000000000)
|
|
echo "No base commit to compare against - skipping the version gate."
|
|
exit 0
|
|
;;
|
|
esac
|
|
if ! git cat-file -e "${base}^{commit}" 2>/dev/null; then
|
|
echo "Base commit $base is not in this clone - skipping the version gate."
|
|
exit 0
|
|
fi
|
|
|
|
changed="$(git diff --name-only "$base" HEAD)"
|
|
stack="$(printf '%s\n' "$changed" \
|
|
| grep -E '^(tools/|types/|instructions/|AGENTS\.md$|[^/]+/CONTRACT\.md$)' || true)"
|
|
|
|
if [ -z "$stack" ]; then
|
|
echo "No stack paths touched - no version bump required."
|
|
exit 0
|
|
fi
|
|
if printf '%s\n' "$changed" | grep -qx 'VERSION'; then
|
|
echo "Stack changed, and VERSION moved to $(cat VERSION)."
|
|
exit 0
|
|
fi
|
|
|
|
echo "Stack paths changed without a VERSION bump:"
|
|
printf '%s\n' "$stack" | sed 's/^/ /'
|
|
echo ""
|
|
echo 'Fix: tools/wikitool version bump --patch --title "<what changed>"'
|
|
echo 'Then `docs verify` holds VERSION and CHANGES.md together.'
|
|
exit 1
|
|
|
|
- name: Export the distribution
|
|
run: tools/wikitool dist export "$DIST_DIR"
|
|
|
|
- name: The distribution works as a fresh instance
|
|
# Replays instructions/setup-instance.md end to end, minus its four
|
|
# interactive decision points. What this tests is the release artifact
|
|
# as an artifact: the documented path from an unpacked export to a
|
|
# verified instance. Running one `instructions verify` against the
|
|
# export would only have re-checked the file it just copied.
|
|
#
|
|
# Personalization is stubbed the same way the identity is: the real
|
|
# step interviews the user, so CI substitutes a fixed answer - here,
|
|
# the template minus its sentinel line. That is deliberately the
|
|
# cheapest thing `doctor`'s personalization check accepts, because
|
|
# what is under test is that the export *carries* the templates, not
|
|
# what a person would write into them.
|
|
run: |
|
|
set -eu
|
|
cd "$DIST_DIR"
|
|
git init -q -b main
|
|
git config user.name "CI Instance"
|
|
git config user.email "ci@example.invalid"
|
|
for personal in USER SOUL; do
|
|
grep -v 'wikitool:template-unfilled' "$personal.md.template" > "$personal.md"
|
|
done
|
|
# The authoring conventions ride the same split one directory down,
|
|
# and are stubbed the same way: what is under test is that the export
|
|
# carries the templates and that `doctor`/`docs verify` accept an
|
|
# adopted one, not what a person would write into them. The collection
|
|
# contracts are adopted verbatim - the shipped text is a working
|
|
# default, unlike a personalization file.
|
|
grep -v 'wikitool:template-unfilled' kb/CONVENTIONS.md.template > kb/CONVENTIONS.md
|
|
for template in kb/*/COLLECTION.md.template; do
|
|
cp "$template" "${template%.template}"
|
|
done
|
|
python3 -m venv tools/.venv
|
|
tools/.venv/bin/pip install --quiet -r tools/requirements.txt
|
|
tools/wikitool instructions sync
|
|
tools/wikitool index rebuild
|
|
tools/wikitool sources rebuild-index
|
|
tools/wikitool doctor
|
|
tools/wikitool docs verify
|
|
tools/wikitool instructions verify
|
|
tools/wikitool lint --fail-on-error
|
|
tools/wikitool version show
|
|
# A fresh instance owes no migration: dist export declares its content
|
|
# version, so `status` must answer rather than ask for a baseline.
|
|
tools/wikitool migrate status
|