Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1fba1e06b2 | ||
|
|
c09fc72500 |
No files matched your search
@@ -1,8 +1,8 @@
|
|||||||
# action/check-naming
|
# action/check-naming
|
||||||
|
|
||||||
Composite Gitea Action that validates a pull request's branch, title, Bug
|
Composite Gitea Action that validates a pull request's branch, title, Bug
|
||||||
tracking, PR-description AI authorship, and commit range against the fritzlab
|
tracking, and AI authorship against the fritzlab Git standard. The current
|
||||||
Git standard. The current action fails (exits 1) on any violation; `@v1` is the legacy warn-only release
|
action fails (exits 1) on any violation; `@v1` is the legacy warn-only release
|
||||||
(Bugs program: see
|
(Bugs program: see
|
||||||
[fritzlab/agenthub#557](https://code.fritzlab.net/fritzlab/agenthub/issues/557)).
|
[fritzlab/agenthub#557](https://code.fritzlab.net/fritzlab/agenthub/issues/557)).
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ Git standard. The current action fails (exits 1) on any violation; `@v1` is the
|
|||||||
```
|
```
|
||||||
Branch: <role>/bug-<id>/<kebab-description> e.g. dev/bug-x7k2m9/fix-terminal-resize
|
Branch: <role>/bug-<id>/<kebab-description> e.g. dev/bug-x7k2m9/fix-terminal-resize
|
||||||
Chore: chore/<kebab-description> bug-less trivia only (dep bumps, typos, CI tweaks)
|
Chore: chore/<kebab-description> bug-less trivia only (dep bumps, typos, CI tweaks)
|
||||||
Title: [bug-x7k2m9] fix(terminal): preserve resize state
|
Title: [bug-x7k2m9] Fix terminal resize loss (chore PRs: no [bug-id] prefix)
|
||||||
```
|
```
|
||||||
|
|
||||||
- `<role>` must be a roster handle: `dev` | `ux` | `ops` | `security` | `perf` | `architect` | `support`
|
- `<role>` must be a roster handle: `dev` | `ux` | `ops` | `security` | `perf` | `architect` | `support`
|
||||||
@@ -23,10 +23,6 @@ Title: [bug-x7k2m9] fix(terminal): preserve resize state
|
|||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize, reopened, edited]
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
naming:
|
naming:
|
||||||
runs-on: fritzlab
|
runs-on: fritzlab
|
||||||
@@ -46,11 +42,6 @@ jobs:
|
|||||||
head-sha: ${{ github.event.pull_request.head.sha }}
|
head-sha: ${{ github.event.pull_request.head.sha }}
|
||||||
```
|
```
|
||||||
|
|
||||||
The explicit `edited` activity is part of the enforcement contract: changing
|
|
||||||
the PR description must issue a new naming status for the same commit.
|
|
||||||
Keep required code-validation jobs in a separate workflow that does not run on
|
|
||||||
`edited`; Gitea treats a skipped required context as passing.
|
|
||||||
|
|
||||||
To wire a new repo warn-only first, add `continue-on-error: true` to the
|
To wire a new repo warn-only first, add `continue-on-error: true` to the
|
||||||
job — the step still fails, but the job cannot block the PR.
|
job — the step still fails, but the job cannot block the PR.
|
||||||
|
|
||||||
@@ -74,12 +65,11 @@ The check validates four things for every non-break-glass Agent PR:
|
|||||||
Unknown roles, missing `bug-` segment, uppercase bug-ids, and empty
|
Unknown roles, missing `bug-` segment, uppercase bug-ids, and empty
|
||||||
kebab descriptions all produce a `FAIL[check-naming]` log line.
|
kebab descriptions all produce a `FAIL[check-naming]` log line.
|
||||||
|
|
||||||
2. **Title form** — for a `role/bug` branch the title must be
|
2. **Title form** — for a `role/bug` branch the title must start with
|
||||||
`[bug-<id>] type(scope): description`. The type starts with a lowercase
|
`[bug-<id>] `. For a `chore` branch the title must have no `[bug-id]`
|
||||||
letter and the type and one-component scope contain only lowercase letters,
|
prefix. If both carry a bug-id they must match. The description is a
|
||||||
digits, and hyphens. An optional `!` may follow the scope, and the description
|
plain-language imperative; Conventional Commit forms such as `fix(scope):`
|
||||||
must contain a non-whitespace character. For a `chore` branch the title must
|
are rejected.
|
||||||
have no `[bug-id]` prefix. If both carry a bug-id they must match.
|
|
||||||
|
|
||||||
3. **Tracking and attribution** — on Bug-backed work, `## Tracking` contains both the literal
|
3. **Tracking and attribution** — on Bug-backed work, `## Tracking` contains both the literal
|
||||||
`Fixes bug-<id>` automation token and the matching navigable
|
`Fixes bug-<id>` automation token and the matching navigable
|
||||||
@@ -91,12 +81,10 @@ The check validates four things for every non-break-glass Agent PR:
|
|||||||
`<h2>` sections outside collapsed `<details>` content. Fenced, commented,
|
`<h2>` sections outside collapsed `<details>` content. Fenced, commented,
|
||||||
scripted, or collapsed copies do not satisfy the visible provenance contract.
|
scripted, or collapsed copies do not satisfy the visible provenance contract.
|
||||||
|
|
||||||
4. **Commit range** — `base-sha` and `head-sha` must identify available commits,
|
4. **Commit attribution** — every commit in `base-sha..head-sha`, including
|
||||||
and `base-sha..head-sha` must contain at least one PR commit.
|
commits on `chore/` branches, ends with
|
||||||
The naming job must check out full history before this action.
|
the canonical `Authored-By` trailer, separated from the message body by a
|
||||||
Attribution is required only in the PR description; commit messages are not
|
blank line. The naming job must check out full history before this action.
|
||||||
inspected. Existing commit trailers remain valid history, but no trailer is
|
|
||||||
required for a new commit.
|
|
||||||
|
|
||||||
Every violation prints a `FAIL[check-naming]: ...` line and the step
|
Every violation prints a `FAIL[check-naming]: ...` line and the step
|
||||||
exits 1. To make the check required on a repo: drop any
|
exits 1. To make the check required on a repo: drop any
|
||||||
@@ -105,7 +93,7 @@ context to the repo's `status_check_contexts` in agenthub `hub/hub.yaml`.
|
|||||||
|
|
||||||
## Versions
|
## Versions
|
||||||
|
|
||||||
- unreleased — requires Bug tracking and visible PR attribution, without commit-message watermarks.
|
- unreleased — adds Bug tracking plus PR and commit attribution enforcement.
|
||||||
- `v2` — enforces branch and title naming.
|
- `v2` — enforces branch and title naming.
|
||||||
- `v1` — legacy warn-only: logs `WARN` lines, always exits 0.
|
- `v1` — legacy warn-only: logs `WARN` lines, always exits 0.
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
name: Check PR contract
|
name: Check PR contract
|
||||||
description: |
|
description: |
|
||||||
Validates that a PR's branch, title, Bug tracking, and PR-description AI attribution
|
Validates that a PR's branch, title, Bug tracking, and AI attribution
|
||||||
follow the fritzlab Git standard. Fails (exits 1) on any violation, with
|
follow the fritzlab Git standard. Fails (exits 1) on any violation, with
|
||||||
FAIL log lines naming it. Warn-only wiring is the consumer's choice: set
|
FAIL log lines naming it. Warn-only wiring is the consumer's choice: set
|
||||||
continue-on-error: true on the job (or pin @v1, which never fails).
|
continue-on-error: true on the job (or pin @v1, which never fails).
|
||||||
@@ -8,7 +8,7 @@ description: |
|
|||||||
Standard:
|
Standard:
|
||||||
branch: <role>/bug-<id>/<kebab> e.g. dev/bug-x7k2m9/fix-terminal-resize
|
branch: <role>/bug-<id>/<kebab> e.g. dev/bug-x7k2m9/fix-terminal-resize
|
||||||
chore: chore/<kebab> bug-less trivia only
|
chore: chore/<kebab> bug-less trivia only
|
||||||
title: [bug-<id>] type(scope): description
|
title: [bug-<id>] Description (chore PRs: no [bug-id] prefix)
|
||||||
|
|
||||||
Role must be a roster handle: dev | ux | ops | security | perf | architect | support
|
Role must be a roster handle: dev | ux | ops | security | perf | architect | support
|
||||||
Break-glass: PRs authored by dfritz are exempt.
|
Break-glass: PRs authored by dfritz are exempt.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Branch, PR body, and commit-range contract checker.
|
# Branch, PR body, and commit contract checker.
|
||||||
# v2: exits 1 on violation. Consumers that want warn-only set
|
# v2: exits 1 on violation. Consumers that want warn-only set
|
||||||
# continue-on-error: true on the job (or stay on @v1).
|
# continue-on-error: true on the job (or stay on @v1).
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@@ -121,18 +121,21 @@ if echo "${TITLE}" | grep -qE "^\[bug-[a-z0-9]+\] ."; then
|
|||||||
TITLE_BUG=$(echo "${TITLE}" | sed -E 's|^\[(bug-[a-z0-9]+)\].*|\1|')
|
TITLE_BUG=$(echo "${TITLE}" | sed -E 's|^\[(bug-[a-z0-9]+)\].*|\1|')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
TITLE_DESCRIPTION=$(printf '%s\n' "${TITLE}" | sed -E 's/^\[bug-[a-z0-9]+\][[:space:]]+//')
|
||||||
|
if printf '%s\n' "${TITLE_DESCRIPTION}" | grep -qE '^[[:alnum:]-]+(\([^)]*\))?!?:[[:space:]]'; then
|
||||||
|
echo "FAIL[check-naming]: title must use a plain-language imperative without Conventional Commit syntax"
|
||||||
|
FAILED=1
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "${BRANCH_KIND}" = "role-bug" ]; then
|
if [ "${BRANCH_KIND}" = "role-bug" ]; then
|
||||||
if [ -z "${TITLE_BUG}" ]; then
|
if [ -z "${TITLE_BUG}" ]; then
|
||||||
echo "FAIL[check-naming]: title must lead with [${BRANCH_BUG}]"
|
echo "FAIL[check-naming]: title must lead with [${BRANCH_BUG}]"
|
||||||
echo " have: ${TITLE}"
|
echo " have: ${TITLE}"
|
||||||
echo " want: [${BRANCH_BUG}] fix(scope): description"
|
echo " want: [${BRANCH_BUG}] ${TITLE}"
|
||||||
FAILED=1
|
FAILED=1
|
||||||
elif [ "${TITLE_BUG}" != "${BRANCH_BUG}" ]; then
|
elif [ "${TITLE_BUG}" != "${BRANCH_BUG}" ]; then
|
||||||
echo "FAIL[check-naming]: bug-id mismatch — branch carries '${BRANCH_BUG}' but title carries '${TITLE_BUG}'"
|
echo "FAIL[check-naming]: bug-id mismatch — branch carries '${BRANCH_BUG}' but title carries '${TITLE_BUG}'"
|
||||||
FAILED=1
|
FAILED=1
|
||||||
elif ! printf '%s\n' "${TITLE}" | grep -qE "^\\[${BRANCH_BUG}\\] [a-z][a-z0-9-]*\\([a-z0-9][a-z0-9-]*\\)!?: .*[^[:space:]]$"; then
|
|
||||||
echo "FAIL[check-naming]: Bug-backed title must use '[${BRANCH_BUG}] type(scope): description'"
|
|
||||||
FAILED=1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
tracking=$(printf '%s\n' "${VISIBLE_BODY}" | awk '
|
tracking=$(printf '%s\n' "${VISIBLE_BODY}" | awk '
|
||||||
@@ -180,9 +183,23 @@ if [ "${BRANCH_KIND}" != "invalid" ]; then
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
if git cat-file -e "${BASE}^{commit}" 2>/dev/null && git cat-file -e "${HEAD}^{commit}" 2>/dev/null; then
|
if git cat-file -e "${BASE}^{commit}" 2>/dev/null && git cat-file -e "${HEAD}^{commit}" 2>/dev/null; then
|
||||||
# Attribution belongs only in the PR description, not commit messages.
|
commit_count=0
|
||||||
# Keep the existing nonempty-range gate independently of message content.
|
while IFS= read -r commit; do
|
||||||
commit_count=$(git rev-list --count "${BASE}..${HEAD}" 2>/dev/null) || commit_count=0
|
[ -n "${commit}" ] || continue
|
||||||
|
commit_count=$((commit_count + 1))
|
||||||
|
message=$(git log -1 --format=%B "${commit}")
|
||||||
|
if ! printf '%s\n' "${message}" | awk '
|
||||||
|
{ line[NR]=$0 }
|
||||||
|
END {
|
||||||
|
n=NR
|
||||||
|
while (n > 0 && line[n] == "") n--
|
||||||
|
if (n < 3 || line[n-1] != "" || line[n] !~ /^Authored-By: .+ \(.+\) <noreply@[[:alnum:].-]+>$/) exit 1
|
||||||
|
}
|
||||||
|
'; then
|
||||||
|
echo "FAIL[check-naming]: commit ${commit} must end with an Authored-By product/model trailer"
|
||||||
|
FAILED=1
|
||||||
|
fi
|
||||||
|
done < <(git rev-list --reverse "${BASE}..${HEAD}" 2>/dev/null)
|
||||||
if [ "${commit_count}" -eq 0 ]; then
|
if [ "${commit_count}" -eq 0 ]; then
|
||||||
echo "FAIL[check-naming]: base-sha..head-sha contains no PR commits"
|
echo "FAIL[check-naming]: base-sha..head-sha contains no PR commits"
|
||||||
FAILED=1
|
FAILED=1
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Tests for check.sh — exercises naming, PR attribution, and commit ranges.
|
# Tests for check.sh — exercises valid/invalid branch+title matrices.
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT="$(cd "$(dirname "$0")/.." && pwd)/check.sh"
|
SCRIPT="$(cd "$(dirname "$0")/.." && pwd)/check.sh"
|
||||||
@@ -48,7 +48,7 @@ check_contract() {
|
|||||||
local desc="$1" body="$2" base="$3" head="$4" diagnostic="$5"
|
local desc="$1" body="$2" base="$3" head="$4" diagnostic="$5"
|
||||||
local out rc=0
|
local out rc=0
|
||||||
out=$(cd "${FIXTURES}" && HEAD_BRANCH="architect/bug-x7k2m9/contract" \
|
out=$(cd "${FIXTURES}" && HEAD_BRANCH="architect/bug-x7k2m9/contract" \
|
||||||
PR_TITLE="[bug-x7k2m9] fix(check): enforce contract" PR_AUTHOR="architect" \
|
PR_TITLE="[bug-x7k2m9] Enforce contract" PR_AUTHOR="architect" \
|
||||||
PR_BODY="$body" GITEA_SERVER_URL="https://code.test" GITEA_TOKEN="test-token" \
|
PR_BODY="$body" GITEA_SERVER_URL="https://code.test" GITEA_TOKEN="test-token" \
|
||||||
GITEA_REPOSITORY="action/check-naming" GITEA_PR_NUMBER=1 PATH="${TEST_BIN}:$PATH" \
|
GITEA_REPOSITORY="action/check-naming" GITEA_PR_NUMBER=1 PATH="${TEST_BIN}:$PATH" \
|
||||||
BASE_SHA="$base" HEAD_SHA="$head" bash "$SCRIPT" 2>&1) || rc=$?
|
BASE_SHA="$base" HEAD_SHA="$head" bash "$SCRIPT" 2>&1) || rc=$?
|
||||||
@@ -66,7 +66,7 @@ check_contract_pass() {
|
|||||||
local desc="$1" body="$2" base="$3" head="$4"
|
local desc="$1" body="$2" base="$3" head="$4"
|
||||||
local out rc=0
|
local out rc=0
|
||||||
out=$(cd "${FIXTURES}" && HEAD_BRANCH="architect/bug-x7k2m9/contract" \
|
out=$(cd "${FIXTURES}" && HEAD_BRANCH="architect/bug-x7k2m9/contract" \
|
||||||
PR_TITLE="[bug-x7k2m9] fix(check): enforce contract" PR_AUTHOR="architect" \
|
PR_TITLE="[bug-x7k2m9] Enforce contract" PR_AUTHOR="architect" \
|
||||||
PR_BODY="$body" GITEA_SERVER_URL="https://code.test" GITEA_TOKEN="test-token" \
|
PR_BODY="$body" GITEA_SERVER_URL="https://code.test" GITEA_TOKEN="test-token" \
|
||||||
GITEA_REPOSITORY="action/check-naming" GITEA_PR_NUMBER=1 PATH="${TEST_BIN}:$PATH" \
|
GITEA_REPOSITORY="action/check-naming" GITEA_PR_NUMBER=1 PATH="${TEST_BIN}:$PATH" \
|
||||||
BASE_SHA="$base" HEAD_SHA="$head" bash "$SCRIPT" 2>&1) || rc=$?
|
BASE_SHA="$base" HEAD_SHA="$head" bash "$SCRIPT" 2>&1) || rc=$?
|
||||||
@@ -98,38 +98,18 @@ check_chore_contract() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
check_author_contract() {
|
|
||||||
local desc="$1" want_fail="$2" author="$3" body="$4" base="$5" head="$6"
|
|
||||||
local out rc=0
|
|
||||||
out=$(cd "${FIXTURES}" && HEAD_BRANCH="chore/contract" \
|
|
||||||
PR_TITLE="Align PR attribution policy" PR_AUTHOR="$author" \
|
|
||||||
PR_BODY="$body" GITEA_SERVER_URL="https://code.test" GITEA_TOKEN="test-token" \
|
|
||||||
GITEA_REPOSITORY="action/check-naming" GITEA_PR_NUMBER=1 PATH="${TEST_BIN}:$PATH" \
|
|
||||||
BASE_SHA="$base" HEAD_SHA="$head" bash "$SCRIPT" 2>&1) || rc=$?
|
|
||||||
local got_lines=0
|
|
||||||
printf '%s\n' "$out" | grep -qE '^FAIL' && got_lines=1 || true
|
|
||||||
if [ "$rc" -eq "$want_fail" ] && [ "$got_lines" -eq "$want_fail" ]; then
|
|
||||||
echo "PASS [$desc]"
|
|
||||||
PASS=$((PASS + 1))
|
|
||||||
else
|
|
||||||
echo "FAIL [$desc]: expected exit=$want_fail fail-lines=$want_fail, got exit=$rc fail-lines=$got_lines"
|
|
||||||
echo " output: $out"
|
|
||||||
FAIL=$((FAIL + 1))
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# break-glass
|
# break-glass
|
||||||
check "dfritz exempt — invalid branch" 0 "totally/wrong-branch" "no prefix" "dfritz"
|
check "dfritz exempt — invalid branch" 0 "totally/wrong-branch" "no prefix" "dfritz"
|
||||||
check "dfritz exempt — mismatch" 0 "dev/bug-abc/thing" "[bug-xyz] Thing" "dfritz"
|
check "dfritz exempt — mismatch" 0 "dev/bug-abc/thing" "[bug-xyz] Thing" "dfritz"
|
||||||
|
|
||||||
# valid role/bug — all roster handles
|
# valid role/bug — all roster handles
|
||||||
check "dev/bug matching title" 0 "dev/bug-x7k2m9/fix-terminal-resize" "[bug-x7k2m9] fix(terminal): preserve resize" "dev"
|
check "dev/bug matching title" 0 "dev/bug-x7k2m9/fix-terminal-resize" "[bug-x7k2m9] Fix terminal resize" "dev"
|
||||||
check "architect/bug matching title" 0 "architect/bug-abc123/refactor-auth" "[bug-abc123] refactor(auth): simplify boundary" "architect"
|
check "architect/bug matching title" 0 "architect/bug-abc123/refactor-auth" "[bug-abc123] Refactor auth" "architect"
|
||||||
check "ops/bug matching title" 0 "ops/bug-zz9/deploy-tweak" "[bug-zz9] fix(delivery): preserve rollout" "ops"
|
check "ops/bug matching title" 0 "ops/bug-zz9/deploy-tweak" "[bug-zz9] Deploy tweak" "ops"
|
||||||
check "security/bug matching title" 0 "security/bug-s1a2/patch-cve" "[bug-s1a2] fix(auth): close bypass" "security"
|
check "security/bug matching title" 0 "security/bug-s1a2/patch-cve" "[bug-s1a2] Patch CVE" "security"
|
||||||
check "perf/bug matching title" 0 "perf/bug-p0p0/reduce-latency" "[bug-p0p0] perf(checker): reduce latency" "perf"
|
check "perf/bug matching title" 0 "perf/bug-p0p0/reduce-latency" "[bug-p0p0] Reduce latency" "perf"
|
||||||
check "ux/bug matching title" 0 "ux/bug-u1u1/polish-modal" "[bug-u1u1] fix(modal): preserve focus" "ux"
|
check "ux/bug matching title" 0 "ux/bug-u1u1/polish-modal" "[bug-u1u1] Polish modal" "ux"
|
||||||
check "support/bug matching title" 0 "support/bug-sup9/clarify-error" "[bug-sup9] docs(error): clarify recovery" "support"
|
check "support/bug matching title" 0 "support/bug-sup9/clarify-error" "[bug-sup9] Clarify error" "support"
|
||||||
|
|
||||||
# valid chore form
|
# valid chore form
|
||||||
check "chore — no title prefix" 0 "chore/bump-deps" "Bump dependency versions" "dev"
|
check "chore — no title prefix" 0 "chore/bump-deps" "Bump dependency versions" "dev"
|
||||||
@@ -143,13 +123,10 @@ check "bug-id mismatch" 1 "dev/bug-x7k2m9/fix-resize" "[bug-
|
|||||||
|
|
||||||
# fail: chore branch with [bug-id] title prefix
|
# fail: chore branch with [bug-id] title prefix
|
||||||
check "chore with bug-id title" 1 "chore/bump-deps" "[bug-abc123] Bump deps" "dev"
|
check "chore with bug-id title" 1 "chore/bump-deps" "[bug-abc123] Bump deps" "dev"
|
||||||
check "role/bug Conventional title" 0 "dev/bug-x7k2m9/fix-resize" "[bug-x7k2m9] fix(hub): fix resize" "dev"
|
check "role/bug Conventional title" 1 "dev/bug-x7k2m9/fix-resize" "[bug-x7k2m9] fix(hub): fix resize" "dev"
|
||||||
check "role/bug plain title" 1 "dev/bug-x7k2m9/fix-resize" "[bug-x7k2m9] Fix resize" "dev"
|
check "role/bug Conventional title gap" 1 "dev/bug-x7k2m9/fix-resize" "[bug-x7k2m9] fix(hub): fix resize" "dev"
|
||||||
check "role/bug missing scope" 1 "dev/bug-x7k2m9/fix-resize" "[bug-x7k2m9] fix: fix resize" "dev"
|
check "chore Conventional title" 1 "chore/bump-deps" "chore(deps): bump deps" "dev"
|
||||||
check "role/bug path scope" 1 "dev/bug-x7k2m9/fix-resize" "[bug-x7k2m9] fix(hub/bugs): fix resize" "dev"
|
check "role/bug breaking title" 1 "dev/bug-x7k2m9/fix-resize" "[bug-x7k2m9] fix!: break resize" "dev"
|
||||||
check "role/bug empty description" 1 "dev/bug-x7k2m9/fix-resize" "[bug-x7k2m9] fix(hub): " "dev"
|
|
||||||
check "role/bug whitespace description" 1 "dev/bug-x7k2m9/fix-resize" "[bug-x7k2m9] fix(hub): " "dev"
|
|
||||||
check "chore Conventional title" 0 "chore/bump-deps" "chore(deps): bump deps" "dev"
|
|
||||||
|
|
||||||
# fail: invalid branch forms
|
# fail: invalid branch forms
|
||||||
check "invalid — no role prefix" 1 "feature/foo-bar" "Add feature" "dev"
|
check "invalid — no role prefix" 1 "feature/foo-bar" "Add feature" "dev"
|
||||||
@@ -239,7 +216,7 @@ canonical_body bug-x7k2m9 >> "${LARGE_BODY}"
|
|||||||
large_out=""
|
large_out=""
|
||||||
large_rc=0
|
large_rc=0
|
||||||
large_out=$(cd "${FIXTURES}" && HEAD_BRANCH="architect/bug-x7k2m9/contract" \
|
large_out=$(cd "${FIXTURES}" && HEAD_BRANCH="architect/bug-x7k2m9/contract" \
|
||||||
PR_TITLE="[bug-x7k2m9] fix(check): enforce contract" PR_AUTHOR="architect" \
|
PR_TITLE="[bug-x7k2m9] Enforce contract" PR_AUTHOR="architect" \
|
||||||
TEST_PR_BODY_FILE="${LARGE_BODY}" GITEA_SERVER_URL="https://code.test" GITEA_TOKEN="test-token" \
|
TEST_PR_BODY_FILE="${LARGE_BODY}" GITEA_SERVER_URL="https://code.test" GITEA_TOKEN="test-token" \
|
||||||
GITEA_REPOSITORY="action/check-naming" GITEA_PR_NUMBER=1 PATH="${TEST_BIN}:$PATH" \
|
GITEA_REPOSITORY="action/check-naming" GITEA_PR_NUMBER=1 PATH="${TEST_BIN}:$PATH" \
|
||||||
BASE_SHA="$BASE" HEAD_SHA="$GOOD_HEAD" bash "$SCRIPT" 2>&1) || large_rc=$?
|
BASE_SHA="$BASE" HEAD_SHA="$GOOD_HEAD" bash "$SCRIPT" 2>&1) || large_rc=$?
|
||||||
@@ -253,46 +230,20 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
git -C "${FIXTURES}" commit --allow-empty -q -m "unwatermarked change"
|
git -C "${FIXTURES}" commit --allow-empty -q -m "unwatermarked change"
|
||||||
PLAIN_HEAD=$(git -C "${FIXTURES}" rev-parse HEAD)
|
BAD_HEAD=$(git -C "${FIXTURES}" rev-parse HEAD)
|
||||||
check_contract_pass "Bug-backed commits need no watermark" "$BODY" "$GOOD_HEAD" "$PLAIN_HEAD"
|
check_contract "agenthub 779 commits reject missing watermark" "$BODY" "$GOOD_HEAD" "$BAD_HEAD" "must end with an Authored-By"
|
||||||
check_chore_contract "chore requires PR attribution" "" "$BASE" "$GOOD_HEAD" "## Attribution must contain"
|
check_chore_contract "chore requires PR attribution" "" "$BASE" "$GOOD_HEAD" "## Attribution must contain"
|
||||||
|
check_chore_contract "chore commits require watermark" "$(canonical_body bug-unused)" "$GOOD_HEAD" "$BAD_HEAD" "must end with an Authored-By"
|
||||||
|
|
||||||
git -C "${FIXTURES}" commit --allow-empty -q -m $'misplaced trailer\nAuthored-By: Codex (GPT-5) <noreply@openai.com>'
|
git -C "${FIXTURES}" commit --allow-empty -q -m $'misplaced trailer\nAuthored-By: Codex (GPT-5) <noreply@openai.com>'
|
||||||
MISPLACED_HEAD=$(git -C "${FIXTURES}" rev-parse HEAD)
|
MISPLACED_HEAD=$(git -C "${FIXTURES}" rev-parse HEAD)
|
||||||
|
check_contract "commit watermark requires blank separator" "$BODY" "$BAD_HEAD" "$MISPLACED_HEAD" "must end with an Authored-By"
|
||||||
|
|
||||||
git -C "${FIXTURES}" commit --allow-empty -q -m "trailer not final" -m $'Authored-By: Codex (GPT-5) <noreply@openai.com>\nextra text'
|
git -C "${FIXTURES}" commit --allow-empty -q -m "trailer not final" -m $'Authored-By: Codex (GPT-5) <noreply@openai.com>\nextra text'
|
||||||
NONFINAL_HEAD=$(git -C "${FIXTURES}" rev-parse HEAD)
|
NONFINAL_HEAD=$(git -C "${FIXTURES}" rev-parse HEAD)
|
||||||
|
check_contract "commit watermark must be final trailer" "$BODY" "$MISPLACED_HEAD" "$NONFINAL_HEAD" "must end with an Authored-By"
|
||||||
git -C "${FIXTURES}" commit --allow-empty -q -m "malformed legacy attribution" -m "Authored-By: incomplete"
|
|
||||||
MALFORMED_HEAD=$(git -C "${FIXTURES}" rev-parse HEAD)
|
|
||||||
|
|
||||||
# Existing Human exemption is unchanged; other Humans and named Agents satisfy
|
|
||||||
# the same PR-description contract independently of any commit-message footer.
|
|
||||||
for author in dfritz human-contributor architect; do
|
|
||||||
for scenario in legacy plain misplaced nonfinal malformed; do
|
|
||||||
case "$scenario" in
|
|
||||||
legacy) head="$GOOD_HEAD" ;;
|
|
||||||
plain) head="$PLAIN_HEAD" ;;
|
|
||||||
misplaced) head="$MISPLACED_HEAD" ;;
|
|
||||||
nonfinal) head="$NONFINAL_HEAD" ;;
|
|
||||||
malformed) head="$MALFORMED_HEAD" ;;
|
|
||||||
esac
|
|
||||||
check_author_contract "$author accepts $scenario commit messages" 0 "$author" "$BODY" "$BASE" "$head"
|
|
||||||
done
|
|
||||||
done
|
|
||||||
|
|
||||||
for author in human-contributor architect; do
|
|
||||||
check_author_contract "$author requires PR-description attribution" 1 "$author" "" "$BASE" "$PLAIN_HEAD"
|
|
||||||
check_author_contract "$author rejects a missing PR model" 1 "$author" \
|
|
||||||
$'## Attribution\n- Authored-By: Codex <noreply@openai.com>' "$BASE" "$PLAIN_HEAD"
|
|
||||||
check_author_contract "$author rejects a non-noreply PR address" 1 "$author" \
|
|
||||||
$'## Attribution\n- Authored-By: Codex (GPT-5) <person@example.invalid>' "$BASE" "$PLAIN_HEAD"
|
|
||||||
done
|
|
||||||
check_author_contract "dfritz exemption still ignores PR attribution and commit range" 0 dfritz "" not-a-sha not-a-sha
|
|
||||||
|
|
||||||
check_contract "missing commit range rejects" "$BODY" "$GOOD_HEAD" "$GOOD_HEAD" "contains no PR commits"
|
check_contract "missing commit range rejects" "$BODY" "$GOOD_HEAD" "$GOOD_HEAD" "contains no PR commits"
|
||||||
check_contract "invalid SHA rejects before git" "$BODY" "not-a-sha" "$GOOD_HEAD" "lowercase hexadecimal"
|
check_contract "invalid SHA rejects before git" "$BODY" "not-a-sha" "$GOOD_HEAD" "lowercase hexadecimal"
|
||||||
check_contract "unavailable commit rejects" "$BODY" 0000000000000000000000000000000000000000 "$GOOD_HEAD" "must name available commits"
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Results: ${PASS} passed, ${FAIL} failed"
|
echo "Results: ${PASS} passed, ${FAIL} failed"
|
||||||
|
|||||||
Reference in new issue
Block a user