feat: implement data contract 1.6 — project-scoped programme and hone… #18
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| # Feature branches too, not only main. The branch prefixes are the ones | |
| # AGENTS.md already prescribes. Running the pipeline solely on main would | |
| # mean a cross-platform determinism failure is discovered at merge time, by | |
| # which point the work is finished and the diagnosis is expensive — which is | |
| # the same shape of mistake as never running the pipeline in CI at all. | |
| push: | |
| branches: | |
| - main | |
| - "feat/**" | |
| - "fix/**" | |
| - "docs/**" | |
| - "chore/**" | |
| pull_request: | |
| branches: [main] | |
| permissions: | |
| contents: read | |
| jobs: | |
| validate: | |
| # Both platforms, because the determinism this project claims is a | |
| # cross-platform claim and has never been tested as one. `dc351c9` fixed CSV | |
| # line endings that differed by platform and was never regressed on Windows; | |
| # the published run_id turned out to be reproducible only under a CRLF | |
| # checkout. A single-OS pipeline found neither. | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest] | |
| runs-on: ${{ matrix.os }} | |
| defaults: | |
| run: | |
| shell: bash | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v6 | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.14" | |
| cache: pip | |
| cache-dependency-path: | | |
| requirements.txt | |
| requirements-dev.txt | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| python -m pip install -r requirements.txt -r requirements-dev.txt | |
| # An IDS document is currently read only by the library that wrote it, | |
| # which proves the two agree and nothing else. `ids-tool` is a separate | |
| # buildingSMART implementation, in another language, and it reads the | |
| # document against the schema and the standard's content rules. The | |
| # conformance corpus showed what that catches: three of the five cases | |
| # where this project disagrees with the standard are documents whose value | |
| # literal contradicts its declared data type, and IfcTester executes them | |
| # and reports a pass. `ids-tool` rejects all three. | |
| # | |
| # Called as an external process at a pinned version. No code is copied. | |
| # The tool targets net8.0, so the SDK is pinned to match rather than | |
| # relying on roll-forward. | |
| - name: Set up .NET for the IDS audit tool | |
| uses: actions/setup-dotnet@v6 | |
| with: | |
| dotnet-version: "8.0.x" | |
| - name: Install the IDS audit tool | |
| run: dotnet tool install --global ids-tool.CommandLine --version 1.0.124 | |
| # Checked before anything runs, because every comparison below depends on | |
| # it. Under data contract 0.1 the published run_id is a SHA-256 over the | |
| # IDS document's raw bytes; without this pin the file checks out with | |
| # platform line endings, so a Linux runner computes a different run_id and | |
| # silently re-keys all forty-seven findings. | |
| - name: Verify the IDS byte pin survives checkout | |
| run: | | |
| if ! grep -qF 'ids/*.ids -text' .gitattributes; then | |
| echo "::error::.gitattributes has lost 'ids/*.ids -text'." | |
| echo "The published run_id is not reproducible across platforms without it." | |
| exit 1 | |
| fi | |
| - name: Lint | |
| run: python -m ruff check . | |
| # EPC_REQUIRE_IDS_AUDIT turns the IDS audit from a test that skips when | |
| # its tool is absent into one that fails. Locally, skipping is right — | |
| # not every contributor has a .NET SDK. Here the tool was just installed, | |
| # so a skip would mean the install silently failed and the gate had | |
| # quietly stopped running, which is the failure mode a gate must not have. | |
| - name: Run regression suite | |
| env: | |
| EPC_REQUIRE_IDS_AUDIT: "1" | |
| run: python -m pytest -p no:cacheprovider tests -q | |
| # The pipeline had never been run by continuous integration at all. Every | |
| # artifact in this repository was produced by hand on one machine, which is | |
| # how a wall-clock timestamp and a set iteration order stayed inside | |
| # published reports for an entire release without anybody noticing. | |
| - name: Regenerate every published artifact | |
| run: python -m epc_control_tower.cli run | |
| - name: Fail if anything the pipeline produces has changed | |
| run: git diff --exit-code | |
| - name: Fail if the pipeline produced anything untracked | |
| run: | | |
| untracked="$(git status --porcelain --untracked-files=all)" | |
| if [ -n "$untracked" ]; then | |
| echo "::error::The pipeline left the working tree dirty." | |
| echo "$untracked" | |
| exit 1 | |
| fi | |
| # Byte equality against the recorded contract snapshot. Moving it requires | |
| # a CHANGELOG entry and an explicit acknowledgement; see | |
| # `epc-ct snapshot --help`. | |
| - name: Check the published contract against its snapshot | |
| run: python -m epc_control_tower.cli snapshot | |
| - name: Validate dashboard core contract | |
| run: python src/validate_dashboard.py --mode core | |
| - name: Validate tracked Power BI project | |
| run: python src/validate_pbip.py | |
| - name: Check dependency consistency | |
| run: python -m pip check |