Repository navigation
UID2-7426: add check_jira_key composite action (commit-based Jira-key required check) - #254
Conversation
…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>
|
It fails closed (safe — no bypass), and it's a rare case, but it can block a legitimate large PR with no obvious cause. Consider paginating the commits call, or at least surfacing the 100-commit cap in the failure message so the reason is diagnosable. |
…ace the 250-commit cap in the failure message [no-jira - reason: review fix]
|
Fixed in 54e1080. The gather now paginates all pages ( |
What
Adds
actions/check_jira_key— a composite action that fails a PR unless the squash commit that will land carries aUID2-<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 apull_requestjob namedcheck_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_patternruleset (UID2-7312). IABTechLab is a GitHub team plan, where commit-metadata ruleset rules are Enterprise-gated and never evaluated — proven live (and a rulesetrequired_status_checksrule does enforce on team plan, so this Action + a required check is viable).What it checks — and the squash-config assumption
match.shreconstructs 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 workflowGITHUB_TOKENcannot readsquash_merge_commit_*(null without admin;administrationis not a grantable workflowpermissions:key — verified live). A runtime tripwire inaction.yamlreads 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 realmatch.sh.actions/check_jira_key/tests/regex_identity.test.sh— asserts the SSOT regex is byte-identical to thecommit_pr_and_mergeself-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)
commit_pr_and_mergemerges 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.uid2-android-sdk(wasPR_TITLE/PR_BODY) is normalized to match, enforced via terraform.require-squash-mergemust be active first (documented prerequisite).\btocommit_pr_and_merge's assertion; a test now asserts the two copies stay byte-identical.LC_ALL=C.UTF-8forgrep -P.Rollout prerequisites (before flipping to a REQUIRED check)
COMMIT_OR_PR_TITLE+COMMIT_MESSAGES(normalizeuid2-android-sdk; enforce via terraform).require-squash-mergeisactive.commit_pr_and_mergewaits for checks, or the release actor is bypassed on the required-check rule.Validation
test-scripts.yamlon Linux:match.test.sh11/11 pass;regex_identity.test.shbyte-identical.uid2-attestation-api: no-key PR →check_jira_keyfails;UID2-1234-in-commit PR → passes. Required-check context:check_jira_key.