Skip to content

UID2-7426: add check_jira_key composite action (commit-based Jira-key required check)#254

Open
jon8787 wants to merge 5 commits into
mainfrom
jon-UID2-7426-check-jira-key
Open

UID2-7426: add check_jira_key composite action (commit-based Jira-key required check)#254
jon8787 wants to merge 5 commits into
mainfrom
jon-UID2-7426-check-jira-key

Conversation

@jon8787

@jon8787 jon8787 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

What

Adds actions/check_jira_key — a composite action that fails a PR unless the squash commit that will land carries a UID2-<n> Jira key or a reasoned [no-jira - reason: <reason>] opt-out. This is the required-status-check enforcement path for IABTechLab (UID2-7426). Callers wire it as a pull_request job named check_jira_key.

(Originally drafted as a reusable workflow; converted to a composite action during review so the shipped logic lives in a unit-tested script rather than un-guarded inline bash.)

Why an Action instead of the native ruleset

UnifiedID2 / EUID (Enterprise) enforce this natively via a commit_message_pattern ruleset (UID2-7312). IABTechLab is a GitHub team plan, where commit-metadata ruleset rules are Enterprise-gated and never evaluated — proven live (and a ruleset required_status_checks rule does enforce on team plan, so this Action + a required check is viable).

What it checks — and the squash-config assumption

match.sh reconstructs the squash commit GitHub will land: subject = the single commit's first line (1-commit PR) or the PR title (multi-commit); body = all commit messages. It requires the key/opt-out there (checking the PR title alone is wrong — a single-commit PR lands the commit message, and the CI marker lands in the commit).

It assumes squash_merge_commit_title = COMMIT_OR_PR_TITLE + squash_merge_commit_message = COMMIT_MESSAGES (the config every gated repo uses) rather than reading it, because the workflow GITHUB_TOKEN cannot read squash_merge_commit_* (null without admin; administration is not a grantable workflow permissions: key — verified live). A runtime tripwire in action.yaml reads the settings anyway and fails loudly only if they are readable and differ from the assumption (null = unreadable = trust the enforcement) — so a UI settings-flip becomes an immediate red check at zero cost on the normal path.

Structure

  • actions/check_jira_key/action.yaml — gathers inputs (live PR title + commits) + the tripwire, calls the matcher.
  • actions/check_jira_key/match.sh — pure matcher (inputs via env, no I/O), holds the SSOT regex.
  • actions/check_jira_key/tests/match.test.sh — 11 cases against the real match.sh.
  • actions/check_jira_key/tests/regex_identity.test.sh — asserts the SSOT regex is byte-identical to the commit_pr_and_merge self-assertion (reads the real files → can't drift).
  • .gitattributes*.sh eol=lf (shebangs on Linux runners).

Both tests are auto-run by the existing test-scripts.yaml (no new CI wiring).

Review response (external review)

  • F1 (CI merge 405s once required)commit_pr_and_merge merges via an immediate PUT with no wait-for-checks. Documented as a required-flip prerequisite (add wait-for-checks, or bypass the release actor). Earlier "no CI change" claim corrected.
  • F2 (squash settings unreadable → silent default) — the token genuinely can't read the fields (verified). Pivoted to assuming the uniform config + a runtime tripwire; uid2-android-sdk (was PR_TITLE/PR_BODY) is normalized to match, enforced via terraform.
  • F3 — PR title fetched live (re-run safe).
  • F4require-squash-merge must be active first (documented prerequisite).
  • F5 — added \b to commit_pr_and_merge's assertion; a test now asserts the two copies stay byte-identical.
  • F6 — failure guidance branches on single- vs multi-commit.
  • F7 — squash subject uses the commit's first line.
  • F8LC_ALL=C.UTF-8 for grep -P.
  • Tests — the logic is now a unit-tested script (composite action), not a scratch mirror.

Rollout prerequisites (before flipping to a REQUIRED check)

  1. Every gated repo is COMMIT_OR_PR_TITLE + COMMIT_MESSAGES (normalize uid2-android-sdk; enforce via terraform).
  2. require-squash-merge is active.
  3. commit_pr_and_merge waits for checks, or the release actor is bypassed on the required-check rule.

Validation

  • test-scripts.yaml on Linux: match.test.sh 11/11 pass; regex_identity.test.sh byte-identical.
  • E2E on uid2-attestation-api: no-key PR → check_jira_key fails; UID2-1234-in-commit PR → passes. Required-check context: check_jira_key.

jon8787 and others added 5 commits July 6, 2026 22:14
…Jira-key gate)

IABTechLab is a GitHub team plan, where commit-metadata ruleset rules
(commit_message_pattern) are Enterprise-gated and silently inert, so the native
require-jira-key rule used on UnifiedID2/EUID (UID2-7312) does not enforce there.
This reusable workflow reproduces the same rule as a required status check, the
only team-plan-compatible mechanism.

It reconstructs the squash commit GitHub will actually land (per each repo's
squash_merge_commit_title/message) and requires a UID2-<n> key or a reasoned
[no-jira - reason: <reason>] opt-out, mirroring the native ruleset and the
commit_pr_and_merge marker (UID2-7400) so CI release PRs pass with no CI change.
Commit-based, not PR-title, because in-scope repos use COMMIT_OR_PR_TITLE, where a
single-commit PR lands the commit message rather than the PR title.

Reconstruction logic validated against 12 cases (single/multi-commit, CI marker,
android-sdk PR_TITLE/PR_BODY, blank opt-out, FOOUID2-1).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…live title/body, first-line subject, locale, conditional guidance; align SSOT regex

- Read squash settings and FAIL CLOSED (never default) when null. A token without
  administration:read gets null and would silently mis-evaluate PR_TITLE/PR_BODY repos
  (e.g. uid2-android-sdk: false-block a key-in-title PR, false-pass a key-in-commit one).
  Add administration:read to permissions; callers must grant it.
- Fetch PR title/body LIVE (not the frozen github.event payload) so a manual re-run after a
  title edit re-evaluates the current title/body.
- Use the commit's FIRST LINE (not the whole message) as the squash subject — a key only in a
  commit body does not land on BLANK / PR_BODY body modes.
- Pin LC_ALL=C.UTF-8 for grep -P (matches commit_pr_and_merge; PCRE errors on non-UTF-8 locale).
- Branch the failure guidance on squash title mode / commit count so authors aren't told to
  amend a commit when the PR title is what lands (or vice-versa).
- Document rollout prerequisites in the header: require-squash-merge must be active, and
  commit_pr_and_merge needs a wait-for-checks (or the release actor a bypass) before this is
  flipped to a required check (its immediate merge API call would otherwise 405).
- Align the SSOT: add \b to the commit_pr_and_merge self-assertion regex so the two in-repo
  copies are byte-identical (previously it lacked \b).

Reconstruction logic re-verified against 16 cases (incl. first-line/body-mode and android-sdk).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…orkflow permissions key); reword fail-closed message [no-jira - reason: review fix]
…ow token can't read squash_merge_commit_*); simplify reconstruction [no-jira - reason: review fix]
…e matcher

Replaces the shared-check-jira-key reusable workflow with actions/check_jira_key
(composite action), so the shipped logic lives in a script that is genuinely
unit-tested rather than mirrored:
- match.sh: pure matcher (inputs via env, no I/O) — the reconstruction + SSOT regex.
- action.yaml: gathers inputs (gh api) + a runtime squash-config TRIPWIRE, then calls match.sh.
- tests/match.test.sh: 11 cases against the real match.sh (auto-run by test-scripts.yaml).
- tests/regex_identity.test.sh: asserts the SSOT regex is byte-identical to the
  commit_pr_and_merge self-assertion (reads the real files, so it can't drift).

Also:
- Runtime tripwire: attempt to read squash settings; fail loudly ONLY if they are readable AND
  differ from the assumed COMMIT_OR_PR_TITLE/COMMIT_MESSAGES (null = unreadable = trust the audit).
  Turns a silent UI settings-flip into an immediate red check at zero cost on the normal path.
- Fix cosmetic success message (dropped the misleading github.ref_name "<n>/merge").
- .gitattributes: *.sh eol=lf so shebangs work on Linux runners.
- Callers pin job name `check_jira_key` -> one status-check context for the terraform required check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jon8787 jon8787 changed the title UID2-7426: add shared-check-jira-key reusable workflow (commit-based Jira-key gate) UID2-7426: add check_jira_key composite action (commit-based Jira-key required check) Jul 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant