Skip to content

Hub Self-Check

Hub Self-Check #124

# ATRIUM hub — the hub checking itself (issue #18, T7).
#
# WHY THIS EXISTS. This repository is the source of truth for 42 caller jobs
# across six repos, and for the canonical modules para-drift.reusable.yml forces
# byte-identical into all five tool repos. Until 2026-07-31 it had no caller
# workflows at all — only `workflow_call` reusables plus the E2E and issue-log
# jobs — so nothing in CI parsed, linted or policy-checked any of it.
#
# The cost was not theoretical: two files were silently overwritten with copies
# of other files, a SHA-pinning pass missed one of eleven occurrences, and
# test_para_licenses.py sat here with 36 passing tests that no workflow invoked.
#
# Both jobs read only, need no secrets, and finish in well under a minute.
name: Hub Self-Check
on:
push:
branches: [main, test]
pull_request:
branches: [main, test]
# Weekly, because the pin check resolves real tags: an action's tag can be
# force-pushed while nothing in this repo changes. Staggered clear of CodeQL
# (5:15) and pre-commit (5:30).
schedule:
- cron: "45 5 * * 1"
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
# `github.event_name` keeps a push from cancelling the scheduled run: this group was
# keyed by ref alone, so both landed in ONE group and the push won -- and a cancelled
# nightly reports no failure rather than a result. (issue atrium-project#10)
cancel-in-progress: true
jobs:
# Calls the reusable by LOCAL path rather than @v1 so the self-check validates
# the ref being pushed. A repo whose self-check is pinned to its own released
# tag can never catch a broken reusable before that tag moves.
workflow-lint:
permissions:
contents: read
uses: ./.github/workflows/workflow-lint.reusable.yml
with:
# Issue #10, G6: the local path above only picks the ref for the reusable's
# LOGIC. The reusable then clones the hub a second time at `hub-ref` and runs
# `python hub-repo/tools/ci/workflow_lint.py` from THAT clone — so with the
# default (`v1`) a PR editing the linter was checked by whatever `v1` already
# pointed at, never by its own edited code. github.sha is the ref under test,
# which is the only value that closes the loop.
hub-ref: ${{ github.sha }}
shared-tests:
name: Test canonical shared modules
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: "3.11"
# jsonschema is NOT optional here despite being an optional import in
# atrium_document.py: test_document_originators.py ends by calling
# validate_document() on a fully-built record (issue #10, D9), and without
# jsonschema that call raises its own fail-loud RuntimeError. Installing
# pytest alone would turn the newly promoted Layer D coverage into a red
# job — or, worse, into a job that only exercises the degraded branch.
#
# PyYAML is equally non-optional, and for a sharper reason: `tests/` now holds
# test_workflow_lint.py, which imports tools/ci/workflow_lint.py, which imports
# yaml at MODULE level. A missing import there is a pytest COLLECTION error, and
# a collection error aborts the ENTIRE session — so the 143 tests that had
# nothing to do with it never ran either. Landed red on 2026-08-06 (run
# 31115978804) for exactly this.
- name: Install pytest, jsonschema and PyYAML
run: pip install pytest jsonschema PyYAML
# tests/ holds the 16 check_version.py tests — the release gate for all
# five tool repos, which had no tests anywhere in the ecosystem until #18.
#
# docs/templates/shared/ is run separately and from inside the directory,
# because test_para_licenses.py imports its module as a plain
# `from para_licenses import ...` and relies on pytest putting the test
# file's own directory on sys.path — the same way it resolves once
# vendored into a tool repo, where pytest.ini sets `pythonpath = .`.
#
# Those 36 tests had been in this repo the whole time, passing, and
# invoked by nothing.
#
# Since issue #10 (D9) that directory also holds
# test_document_originators.py, so this step is now what stops a
# regression in _assert_origin_consistent() passing Hub Self-Check and
# propagating to five repos on the next `v1` move — the hole G6 named.
- name: Run release-gate tests
run: python -m pytest tests/ -v
- name: Run canonical shared-module tests
working-directory: docs/templates/shared
run: python -m pytest . -v