@metasession.co/devaudit-cli 0.3.8 → 0.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/scripts/upload-evidence.sh +23 -3
- package/sdlc/files/_common/joining-an-existing-project.md +5 -4
- package/sdlc/files/_common/scripts/close-out-contract.test.sh +71 -0
- package/sdlc/files/_common/scripts/derive-release-version.test.sh +48 -0
- package/sdlc/files/_common/scripts/extract-release-metadata.test.sh +85 -0
- package/sdlc/files/_common/scripts/prepare-release-pr.sh +99 -0
- package/sdlc/files/_common/scripts/upload-evidence.sh +593 -0
- package/sdlc/files/_common/scripts/validate-commits.sh +32 -7
- package/sdlc/files/_common/scripts/validate-commits.test.sh +143 -0
- package/sdlc/files/_common/skills/sdlc-implementer/SKILL.md +39 -3
- package/sdlc/files/ci/ci.yml.template +2 -2
- package/sdlc/files/ci/compliance-evidence.yml.template +49 -4
- package/sdlc/files/ci/incident-export.yml.template +7 -3
- package/sdlc/files/ci/label-retention.yml.template +3 -1
- package/sdlc/files/ci/periodic-review.yml.template +5 -3
- package/sdlc/files/ci/post-deploy-prod.yml.template +6 -1
- package/sdlc/files/ci/quality-gates-provenance.yml.template +1 -1
- package/sdlc/files/stacks/node/hooks/pre-push +13 -13
- package/sdlc/package.json +1 -1
- package/sdlc/src/bin/devaudit-sdlc.js +490 -54
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# validate-commits.test.sh — Fixture-based tests for validate-commits.sh.
|
|
3
|
+
#
|
|
4
|
+
# Covers the relaxed "exactly one active tracked release" behavior:
|
|
5
|
+
# implementation commits without REQ tags downgrade to warnings only when
|
|
6
|
+
# release context is unambiguous; otherwise they remain hard errors.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# ./scripts/validate-commits.test.sh
|
|
10
|
+
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
14
|
+
VALIDATOR="$SCRIPT_DIR/validate-commits.sh"
|
|
15
|
+
[ -x "$VALIDATOR" ] || chmod +x "$VALIDATOR"
|
|
16
|
+
|
|
17
|
+
PASS=0
|
|
18
|
+
FAIL=0
|
|
19
|
+
|
|
20
|
+
make_fixture() {
|
|
21
|
+
local dir="$1" subject="$2" body="${3:-}"
|
|
22
|
+
rm -rf "$dir"
|
|
23
|
+
mkdir -p "$dir"
|
|
24
|
+
cd "$dir"
|
|
25
|
+
git init -q --initial-branch=main
|
|
26
|
+
git config user.email "test@example.com"
|
|
27
|
+
git config user.name "test"
|
|
28
|
+
echo "base" > base.txt
|
|
29
|
+
git add base.txt
|
|
30
|
+
git commit -q -m "chore: base"
|
|
31
|
+
|
|
32
|
+
git checkout -q -b feature
|
|
33
|
+
echo "feature" > app.ts
|
|
34
|
+
git add app.ts
|
|
35
|
+
if [ -n "$body" ]; then
|
|
36
|
+
git commit -q -m "$subject" -m "$body"
|
|
37
|
+
else
|
|
38
|
+
git commit -q -m "$subject"
|
|
39
|
+
fi
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
run_validator() {
|
|
43
|
+
set +e
|
|
44
|
+
OUT_FILE=$(mktemp)
|
|
45
|
+
bash "$VALIDATOR" main > "$OUT_FILE" 2>&1
|
|
46
|
+
LAST_EXIT=$?
|
|
47
|
+
set -e
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
assert_exit() {
|
|
51
|
+
local desc="$1" want="$2"
|
|
52
|
+
if [ "$LAST_EXIT" = "$want" ]; then
|
|
53
|
+
echo " PASS: $desc (exit=$LAST_EXIT)"
|
|
54
|
+
PASS=$((PASS + 1))
|
|
55
|
+
else
|
|
56
|
+
echo " FAIL: $desc (want=$want, got=$LAST_EXIT)"
|
|
57
|
+
sed 's/^/ /' "$OUT_FILE"
|
|
58
|
+
FAIL=$((FAIL + 1))
|
|
59
|
+
fi
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
assert_grep() {
|
|
63
|
+
local desc="$1" pattern="$2" want_match="$3"
|
|
64
|
+
local found=0
|
|
65
|
+
if grep -qE "$pattern" "$OUT_FILE"; then
|
|
66
|
+
found=1
|
|
67
|
+
fi
|
|
68
|
+
if [ "$found" = "$want_match" ]; then
|
|
69
|
+
echo " PASS: $desc"
|
|
70
|
+
PASS=$((PASS + 1))
|
|
71
|
+
else
|
|
72
|
+
echo " FAIL: $desc (want match=$want_match, got=$found, pattern=$pattern)"
|
|
73
|
+
sed 's/^/ /' "$OUT_FILE"
|
|
74
|
+
FAIL=$((FAIL + 1))
|
|
75
|
+
fi
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
WORKDIR=$(mktemp -d -t validate-commits-test-XXXX)
|
|
79
|
+
trap 'rm -rf "$WORKDIR"' EXIT
|
|
80
|
+
|
|
81
|
+
echo "=== validate-commits.sh tests ==="
|
|
82
|
+
|
|
83
|
+
# Case 1: one active pending ticket makes a missing-REQ implementation
|
|
84
|
+
# commit a warning, not a hard failure.
|
|
85
|
+
echo "Case 1: single active pending release downgrades missing REQ to warning"
|
|
86
|
+
make_fixture "$WORKDIR/case1" "feat: implement kitchen workflow" "Co-Authored-By: Test <test@example.com>"
|
|
87
|
+
mkdir -p compliance/pending-releases
|
|
88
|
+
cat > compliance/pending-releases/RELEASE-TICKET-REQ-123.md <<'EOF'
|
|
89
|
+
# Release Ticket: REQ-123
|
|
90
|
+
EOF
|
|
91
|
+
git add compliance/pending-releases
|
|
92
|
+
git commit -q --amend --no-edit
|
|
93
|
+
run_validator
|
|
94
|
+
assert_exit "single active pending release exits 0" 0
|
|
95
|
+
assert_grep "warning emitted for missing REQ with one active release" 'WARNING .*one active tracked release \(REQ-123\)' 1
|
|
96
|
+
assert_grep "no hard error for missing requirement" "ERROR .*implementation commit but cites no requirement" 0
|
|
97
|
+
|
|
98
|
+
# Case 2: ambiguous pending release context keeps the missing-REQ path as
|
|
99
|
+
# a hard error.
|
|
100
|
+
echo "Case 2: two active pending releases keep missing REQ as hard error"
|
|
101
|
+
make_fixture "$WORKDIR/case2" "feat: implement kitchen workflow" "Co-Authored-By: Test <test@example.com>"
|
|
102
|
+
mkdir -p compliance/pending-releases
|
|
103
|
+
cat > compliance/pending-releases/RELEASE-TICKET-REQ-123.md <<'EOF'
|
|
104
|
+
# Release Ticket: REQ-123
|
|
105
|
+
EOF
|
|
106
|
+
cat > compliance/pending-releases/RELEASE-TICKET-REQ-124.md <<'EOF'
|
|
107
|
+
# Release Ticket: REQ-124
|
|
108
|
+
EOF
|
|
109
|
+
git add compliance/pending-releases
|
|
110
|
+
git commit -q --amend --no-edit
|
|
111
|
+
run_validator
|
|
112
|
+
assert_exit "ambiguous pending releases exits 1" 1
|
|
113
|
+
assert_grep "hard error emitted when active context is ambiguous" "ERROR .*implementation commit but cites no requirement" 1
|
|
114
|
+
|
|
115
|
+
# Case 3: no pending ticket, but exactly one active RTM row in the
|
|
116
|
+
# accepted statuses also downgrades to a warning.
|
|
117
|
+
echo "Case 3: RTM fallback with one active row downgrades missing REQ to warning"
|
|
118
|
+
make_fixture "$WORKDIR/case3" "fix: recover already-merged history" "Co-Authored-By: Test <test@example.com>"
|
|
119
|
+
mkdir -p compliance
|
|
120
|
+
cat > compliance/RTM.md <<'EOF'
|
|
121
|
+
# RTM
|
|
122
|
+
| REQ-ID | Title | Status |
|
|
123
|
+
| ------- | ----- | ------------------------- |
|
|
124
|
+
| REQ-200 | Done | APPROVED - DEPLOYED |
|
|
125
|
+
| REQ-201 | Live | TESTED - PENDING SIGN-OFF |
|
|
126
|
+
EOF
|
|
127
|
+
git add compliance/RTM.md
|
|
128
|
+
git commit -q --amend --no-edit
|
|
129
|
+
run_validator
|
|
130
|
+
assert_exit "single active RTM row exits 0" 0
|
|
131
|
+
assert_grep "warning emitted for RTM fallback context" 'WARNING .*one active tracked release \(REQ-201\)' 1
|
|
132
|
+
assert_grep "no hard error under single RTM fallback context" "ERROR .*implementation commit but cites no requirement" 0
|
|
133
|
+
|
|
134
|
+
# Case 4: zero active context remains a hard error.
|
|
135
|
+
echo "Case 4: no active release context keeps missing REQ as hard error"
|
|
136
|
+
make_fixture "$WORKDIR/case4" "perf: tune kitchen workflow" "Co-Authored-By: Test <test@example.com>"
|
|
137
|
+
run_validator
|
|
138
|
+
assert_exit "no active context exits 1" 1
|
|
139
|
+
assert_grep "hard error emitted without any active release context" "ERROR .*implementation commit but cites no requirement" 1
|
|
140
|
+
|
|
141
|
+
echo
|
|
142
|
+
echo "Result: $PASS passed, $FAIL failed"
|
|
143
|
+
[ "$FAIL" = "0" ]
|
|
@@ -553,7 +553,17 @@ Reached only on the **tracked** route from Phase 0 (the issue is already fetched
|
|
|
553
553
|
|
|
554
554
|
### Phase 4 — Submit for UAT review (SDLC stage 4)
|
|
555
555
|
|
|
556
|
-
1. **
|
|
556
|
+
1. **Prepare the truthful release PR** — the PR that carries the UAT four-eyes approval gate (`check-release-approval.yml`), always into `$RELEASE_BRANCH` from `$INTEGRATION_BRANCH` (e.g. `develop → main`). The implementation already landed on `$INTEGRATION_BRANCH` in Phase 2; this promotes it. (Note: if other work is also waiting on `$INTEGRATION_BRANCH`, this is a bundled release — every in-scope REQ keeps its own release record and Production approval.)
|
|
557
|
+
|
|
558
|
+
Before creating a PR manually, run the synced helper:
|
|
559
|
+
|
|
560
|
+
```bash
|
|
561
|
+
./scripts/prepare-release-pr.sh --apply --mode=update
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
This is mandatory for devaudit-installer#312. It detects an existing open `$INTEGRATION_BRANCH → $RELEASE_BRANCH` PR, derives the current governing release from the current head, and ensures there is exactly one truthful release PR. If the existing PR is stale, the default `update` mode rewrites the title/body and comments with the refresh. Use `--mode=recreate` only when the operator explicitly wants the older PR closed and a new PR opened.
|
|
565
|
+
|
|
566
|
+
If the helper reports multiple open release PRs, halt with: "Multiple open release PRs for `$INTEGRATION_BRANCH → $RELEASE_BRANCH`; operator action — close duplicates or choose the authoritative PR, then resume REQ-XXX." Do not open another PR.
|
|
557
567
|
|
|
558
568
|
PR body per the SDLC PR template (see [`.github/pull_request_template.md`](../../../../../.github/pull_request_template.md)):
|
|
559
569
|
- Closes #N
|
|
@@ -571,8 +581,30 @@ Reached only on the **tracked** route from Phase 0 (the issue is already fetched
|
|
|
571
581
|
|
|
572
582
|
3. **Apply labels** — `awaiting-uat-review`, `risk:<class>`.
|
|
573
583
|
4. **Comment on the issue**: "Implementation complete. PR #M opened. Evidence on portal: <link>. UAT review requested. Resume with `resume REQ-XXX` once UAT approval is granted on the portal."
|
|
574
|
-
5. **
|
|
575
|
-
|
|
584
|
+
5. **Inspect PR blockers before handoff (devaudit-installer#304).** Run `gh pr view <N> --json mergeStateStatus,reviewDecision,statusCheckRollup,url` and categorize the state:
|
|
585
|
+
- `reviewDecision` not approved → human approval blocker; hand off to the reviewer.
|
|
586
|
+
- Release Approval Gate failing while portal state is not `uat_approved` → portal approval blocker; hand off to portal UAT reviewer.
|
|
587
|
+
- Release Approval Gate failing while portal state is `uat_approved` → retry path below.
|
|
588
|
+
- Compliance validation / evidence completeness failing → real workflow or evidence defect; fix or create a follow-up PR before handoff.
|
|
589
|
+
- Checks pending → wait/poll until terminal before declaring the PR ready.
|
|
590
|
+
- Stale PR context detected by `prepare-release-pr.sh` → rerun the helper in `--apply` mode and repeat this blocker inspection.
|
|
591
|
+
|
|
592
|
+
**Executable loop (devaudit-installer#304).** Use the bundled watcher rather than a one-shot `gh` read when the PR is blocked or waiting:
|
|
593
|
+
|
|
594
|
+
```bash
|
|
595
|
+
node SDLC/bin/devaudit-sdlc.js \
|
|
596
|
+
--watch-pr=<N> \
|
|
597
|
+
--repo <owner/name> \
|
|
598
|
+
--req XXX \
|
|
599
|
+
--release REQ-XXX \
|
|
600
|
+
--project-slug <project-slug> \
|
|
601
|
+
--base-url <devaudit-base-url>
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
The watcher persists retry state in `.sdlc-pr-watch.json`, polls `gh pr view` + `gh pr checks`, re-runs likely flaky workflows automatically, and re-runs the Release Approval Gate when the portal is already approved but GitHub has not converged yet. Use `--once` when you only need a single classification pass.
|
|
605
|
+
|
|
606
|
+
6. **Hard stop only after blocker classification.** Phase 4 ends here. Do not proceed to merge; the human's next action is reviewing on the portal or clearing the categorized blocker above.
|
|
607
|
+
7. **Update SDLC status sticky** before halting: `bash scripts/update-sdlc-status.sh "$ISSUE_NUM" "Phase 4 — truthful release PR #<N> prepared against $RELEASE_BRANCH; blockers classified" "Operator action — review PR #<N> + approve UAT release on the portal; sdlc-implementer halts until you ping resume REQ-XXX"`. This is a critical handoff — the sticky must reflect that the agent has stopped + the operator is on the hook.
|
|
576
608
|
|
|
577
609
|
**When an external gate hangs or fails for unrelated reasons.** A required gate may fail for reasons outside the change's scope — flaky infra, an unrelated regression test that hangs at hour-plus runtime with no log activity, a known-failing suite. When this happens:
|
|
578
610
|
|
|
@@ -685,6 +717,10 @@ The native agent must NOT continue to the next phase (Phase 3 → Phase 4, Phase
|
|
|
685
717
|
|
|
686
718
|
**PR merged to main ≠ done.** After the release PR is merged to `main`, the native agent must re-invoke the skill for Phase 5 close-out (see Phase 5 step 0 below). The merge is not the end of the workflow — post-merge compliance steps are mandatory.
|
|
687
719
|
|
|
720
|
+
### Commit-history hygiene recovery (devaudit-installer#315)
|
|
721
|
+
|
|
722
|
+
Commit-level `[REQ-XXX]` hygiene is preventive, not the strongest source of release truth after history has already merged to a protected integration branch. If CI reports a missing commit-level REQ after the branch has exactly one active tracked release ticket or one matching RTM row, treat the release ticket / RTM context as authoritative and add a follow-up compliance note instead of rewriting protected history. The generated `validate-commits.sh` implements this as a warning in the exactly-one-active-release case. If there are zero or multiple active tracked releases, the validation remains a hard error because the release context is ambiguous.
|
|
723
|
+
|
|
688
724
|
### Commit-scoping rule for SRS updates (devaudit-installer#226)
|
|
689
725
|
|
|
690
726
|
When a commit is authored as part of REQ-XXX's SDLC flow, the commit subject must cite `[REQ-XXX]` — the **active REQ** driving the current SDLC flow — regardless of which other REQs the content touches. Other REQs may appear in the body for traceability, but never in the subject.
|
|
@@ -783,13 +783,13 @@ jobs:
|
|
|
783
783
|
# "zero traceable evidence" even though well-written, REQ-tagged
|
|
784
784
|
# specs exist on disk. If specs are found but weren't run in
|
|
785
785
|
# this tier, emit a non-blocking warning instead of an error.
|
|
786
|
-
SPECS_ON_DISK=$(grep -rl --include='*.spec.ts' -e "@requirement ${REQ}" -e "tagTest.*${REQ}" -e "'${REQ}'" e2e/ 2>/dev/null | wc -l)
|
|
786
|
+
SPECS_ON_DISK=$( { grep -rl --include='*.spec.ts' -e "@requirement ${REQ}" -e "tagTest.*${REQ}" -e "'${REQ}'" e2e/ 2>/dev/null || true; } | wc -l | tr -d ' ')
|
|
787
787
|
|
|
788
788
|
# devaudit-installer#237 — also scan unit test files for
|
|
789
789
|
# @requirement REQ-XXX annotations. A REQ with thorough unit
|
|
790
790
|
# test coverage but no E2E specs (legitimate for API-only or
|
|
791
791
|
# backend-only changes) should not be hard-blocked.
|
|
792
|
-
UNIT_TESTS_ON_DISK=$(grep -rl --include='*.test.ts' --include='*.test.tsx' -e "@requirement ${REQ}" -e "'${REQ}'" e2e/ __tests__/ tests/ src/ 2>/dev/null | wc -l)
|
|
792
|
+
UNIT_TESTS_ON_DISK=$( { grep -rl --include='*.test.ts' --include='*.test.tsx' -e "@requirement ${REQ}" -e "'${REQ}'" e2e/ __tests__/ tests/ src/ 2>/dev/null || true; } | wc -l | tr -d ' ')
|
|
793
793
|
|
|
794
794
|
# devaudit-installer#237 — check for test-execution-summary.md
|
|
795
795
|
# as a secondary signal. This is a human-authored document and
|
|
@@ -638,6 +638,7 @@ jobs:
|
|
|
638
638
|
permissions:
|
|
639
639
|
contents: read
|
|
640
640
|
actions: read
|
|
641
|
+
issues: write
|
|
641
642
|
env:
|
|
642
643
|
DEVAUDIT_BASE_URL_VAR: ${{ vars.DEVAUDIT_BASE_URL }}
|
|
643
644
|
DEVAUDIT_API_KEY: ${{ secrets.DEVAUDIT_API_KEY }}
|
|
@@ -682,7 +683,7 @@ jobs:
|
|
|
682
683
|
name: e2e-regression-report
|
|
683
684
|
path: e2e-artifacts/
|
|
684
685
|
run-id: ${{ github.event.workflow_run.id }}
|
|
685
|
-
github-token: ${{
|
|
686
|
+
github-token: ${{ github.token }}
|
|
686
687
|
continue-on-error: true
|
|
687
688
|
|
|
688
689
|
- name: Derive release version
|
|
@@ -726,8 +727,9 @@ jobs:
|
|
|
726
727
|
FLAGS="${FLAGS} --test-cycle ${{ github.event.workflow_run.id }}"
|
|
727
728
|
|
|
728
729
|
# In-scope REQs from pending release tickets at the triggering
|
|
729
|
-
# SHA.
|
|
730
|
-
# the
|
|
730
|
+
# SHA. If the ticket was created after the triggering E2E run,
|
|
731
|
+
# recover REQs from the Playwright JSON artifact before falling
|
|
732
|
+
# back to `_compliance-docs` (DevAudit-Installer#311).
|
|
731
733
|
REQS=()
|
|
732
734
|
if [ -d compliance/pending-releases ]; then
|
|
733
735
|
for TICKET in compliance/pending-releases/RELEASE-TICKET-REQ-*.md; do
|
|
@@ -742,6 +744,47 @@ jobs:
|
|
|
742
744
|
REQS+=("$REQ_ID")
|
|
743
745
|
done
|
|
744
746
|
fi
|
|
747
|
+
|
|
748
|
+
if [ "${#REQS[@]}" -eq 0 ] && [ -f e2e-artifacts/e2e-regression-results.json ]; then
|
|
749
|
+
while IFS= read -r REQ_ID; do
|
|
750
|
+
[ -n "$REQ_ID" ] || continue
|
|
751
|
+
REQS+=("$REQ_ID")
|
|
752
|
+
done < <(python3 - <<'PY'
|
|
753
|
+
import json
|
|
754
|
+
import re
|
|
755
|
+
import sys
|
|
756
|
+
|
|
757
|
+
try:
|
|
758
|
+
with open('e2e-artifacts/e2e-regression-results.json', 'r', encoding='utf-8') as handle:
|
|
759
|
+
data = json.load(handle)
|
|
760
|
+
except Exception:
|
|
761
|
+
sys.exit(0)
|
|
762
|
+
|
|
763
|
+
seen = set()
|
|
764
|
+
|
|
765
|
+
def walk(value):
|
|
766
|
+
if isinstance(value, str):
|
|
767
|
+
for req in re.findall(r'REQ-[0-9]+', value):
|
|
768
|
+
seen.add(req)
|
|
769
|
+
elif isinstance(value, dict):
|
|
770
|
+
for item in value.values():
|
|
771
|
+
walk(item)
|
|
772
|
+
elif isinstance(value, list):
|
|
773
|
+
for item in value:
|
|
774
|
+
walk(item)
|
|
775
|
+
|
|
776
|
+
walk(data)
|
|
777
|
+
for req in sorted(seen):
|
|
778
|
+
print(req)
|
|
779
|
+
PY
|
|
780
|
+
)
|
|
781
|
+
fi
|
|
782
|
+
|
|
783
|
+
if [ "${#REQS[@]}" -eq 1 ] && echo "$DERIVED_RELEASE" | grep -qE '^v[0-9]{4}\.[0-9]{2}\.[0-9]{2}([.][0-9]+)?$'; then
|
|
784
|
+
echo "E2E upload recovered tracked release ${REQS[0]} from artifact metadata; overriding bare-date release ${DERIVED_RELEASE}."
|
|
785
|
+
DERIVED_RELEASE="${REQS[0]}"
|
|
786
|
+
fi
|
|
787
|
+
|
|
745
788
|
if [ "${#REQS[@]}" -eq 0 ]; then
|
|
746
789
|
REQS=(_compliance-docs)
|
|
747
790
|
fi
|
|
@@ -812,7 +855,9 @@ jobs:
|
|
|
812
855
|
- name: Triage and file incidents on E2E Regression failure
|
|
813
856
|
if: steps.resolve.outputs.skip != 'true' && github.event.workflow_run.conclusion == 'failure'
|
|
814
857
|
env:
|
|
815
|
-
|
|
858
|
+
# GitHub issue/label mutations use the workflow token. DevAudit
|
|
859
|
+
# user tokens are portal PATs, not guaranteed GitHub PATs (#305).
|
|
860
|
+
GH_TOKEN: ${{ github.token }}
|
|
816
861
|
CI_RUN_ID: ${{ github.event.workflow_run.id }}
|
|
817
862
|
WORKFLOW_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}
|
|
818
863
|
GIT_SHA: ${{ github.event.workflow_run.head_sha }}
|
|
@@ -49,7 +49,9 @@ jobs:
|
|
|
49
49
|
- uses: actions/checkout@v6
|
|
50
50
|
with:
|
|
51
51
|
fetch-depth: 0
|
|
52
|
-
|
|
52
|
+
# GitHub repo mutations use the workflow token. The DevAudit
|
|
53
|
+
# user token is for portal attribution, not GitHub checkout/push.
|
|
54
|
+
token: ${{ github.token }}
|
|
53
55
|
|
|
54
56
|
- name: Classify routing (Path A vs Path B)
|
|
55
57
|
id: route
|
|
@@ -436,7 +438,8 @@ jobs:
|
|
|
436
438
|
- name: Direct commit to develop (Path A)
|
|
437
439
|
if: steps.route.outputs.path == 'A'
|
|
438
440
|
env:
|
|
439
|
-
|
|
441
|
+
# GitHub push uses the workflow token plus job permissions.
|
|
442
|
+
GH_TOKEN: ${{ github.token }}
|
|
440
443
|
OUT: ${{ steps.paths.outputs.out }}
|
|
441
444
|
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
|
442
445
|
ISSUE_URL: ${{ github.event.issue.html_url }}
|
|
@@ -454,7 +457,8 @@ jobs:
|
|
|
454
457
|
- name: Open export PR (Path B)
|
|
455
458
|
if: steps.route.outputs.path == 'B'
|
|
456
459
|
env:
|
|
457
|
-
|
|
460
|
+
# GitHub branch push + PR creation use the workflow token.
|
|
461
|
+
GH_TOKEN: ${{ github.token }}
|
|
458
462
|
BRANCH: ${{ steps.paths.outputs.branch }}
|
|
459
463
|
OUT: ${{ steps.paths.outputs.out }}
|
|
460
464
|
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
|
@@ -56,7 +56,9 @@ jobs:
|
|
|
56
56
|
- name: Enforce label retention
|
|
57
57
|
if: steps.opt.outputs.enabled == 'true'
|
|
58
58
|
env:
|
|
59
|
-
|
|
59
|
+
# GitHub issue/comment mutations use the workflow token. The
|
|
60
|
+
# DevAudit user token is reserved for portal attribution flows.
|
|
61
|
+
GH_TOKEN: ${{ github.token }}
|
|
60
62
|
EVENT_NAME: ${{ github.event_name }}
|
|
61
63
|
EVENT_ACTION: ${{ github.event.action }}
|
|
62
64
|
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
|
@@ -37,8 +37,9 @@ jobs:
|
|
|
37
37
|
- uses: actions/checkout@v6
|
|
38
38
|
with:
|
|
39
39
|
fetch-depth: 0
|
|
40
|
-
# Need write access for the chore branch.
|
|
41
|
-
token
|
|
40
|
+
# Need write access for the chore branch. GitHub repo mutations
|
|
41
|
+
# use the workflow token; the DevAudit user token is portal auth.
|
|
42
|
+
token: ${{ github.token }}
|
|
42
43
|
|
|
43
44
|
- name: Compute review period
|
|
44
45
|
id: period
|
|
@@ -188,7 +189,8 @@ jobs:
|
|
|
188
189
|
|
|
189
190
|
- name: Open / update review PR
|
|
190
191
|
env:
|
|
191
|
-
|
|
192
|
+
# GitHub branch push + PR creation use the workflow token.
|
|
193
|
+
GH_TOKEN: ${{ github.token }}
|
|
192
194
|
BRANCH: ${{ steps.period.outputs.branch }}
|
|
193
195
|
REVIEW_ID: ${{ steps.period.outputs.review_id }}
|
|
194
196
|
run: |
|
|
@@ -34,6 +34,9 @@ jobs:
|
|
|
34
34
|
production-evidence:
|
|
35
35
|
name: Production Evidence
|
|
36
36
|
runs-on: {{RUNNER}}
|
|
37
|
+
permissions:
|
|
38
|
+
contents: read
|
|
39
|
+
issues: write
|
|
37
40
|
env:
|
|
38
41
|
DEVAUDIT_BASE_URL_VAR: ${{ vars.DEVAUDIT_BASE_URL }}
|
|
39
42
|
DEVAUDIT_API_KEY: ${{ secrets.DEVAUDIT_API_KEY }}
|
|
@@ -151,7 +154,9 @@ jobs:
|
|
|
151
154
|
- name: File incident on smoke failure
|
|
152
155
|
if: failure()
|
|
153
156
|
env:
|
|
154
|
-
|
|
157
|
+
# GitHub issue/label mutations use the workflow token. DevAudit
|
|
158
|
+
# user tokens are portal PATs, not guaranteed GitHub PATs (#305).
|
|
159
|
+
GH_TOKEN: ${{ github.token }}
|
|
155
160
|
PROD_URL: ${{ secrets.{{PRODUCTION_URL_SECRET}} }}
|
|
156
161
|
GIT_SHA: ${{ github.sha }}
|
|
157
162
|
CI_RUN: ${{ github.run_id }}
|
|
@@ -31,7 +31,7 @@ jobs:
|
|
|
31
31
|
steps:
|
|
32
32
|
- name: Verify prior Quality Gates success on head SHA
|
|
33
33
|
env:
|
|
34
|
-
GH_TOKEN: ${{
|
|
34
|
+
GH_TOKEN: ${{ github.token }}
|
|
35
35
|
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
36
36
|
CURRENT_RUN_ID: ${{ github.run_id }}
|
|
37
37
|
run: |
|
|
@@ -38,6 +38,9 @@ echo "Pre-push: TypeScript check passed."
|
|
|
38
38
|
# Previous code had two `while read` loops; the second consumed nothing
|
|
39
39
|
# because stdin was already exhausted by the first (devaudit-installer#278).
|
|
40
40
|
PUSH_REFS="$(cat)"
|
|
41
|
+
PUSH_REFS_FILE=$(mktemp)
|
|
42
|
+
trap 'rm -f "$PUSH_REFS_FILE"' EXIT HUP INT TERM
|
|
43
|
+
printf '%s\n' "$PUSH_REFS" > "$PUSH_REFS_FILE"
|
|
41
44
|
|
|
42
45
|
INTEGRATION_BRANCH=$(jq -r '.integration_branch // "develop"' sdlc-config.json 2>/dev/null || echo "develop")
|
|
43
46
|
|
|
@@ -49,7 +52,8 @@ INTEGRATION_BRANCH=$(jq -r '.integration_branch // "develop"' sdlc-config.json 2
|
|
|
49
52
|
# (written by e2e-test-engineer Phase 5½ after validating tagTest/evidenceShot).
|
|
50
53
|
# Scoped to integration-branch pushes (correct for the normal workflow).
|
|
51
54
|
|
|
52
|
-
|
|
55
|
+
while read -r local_ref local_sha remote_ref remote_sha; do
|
|
56
|
+
[ -n "${local_ref:-}" ] || continue
|
|
53
57
|
# Check if pushing to the integration branch
|
|
54
58
|
case "$remote_ref" in
|
|
55
59
|
*"refs/heads/$INTEGRATION_BRANCH") ;;
|
|
@@ -103,7 +107,7 @@ echo "$PUSH_REFS" | while read -r local_ref local_sha remote_ref remote_sha; do
|
|
|
103
107
|
fi
|
|
104
108
|
echo "Pre-push: E2E evidence wiring check passed."
|
|
105
109
|
fi
|
|
106
|
-
done
|
|
110
|
+
done < "$PUSH_REFS_FILE"
|
|
107
111
|
|
|
108
112
|
# ── 3. Skill-invocation sentinel (devaudit-installer#226) ────────────
|
|
109
113
|
# If feat/fix/refactor/perf commits are in the push, verify the
|
|
@@ -115,11 +119,9 @@ done
|
|
|
115
119
|
TRACKED_TYPES='^(feat|fix|refactor|perf)(\(.+\))?!?:'
|
|
116
120
|
HAS_TRACKED=false
|
|
117
121
|
|
|
118
|
-
# Parse
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
local_sha="$2"
|
|
122
|
-
remote_sha="$4"
|
|
122
|
+
# Parse captured refs in the current shell so HAS_TRACKED propagates.
|
|
123
|
+
while read -r local_ref local_sha remote_ref remote_sha; do
|
|
124
|
+
[ -n "${local_ref:-}" ] || continue
|
|
123
125
|
if [ "$remote_sha" = "0000000000000000000000000000000000000000" ]; then
|
|
124
126
|
RANGE="$local_sha"
|
|
125
127
|
else
|
|
@@ -129,7 +131,7 @@ for ref_line in $PUSH_REFS; do
|
|
|
129
131
|
if echo "$COMMITS" | grep -qE "$TRACKED_TYPES"; then
|
|
130
132
|
HAS_TRACKED=true
|
|
131
133
|
fi
|
|
132
|
-
done
|
|
134
|
+
done < "$PUSH_REFS_FILE"
|
|
133
135
|
|
|
134
136
|
if [ "$HAS_TRACKED" = "true" ]; then
|
|
135
137
|
if [ ! -f .sdlc-implementer-invoked ]; then
|
|
@@ -147,10 +149,8 @@ if [ "$HAS_TRACKED" = "true" ]; then
|
|
|
147
149
|
# If the commit touches Phase 3 artifacts (compliance/evidence/REQ-XXX/),
|
|
148
150
|
# the sentinel must contain a phase "3" record. If it touches Phase 5
|
|
149
151
|
# close-out (approved-releases/ or RTM RELEASED), sentinel must have phase "5".
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
local_sha="$2"
|
|
153
|
-
remote_sha="$4"
|
|
152
|
+
while read -r local_ref local_sha remote_ref remote_sha; do
|
|
153
|
+
[ -n "${local_ref:-}" ] || continue
|
|
154
154
|
if [ "$remote_sha" = "0000000000000000000000000000000000000000" ]; then
|
|
155
155
|
RANGE="$local_sha"
|
|
156
156
|
else
|
|
@@ -181,7 +181,7 @@ if [ "$HAS_TRACKED" = "true" ]; then
|
|
|
181
181
|
exit 1
|
|
182
182
|
fi
|
|
183
183
|
fi
|
|
184
|
-
done
|
|
184
|
+
done < "$PUSH_REFS_FILE"
|
|
185
185
|
echo "Pre-push: phase-progression check passed."
|
|
186
186
|
fi
|
|
187
187
|
|
package/sdlc/package.json
CHANGED