Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 18 additions & 35 deletions .github/workflows/cetlvast.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ permissions:

concurrency:
group: cetlvast-${{ github.ref }}
cancel-in-progress: false
# A newer commit supersedes a pull request run. Runs on main are never cancelled: the release waits for them.
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

on:
push: # Further filtering is done in the jobs.
push:
# Tags are excluded: a release reuses the main run of its commit. Further filtering is done in the jobs.
branches: ["**"]
pull_request:
branches:
- main
Expand Down Expand Up @@ -47,16 +50,12 @@ jobs:

cache-warmup:
if: >
startsWith(github.ref, 'refs/tags/v') ||
github.event_name == 'pull_request' ||
github.event_name == 'workflow_dispatch' ||
contains(github.event.head_commit.message, '#verification') ||
contains(github.event.head_commit.message, '#docs') ||
contains(github.ref, '/main') ||
contains(github.ref, '/issue/') ||
(github.event_name == 'pull_request')
github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
container: ghcr.io/opencyphal/toolshed:ts26.4.5
needs: [version]
needs: [version, release-tests]
steps:
- uses: actions/checkout@v7
with:
Expand All @@ -74,12 +73,9 @@ jobs:

verification-amd64:
if: >
startsWith(github.ref, 'refs/tags/v') ||
github.event_name == 'pull_request' ||
github.event_name == 'workflow_dispatch' ||
contains(github.event.head_commit.message, '#verification') ||
contains(github.ref, '/main') ||
contains(github.ref, '/issue/') ||
(github.event_name == 'pull_request')
github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
container: ghcr.io/opencyphal/toolshed:ts26.4.5
needs:
Expand Down Expand Up @@ -117,12 +113,9 @@ jobs:

verification-arm64:
if: >
startsWith(github.ref, 'refs/tags/v') ||
github.event_name == 'pull_request' ||
github.event_name == 'workflow_dispatch' ||
contains(github.event.head_commit.message, '#verification') ||
contains(github.ref, '/main') ||
contains(github.ref, '/issue/') ||
(github.event_name == 'pull_request')
github.ref == 'refs/heads/main'
runs-on: ubuntu-24.04-arm
container: ghcr.io/opencyphal/toolshed:ts26.4.5
needs:
Expand Down Expand Up @@ -161,12 +154,9 @@ jobs:
# Compilers the current toolshed image no longer ships run here, in the last image that has them.
verification-legacy-arm64:
if: >
startsWith(github.ref, 'refs/tags/v') ||
github.event_name == 'pull_request' ||
github.event_name == 'workflow_dispatch' ||
contains(github.event.head_commit.message, '#verification') ||
contains(github.ref, '/main') ||
contains(github.ref, '/issue/') ||
(github.event_name == 'pull_request')
github.ref == 'refs/heads/main'
runs-on: ubuntu-24.04-arm
# ts26.4.x dropped g++-7. Keep this pinned to ts24.4.3 rather than following the other jobs.
container: ghcr.io/opencyphal/toolshed:ts24.4.3
Expand Down Expand Up @@ -206,13 +196,9 @@ jobs:

docs:
if: >
startsWith(github.ref, 'refs/tags/v') ||
github.event_name == 'pull_request' ||
github.event_name == 'workflow_dispatch' ||
contains(github.event.head_commit.message, '#verification') ||
contains(github.event.head_commit.message, '#docs') ||
contains(github.ref, '/main') ||
contains(github.ref, '/issue/') ||
(github.event_name == 'pull_request')
github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
container: ghcr.io/opencyphal/toolshed:ts26.4.5
needs:
Expand Down Expand Up @@ -254,12 +240,9 @@ jobs:
release-ready:
if: >-
always() && (
startsWith(github.ref, 'refs/tags/v') ||
github.event_name == 'pull_request' ||
github.event_name == 'workflow_dispatch' ||
contains(github.event.head_commit.message, '#verification') ||
contains(github.ref, '/main') ||
contains(github.ref, '/issue/') ||
github.event_name == 'pull_request')
github.ref == 'refs/heads/main')
runs-on: ubuntu-latest
needs: [version, release-tests, cache-warmup, verification-amd64, verification-arm64, verification-legacy-arm64, docs]
steps:
Expand Down
23 changes: 10 additions & 13 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,23 +164,20 @@ invoke Clang-Format of the correct version from the container (be sure to use th
docker run --rm -v ${PWD}:/repo ghcr.io/opencyphal/toolshed:ts26.4.5 /bin/sh -c 'cd cetlvast && cmake --preset configure-clang-native-cpp-17-offline && cd build && ninja danger-danger-cetlvast-clang-format-in-place'
```

# `issue/*` and hashtag-based CI triggering
# CI triggering

Normally, the CI will only run on pull requests (PR), releases, and perhaps some other special occasions on `main` branch.
Often, however, you will want to run it on your branch before proposing the changes to ensure all checks are
green and test coverage is adequate - to do that:
Every push to a branch runs version/header validation and the release-tool tests. Full verification, including
documentation and release archive checks, runs for pull requests (PR) into `main` or an `issue/*` branch, for pushes
to `main`, and for manual dispatches. A PR in a GitHub stack gets full verification as if it targeted the base of
the stack. To verify a branch before proposing the changes to `main`:
- either target your PR to any `issue/NN_LABEL` branch, where `NN` is the issue number and `LABEL` is a small title giving context (like `issue/83_any`)
- or add a hashtag with the name of the workflow you need to run to the head commit;
for example, making a commit with a message like `Add feature such and such #verification #docs`
will force the CI to execute jobs named `verification` and `docs`.
- or, with write access, dispatch the workflow on the branch: `gh workflow run cetlvast.yml --ref <branch>`.

Note that if the job you requested is dependent on other jobs that are not triggered, it will not run.
Do not dispatch for a commit that a PR run already verifies; each full run is expensive.

Version/header validation and release-tool tests run independently of hashtags.
The `#verification` trigger includes documentation and release archive checks.
Version tags and verification dispatches run full verification. Documentation
deployment verifies the published bundle and its successful main CI run, then
deploys the packaged documentation.
A new commit cancels the running verification of the PR it is pushed to. Runs on `main` are never cancelled.
A release reuses the `main` run of the tagged commit; pushing the version tag starts no CI. Documentation
deployment verifies the published bundle and its successful main CI run, then deploys the packaged documentation.
See [Releasing CETL](RELEASING.md) for the maintainer Python CLI and recovery instructions.
The CMake `release` target is a legacy build/test alias and does not publish a release.

Expand Down
11 changes: 9 additions & 2 deletions scripts/tests/test_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,6 @@ def test_matrix_and_gate_contract_matches_workflow(self):
expected = {f"verification-{architecture} ({compiler}, {standard})"
for compiler in compilers for standard in standards}
self.assertEqual(expected, {name for name in required_jobs() if name.startswith(f"verification-{architecture} (")})
self.assertIn("startsWith(github.ref, 'refs/tags/v')", section)
self.assertNotIn("!inputs.docs_ref", section)
gate = _workflow_group(r"^ release-ready:\n(.*?)(?=^ [a-z])", workflow, re.M | re.S)
dependencies = _workflow_group(r"needs: \[(.*)\]", gate).split(", ")
Expand All @@ -162,7 +161,7 @@ def test_full_release_gate_triggers_cover_all_dependencies(self):
gate = _workflow_group(r"^ release-ready:\n(.*?)(?=^ [a-z])", workflow, re.M | re.S)
condition_pattern = r"^ if: >-?\n(.*?)(?=^ \S)"
# Each predicate is an OR term; always() lets the gate inspect failed dependencies.
predicate_pattern = r"(?:startsWith|contains)\([^()\n]*\)|github\.event_name == '[^']+'"
predicate_pattern = r"(?:startsWith|contains)\([^()\n]*\)|github\.(?:event_name|ref) == '[^']+'"
condition = _workflow_group(condition_pattern, gate, re.M | re.S)
gate_triggers = set(re.findall(predicate_pattern, condition))
self.assertTrue(gate_triggers)
Expand All @@ -177,6 +176,14 @@ def test_full_release_gate_triggers_cover_all_dependencies(self):
missing = gate_triggers - set(re.findall(predicate_pattern, condition))
self.assertFalse(missing, f"{dependency} skips full-gate triggers: {sorted(missing)}")

def test_version_tags_reuse_the_main_run(self):
"""Leave version tags out of CETLVaSt; the release reuses the tagged commit's main push run."""
workflow = (SCRIPTS.parent / ".github/workflows/cetlvast.yml").read_text()
push = _workflow_group(r"^ push:\n(.*?)(?=^ [a-z])", workflow, re.M | re.S)
# A push filter with only branches never matches a tag.
self.assertEqual(re.findall(r"^ ([a-z-]+):", push, re.M), ["branches"])
self.assertNotIn("refs/tags", workflow)


class GitHubTransportTests(unittest.TestCase):
"""Check CLI/API encoding, local prerequisites, diagnostics, and public HTML reads."""
Expand Down
Loading