From 5a71ed341d3322d9f0ae8c6c9b314e43c6b5dfdd Mon Sep 17 00:00:00 2001 From: Tomtastisch <82227609+tomtastisch@users.noreply.github.com> Date: Sat, 14 Feb 2026 15:13:47 +0100 Subject: [PATCH 1/2] docs(governance): add tag-ruleset policy (stable+rc) Document GitHub tag rulesets for stable and RC tags, including patterns, enforcement, and expected behavior. Signed-off-by: Tomtastisch <82227609+tomtastisch@users.noreply.github.com> --- .../governance/002_POLICY_TAGGING_RULESETS.md | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 docs/governance/002_POLICY_TAGGING_RULESETS.md diff --git a/docs/governance/002_POLICY_TAGGING_RULESETS.md b/docs/governance/002_POLICY_TAGGING_RULESETS.md new file mode 100644 index 0000000..8532483 --- /dev/null +++ b/docs/governance/002_POLICY_TAGGING_RULESETS.md @@ -0,0 +1,45 @@ +# POLICY: GitHub Tag Rulesets `tags-stable` + `tags-rc` + +## Scope / Target +Tags are SSOT for publish. Patterns: +- stable: `v*` (exclude `v*-rc.*` if UI supports excludes) +- rc: `v*-rc.*` + +## Rulesets (GitHub UI) +Create rulesets at https://github.com/tomtastisch/FileClassifier/settings/rules/new?target=tag + +### Ruleset `tags-stable` +- Target: tag refs +- Pattern: + - include: `v*` + - exclude: `v*-rc.*` (only if UI supports excludes; otherwise `tags-rc` is stricter and will match rc tags too) +- Enforcement: Active (fail-closed) +- Bypass: minimal (Admins or small Maintainer team). No broad bot/app bypass unless explicitly required. +- Rules to enable (depending on UI availability): + - Restrict/Prevent deletions + - Restrict/Prevent updates / force pushes / moving tags + - (optional) Restrict creations (only if you want to limit who can create release tags) + - (optional) Require signed commits / signed tags (only if available) + - (later) Require status checks (enable only after tag workflows exist and are stable) + +### Ruleset `tags-rc` +- Target: tag refs +- Pattern: `v*-rc.*` +- Enforcement and bypass: same as `tags-stable` +- Rules: + - Restrict/Prevent deletions + - Restrict/Prevent updates / force pushes / moving tags + - (optional) Restrict creations + - (optional) Require signed commits / signed tags + - Require status checks once the rc tag workflow is stable (RC is fail-closed) + +## Expected behaviour +- Stable and RC tags can’t be moved or deleted (without bypass). +- Stable and RC tags become the auditable SSOT for releases/publish. +- Publish workflows must depend on the tag gate. + +## Ordering / rollout +1. Create rulesets (in evaluate mode only if needed, otherwise active). +2. Add `.github/workflows/tag-gate.yml`. +3. Wire publish pipelines to `needs: tag-gate` or via `workflow_run` on success. +4. Enable status checks for tags only once the checks exist and are stable. From 4c82ee6cd4692ca217002dcd5faa94f535b16fee Mon Sep 17 00:00:00 2001 From: Tomtastisch <82227609+tomtastisch@users.noreply.github.com> Date: Sat, 14 Feb 2026 15:15:55 +0100 Subject: [PATCH 2/2] Add GitHub Actions workflow fci(governance): add tag gate workflowor tag validation Signed-off-by: Tomtastisch <82227609+tomtastisch@users.noreply.github.com> --- .github/workflows/tag-gate.yml | 63 ++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 .github/workflows/tag-gate.yml diff --git a/.github/workflows/tag-gate.yml b/.github/workflows/tag-gate.yml new file mode 100644 index 0000000..7fdb7ec --- /dev/null +++ b/.github/workflows/tag-gate.yml @@ -0,0 +1,63 @@ +name: Tag Gate + +on: + push: + tags: + - 'v*' + +permissions: + contents: read + +jobs: + gate: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + fetch-depth: 0 + + - name: Setup .NET + uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 + with: + dotnet-version: | + 8.0.x + 10.0.102 + + - name: Gate - validate tag format (stable/rc) + shell: bash + run: | + set -euo pipefail + TAG="${GITHUB_REF_NAME}" + + STABLE_RE='^v[0-9]+\.[0-9]+\.[0-9]+$' + RC_RE='^v[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+$' + + if [[ "$TAG" =~ $STABLE_RE ]]; then + echo "OK: stable tag $TAG" + elif [[ "$TAG" =~ $RC_RE ]]; then + echo "OK: rc tag $TAG" + else + echo "FAIL: invalid tag format: $TAG" >&2 + exit 1 + fi + + - name: Gate - derive tag outputs (SSOT) + shell: bash + env: + RELEASE_TAG: ${{ github.ref_name }} + run: bash tools/ci/release/derive_tag_outputs.sh + + - name: Evidence + shell: bash + run: | + set -euo pipefail + mkdir -p artifacts/tag-gate + printf '%s\n' "${GITHUB_REF_NAME}" > artifacts/tag-gate/tag.txt + + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + if: always() + with: + name: tag-gate-evidence + path: artifacts/tag-gate/ + if-no-files-found: error