bigpowers 2.88.14 → 2.88.15
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/.gemini/extensions/bigpowers/gemini-extension.json +1 -1
- package/.pi/package.json +1 -1
- package/CHANGELOG.md +7 -0
- package/CLAUDE.md +1 -1
- package/package.json +1 -1
- package/scripts/lib/__pycache__/link_utils.cpython-310.pyc +0 -0
- package/scripts/lib/__pycache__/srp_engine_links.cpython-310.pyc +0 -0
- package/scripts/test-ci-pipeline-contract.sh +82 -9
package/.pi/package.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [2.88.15](https://github.com/danielvm-git/bigpowers/compare/v2.88.14...v2.88.15) (2026-10-25)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **ci:** gate the GitHub Pages deploy on every check in test-build-release ([#164](https://github.com/danielvm-git/bigpowers/issues/164)) ([5f3e4de](https://github.com/danielvm-git/bigpowers/commit/5f3e4de59454d5ffe95d932a3da873db7b4e1d80))
|
|
7
|
+
|
|
1
8
|
## [2.88.14](https://github.com/danielvm-git/bigpowers/compare/v2.88.13...v2.88.14) (2026-10-25)
|
|
2
9
|
|
|
3
10
|
|
package/CLAUDE.md
CHANGED
|
@@ -68,7 +68,7 @@ Stack: Markdown / Bash (documentation-based; skills integrate with Claude Code,
|
|
|
68
68
|
| Lint | `bash scripts/sync-skills.sh` (validates SKILL.md syntax) |
|
|
69
69
|
| Validate specs YAML | `bash scripts/validate-specs-yaml.sh` |
|
|
70
70
|
| Typecheck | N/A (Markdown / Bash project) |
|
|
71
|
-
| CI platform | GitHub Actions — one pipeline `.github/workflows/test-build-release.yml` (sync, verification-gates, skill-verify, story-verify → release); plus `
|
|
71
|
+
| CI platform | GitHub Actions — one pipeline `.github/workflows/test-build-release.yml` (sync, verification-gates, skill-verify, story-verify → release, and → pages-build → pages-deploy when docs paths change); plus `publish-wiki.yml` |
|
|
72
72
|
| Compliance | `npm run compliance` |
|
|
73
73
|
| Verification Gates | `bash scripts/run-verification-gates.sh` |
|
|
74
74
|
| Traceability | `bash scripts/trace-stories.sh --strict` | grep for story tags (traceability check) |
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# story: BUG-2026-10-25-release-not-gated
|
|
2
|
+
# story: BUG-2026-10-25-release-not-gated BUG-2026-10-25-pages-not-gated
|
|
3
3
|
# test-ci-pipeline-contract.sh — the CI shape the portfolio template requires
|
|
4
4
|
# (danielvm-git/.github, docs/concept/github-actions-best-practices.md):
|
|
5
5
|
#
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
# 3. no ubuntu-latest; every non-local action is pinned to a 40-char SHA
|
|
11
11
|
# 4. every job has timeout-minutes
|
|
12
12
|
# 5. release and Pages never cancel a run in progress
|
|
13
|
+
# 6. the Pages deploy (any job using actions/deploy-pages) lives in the
|
|
14
|
+
# pipeline, needs: every gate job and runs only for main
|
|
13
15
|
#
|
|
14
16
|
# Agent workflows (agent-locks.yml, generated agentics-maintenance.yml) are
|
|
15
17
|
# exempt from 3–4 per migration plan v2.
|
|
@@ -40,6 +42,27 @@ def cancels(block):
|
|
|
40
42
|
# Literal true cancels; an expression or false does not cancel a push run.
|
|
41
43
|
return isinstance(block, dict) and block.get("cancel-in-progress") is True
|
|
42
44
|
|
|
45
|
+
def needs_of(job):
|
|
46
|
+
needs = job.get("needs", [])
|
|
47
|
+
return {needs} if isinstance(needs, str) else set(needs)
|
|
48
|
+
|
|
49
|
+
def ancestors(jobs, name):
|
|
50
|
+
seen, todo = set(), list(needs_of(jobs.get(name, {})))
|
|
51
|
+
while todo:
|
|
52
|
+
n = todo.pop()
|
|
53
|
+
if n not in seen:
|
|
54
|
+
seen.add(n)
|
|
55
|
+
todo.extend(needs_of(jobs.get(n, {})))
|
|
56
|
+
return seen
|
|
57
|
+
|
|
58
|
+
def deploys_pages(job):
|
|
59
|
+
return any(str(st.get("uses", "")).startswith("actions/deploy-pages@")
|
|
60
|
+
for st in job.get("steps") or [])
|
|
61
|
+
|
|
62
|
+
def main_only(cond):
|
|
63
|
+
return ("refs/heads/main" in cond and
|
|
64
|
+
("github.event_name == 'push'" in cond or "workflow_dispatch" in cond))
|
|
65
|
+
|
|
43
66
|
pipe_path = os.path.join(d, "test-build-release.yml")
|
|
44
67
|
if not os.path.isfile(pipe_path):
|
|
45
68
|
bad.append("test-build-release.yml missing")
|
|
@@ -49,13 +72,30 @@ else:
|
|
|
49
72
|
missing = GATES - set(jobs)
|
|
50
73
|
if missing:
|
|
51
74
|
bad.append(f"pipeline lacks gate jobs: {sorted(missing)}")
|
|
75
|
+
# Gate jobs: the four named ones plus any other job that needs nothing —
|
|
76
|
+
# a new root job is a gate until it says otherwise with a needs: edge.
|
|
77
|
+
gate_jobs = (GATES | {n for n, j in jobs.items() if not needs_of(j)}) - {"release"}
|
|
52
78
|
rel = jobs.get("release")
|
|
53
79
|
if not rel:
|
|
54
80
|
bad.append("pipeline has no release job")
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
81
|
+
pages = [n for n, j in jobs.items() if deploys_pages(j)]
|
|
82
|
+
if not pages:
|
|
83
|
+
bad.append("pipeline has no Pages deploy job")
|
|
84
|
+
for name in pages:
|
|
85
|
+
job, up = jobs[name], ancestors(jobs, name)
|
|
86
|
+
if not gate_jobs <= up:
|
|
87
|
+
bad.append(f"{name}: Pages deploy does not need: {sorted(gate_jobs - up)}")
|
|
88
|
+
conds = [str(jobs[n].get("if", "")) for n in up | {name} if n in jobs]
|
|
89
|
+
if any("always()" in c or "cancelled()" in c for c in conds):
|
|
90
|
+
bad.append(f"{name}: Pages deploy runs even when a gate fails (always()/cancelled())")
|
|
91
|
+
if not any(main_only(c) for c in conds):
|
|
92
|
+
bad.append(f"{name}: Pages deploy is not limited to main")
|
|
93
|
+
if cancels(pipe.get("concurrency")) or cancels(job.get("concurrency")):
|
|
94
|
+
bad.append(f"{name}: Pages deploy can be cancelled in progress")
|
|
95
|
+
if not isinstance(job.get("concurrency"), dict) or not job["concurrency"].get("group"):
|
|
96
|
+
bad.append(f"{name}: Pages deploy has no concurrency group")
|
|
97
|
+
if rel:
|
|
98
|
+
needs = needs_of(rel)
|
|
59
99
|
if not gate_jobs <= needs:
|
|
60
100
|
bad.append(f"release does not need: {sorted(gate_jobs - needs)}")
|
|
61
101
|
cond = str(rel.get("if", ""))
|
|
@@ -68,16 +108,14 @@ for name in RETIRED:
|
|
|
68
108
|
if os.path.exists(os.path.join(d, name)):
|
|
69
109
|
bad.append(f"retired workflow still present: {name}")
|
|
70
110
|
|
|
71
|
-
docs = os.path.join(d, "docs-site.yml")
|
|
72
|
-
if os.path.isfile(docs) and cancels(load(docs).get("concurrency")):
|
|
73
|
-
bad.append("docs-site.yml: Pages deploy can be cancelled in progress")
|
|
74
|
-
|
|
75
111
|
for path in sorted(glob.glob(os.path.join(d, "*.yml"))):
|
|
76
112
|
name = os.path.basename(path)
|
|
77
113
|
if name in EXEMPT:
|
|
78
114
|
continue
|
|
79
115
|
for job_name, job in (load(path).get("jobs") or {}).items():
|
|
80
116
|
where = f"{name}:{job_name}"
|
|
117
|
+
if name != "test-build-release.yml" and deploys_pages(job):
|
|
118
|
+
bad.append(f"{where}: Pages deploy outside the pipeline (no needs: edge to the gates)")
|
|
81
119
|
if "uses" in job: # reusable-workflow call: no runner of its own
|
|
82
120
|
continue
|
|
83
121
|
if "ubuntu-latest" in str(job.get("runs-on", "")):
|
|
@@ -101,6 +139,7 @@ self_test() {
|
|
|
101
139
|
tmp=$(mktemp -d)
|
|
102
140
|
trap "rm -rf '$tmp'" EXIT
|
|
103
141
|
local pin="actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0"
|
|
142
|
+
local deploy="actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128"
|
|
104
143
|
write_fixture() { # $1 = dir
|
|
105
144
|
mkdir -p "$1"
|
|
106
145
|
cat > "$1/test-build-release.yml" <<YAML
|
|
@@ -117,6 +156,18 @@ jobs:
|
|
|
117
156
|
timeout-minutes: 15
|
|
118
157
|
concurrency: {group: release, cancel-in-progress: false}
|
|
119
158
|
steps: [{run: "true"}]
|
|
159
|
+
pages-build:
|
|
160
|
+
needs: [sync, verification-gates, skill-verify, story-verify]
|
|
161
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
162
|
+
runs-on: ubuntu-22.04
|
|
163
|
+
timeout-minutes: 15
|
|
164
|
+
steps: [{run: "true"}]
|
|
165
|
+
pages-deploy:
|
|
166
|
+
needs: pages-build
|
|
167
|
+
runs-on: ubuntu-22.04
|
|
168
|
+
timeout-minutes: 15
|
|
169
|
+
concurrency: {group: pages, cancel-in-progress: false}
|
|
170
|
+
steps: [{uses: "$deploy"}]
|
|
120
171
|
YAML
|
|
121
172
|
}
|
|
122
173
|
expect() { # $1 = label, $2 = want pass|fail, $3 = dir
|
|
@@ -145,6 +196,28 @@ YAML
|
|
|
145
196
|
write_fixture "$tmp/retired"; : > "$tmp/retired/publish.yml"
|
|
146
197
|
expect "retired workflow present fails" fail "$tmp/retired"
|
|
147
198
|
|
|
199
|
+
write_fixture "$tmp/pages-needs"
|
|
200
|
+
sed -i.bak '/pages-build:/{n;s/needs: \[.*\]/needs: [sync]/;}' "$tmp/pages-needs/test-build-release.yml"
|
|
201
|
+
expect "Pages deploy missing gate needs fails" fail "$tmp/pages-needs"
|
|
202
|
+
|
|
203
|
+
write_fixture "$tmp/pages-always"
|
|
204
|
+
sed -i.bak '/pages-deploy:/{n;s/needs: pages-build/needs: pages-build\
|
|
205
|
+
if: always()/;}' "$tmp/pages-always/test-build-release.yml"
|
|
206
|
+
expect "Pages deploy with always() fails" fail "$tmp/pages-always"
|
|
207
|
+
|
|
208
|
+
write_fixture "$tmp/pages-anyref"
|
|
209
|
+
sed -i.bak '/pages-build:/{n;n;s/if: .*/if: github.event_name == '"'push'"'/;}' "$tmp/pages-anyref/test-build-release.yml"
|
|
210
|
+
expect "Pages deploy not limited to main fails" fail "$tmp/pages-anyref"
|
|
211
|
+
|
|
212
|
+
write_fixture "$tmp/pages-cancel"
|
|
213
|
+
sed -i.bak 's/group: pages, cancel-in-progress: false/group: pages, cancel-in-progress: true/' "$tmp/pages-cancel/test-build-release.yml"
|
|
214
|
+
expect "cancellable Pages deploy fails" fail "$tmp/pages-cancel"
|
|
215
|
+
|
|
216
|
+
write_fixture "$tmp/pages-split"
|
|
217
|
+
printf 'on: push\njobs:\n deploy: {runs-on: ubuntu-22.04, timeout-minutes: 15, steps: [{uses: %s}]}\n' \
|
|
218
|
+
"$deploy" > "$tmp/pages-split/docs-site.yml"
|
|
219
|
+
expect "Pages deploy in a separate workflow fails" fail "$tmp/pages-split"
|
|
220
|
+
|
|
148
221
|
[ "$ok" -eq 1 ] && echo "OK: pipeline contract self-test" || { echo "pipeline contract self-test FAILED"; exit 1; }
|
|
149
222
|
}
|
|
150
223
|
|