Repository navigation
Description
Problem Statement
Squad ships 11 workflow templates to user repos. PR #887 added caching, timeouts, concurrency, SHA pinning, and security hardening — but there are zero automated tests ensuring these properties persist. A SHA transcription error was caught only by manual security review. Future edits could silently regress any of these properties.
Current State
test/workflows.test.cjsexists — testsname:,on:,jobs:keys and framework workflow copying on init. Does NOT test structural properties.test/template-sync.test.tsexists — 146 tests ensuring byte-identity across 4 template directories. Works great for sync, but doesn't validate content correctness.test/ci-concurrency.test.cjsexists — tests Squad's OWN.github/workflows/CI, NOT the shipped user templates.- Zero tests for: timeout presence, SHA pinning, concurrency blocks, cancel-in-progress values, cache configuration, bot guards, persist-credentials, id-token scoping, permissions blocks.
Proposed Solution — Two Layers
Phase 1: Structural Tests (must ship)
Add to new test/template-ci-hardening.test.cjs or extend test/workflows.test.cjs:
Tests to write (20 structural assertions):
- timeout-minutes required — Every template has
timeout-minuteson every job - SHA pinning (40 hex) — No
uses: actions/*@v[0-9]+pattern. All action refs must be 40-character lowercase hex SHA (e.g.,action@abc123...def), NOT floating refs like@main,@latest,@HEAD,@master - Concurrency blocks — Every template has a
concurrency:block (all 11 templates) - cancel-in-progress: false group — Release/promote/insider-release have
cancel-in-progress: false(includes: squad-release.yml, squad-promote.yml, squad-insider-release.yml) - cancel-in-progress: true group — CI/preview/sync-labels have
cancel-in-progress: true(includes: squad-ci.yml, squad-preview.yml, squad-sync-labels.yml, squad-docs.yml, squad-triage.yml, squad-issue-assign.yml, squad-label-enforce.yml, squad-heartbeat.yml) - npm cache configuration — Templates with
setup-nodehavecache: npm - no npm cache when no setup-node — Templates WITHOUT
setup-nodedo NOT havecache: npm - Bot guards on label-triggered workflows — Triage, issue-assign, and label-enforce workflows include bot guards to skip
github-actions[bot]anddependabot[bot]runs - persist-credentials: false on read-only — Read-only workflows (triage, issue-assign, label-enforce) have
persist-credentials: false - id-token scoping (indentation-aware) — squad-docs.yml has
id-token: writeonly in the deploy job, NOT at workflow level. Must use indentation-aware regex or YAML parser to distinguish workflow-level vs job-level scoping (simpleincludes()check is insufficient) - SHA format validation — All SHA pins are exactly 40 lowercase hexadecimal characters
- SHA version comments — All SHA pins have version comments (e.g.,
# v6.0.2) for audit trail - SHA consistency — Same action used across different templates must have identical SHA
- dorny/paths-filter wiring — squad-ci.yml uses
dorny/paths-filter(notpaths-ignore). Assertion checks: (a)uses: dorny/paths-filterpresent, (b)filters:block exists, (c) downstream job referencesneeds.changes.outputs - permissions block required — Every template declares a top-level
permissions:block (zero-trust baseline — without it, GITHUB_TOKEN defaults tocontents: write) - no pull_request_target — Shipped templates must not use
pull_request_targettrigger. Exception: if used, must pair with fork-safety guardgithub.event.pull_request.head.repo.full_name == github.repository - no workflow-level id-token: write —
id-token: writemust not appear at workflow level in any template (only squad-docs.yml deploy job should have it; all other templates must have no id-token grant or job-level only) - no secrets: inherit — Templates must not use
secrets: inheritin reusable workflow calls (violates least-privilege principle) - HAS_COPILOT_TOKEN pattern — Heartbeat env block correctly uses
secrets.COPILOT_ASSIGN_TOKEN != ''boolean comparison, not raw secret interpolation in strings - Concurrency group sanity — Every
concurrency:expression'sgroup:is non-empty and contains${{ ... }}interpolation (exception: squad-docs.yml uses staticgroup: pages)
Also added to Phase 1:
- Known-good SHA map — Add
test/fixtures/known-good-shas.json. Mapsaction@tag → expected SHA. Tests compare against this map. Any SHA change requires deliberate fixture update = audit trail. - YAML schema validation — Use
ajv+ GitHub workflow JSON schema. Catches structural errors text-matching misses. Validates all 11 templates againsthttps://json.schemastore.org/github-workflow.json
Test infrastructure:
- Use
getAllTemplateWorkflows()helper (already exists inworkflows.test.cjs) as iteration basis, so new templates are auto-covered by all assertions - File:
test/template-ci-hardening.test.cjs(separate fromworkflows.test.cjswhich tests CLI behavior) - Dependencies:
node:fs,node:assert,ajv(for schema validation)
Estimated effort: 3-4 hours. Zero build-breaking dependencies.
Phase 2: Pre-Release Smoke Testing with act (nice to have)
- Install
nektos/actas optional dev documentation - Create minimal test fixture repo in
test/fixtures/act-test-repo/withpackage.json+package-lock.json - Run each template through
actwith:- Happy path: npm project with lockfile
- Edge case: no lockfile (should degrade gracefully)
- Edge case: pnpm project (should not hard-fail)
- Document in
docs/contributing/testing-workflows.md
SHA resolution companion script — Document in Phase 2: a 20-line shell script using gh api /repos/{owner}/{action}/git/commits/{sha} to verify SHAs resolve. Not CI-blocking, but pre-release manual check.
Estimated effort: 4-6 hours. Requires Docker (manual gate, not CI).
Phase 3: Dynamic Template Enumeration & Broader Coverage (nice to have)
- All structural tests use
getAllTemplateWorkflows()for auto-discovery - Broader floating ref regex catches
@main,@latest,@HEAD,@master, not just@vN
Success Criteria
- 20 structural test assertions covering all properties from PRD: CI Template Overhaul — Performance & Safety for Shipped Workflows #886 + team review feedback
- Tests run in existing
npm test— no new CI jobs - Tests catch: missing timeout, floating action tag, wrong cancel-in-progress, missing bot guard, missing permissions block, id-token leaks, secrets: inherit
- Tests fail if a future edit removes SHA pinning or reverts to
@v4 - Known-good SHA map prevents silent SHA drift
- YAML schema validation in Phase 1 (promoted from Phase 2)
- Documentation for optional
actsmoke testing
Non-Goals
- Testing workflow LOGIC (triage routing, label assignment, etc.) — that's a separate concern
- Adding
actto CI (too heavy — Docker dependency) - Testing Squad-internal workflows (
.github/workflows/) — covered byci-concurrency.test.cjs
Team Review History
v2 — Updated with team review from Booster, FIDO, RETRO
Changes from v1:
- Assertion 3 fix — Removed "(except squad-docs.yml)" exception. ALL 11 templates now confirmed to have
concurrency:blocks. - Assertions 4/5 expansion — Added 5 missing templates with correct cancel-in-progress grouping:
cancel-in-progress: true: squad-docs.yml, squad-triage.yml, squad-issue-assign.yml, squad-label-enforce.yml, squad-heartbeat.ymlcancel-in-progress: false: squad-release.yml, squad-promote.yml, squad-insider-release.yml
- Assertion 10 refinement — Clarified that
includes()is insufficient. Must use indentation-aware regex or YAML parser to distinguish workflow-level vs job-level id-token scoping. - New assertions 15-18 — Added four critical security assertions:
- 15: Explicit
permissions:block required at top-level (zero-trust baseline) - 16: No
pull_request_targettrigger (or with fork-safety guard) - 17: No
id-token: writeat workflow level (only squad-docs deploy job) - 18: No
secrets: inheritin reusable workflow calls
- 15: Explicit
- Assertion 19 — Added
HAS_COPILOT_TOKENpattern validation (boolean comparison, not raw interpolation) - Assertion 20 — Added concurrency group sanity check (non-empty, interpolation required except squad-docs static group)
- Known-good SHA map — Promoted to Phase 1. Maps
action@tag → expected SHAwith audit trail. - YAML schema validation — Promoted to Phase 1 (from "nice to have"). Catches structural errors.
- dorny/paths-filter wiring — Enhanced Assertion 14 to validate not just presence but also
filters:block and downstreamneeds.changes.outputsreference. - Broader floating ref coverage — Assertion 2 now catches
@main,@latest,@HEAD,@masterand all non-SHA refs. - Dynamic template enumeration — Tests use
getAllTemplateWorkflows()helper for auto-discovery of new templates. - SHA resolution script — Documented as Phase 2 manual pre-release gate.
- New test file —
test/template-ci-hardening.test.cjsseparates CI hardening tests from workflow sync/initialization tests. - Total structural assertions: 20 (up from 14) + concurrency group sanity.
References
- Issue PRD: CI Template Overhaul — Performance & Safety for Shipped Workflows #886 — PRD: CI Template Overhaul
- PR feat(ci): harden shipped workflow templates — caching, timeouts, concurrency, SHA pins #887 — Implementation (all 3 phases)
- Booster's review: clarified scope for all 11 templates
- FIDO's review: identified zero structural tests as a blocker
- RETRO's review: caught invalid SHA — structural test would have prevented this
nektos/act— https://github.com/nektos/act- GitHub workflow JSON schema — https://json.schemastore.org/github-workflow.json
Activity
github-actions commentedon Apr 6, 2026
🏗️ Squad Triage — Flight (Lead)
Issue: #888 — PRD: CI Template Testing Infrastructure — Structural Tests & Pre-Release Validation
Assigned to: FIDO (Quality Owner)
Reason: Issue relates to testing/quality work
@copilot evaluation: No strong capability match — routed to squad member.
Team roster:
squad:flightsquad:proceduressquad:eecomsquad:fidosquad:paosquad:capcomsquad:controlsquad:surgeonsquad:boostersquad:gncsquad:networksquad:retrosquad:incosquad:guidosquad:telemetrysquad:voxsquad:dskysquad:simssquad:handbooksquad:ralphsquad:copilotdiberry commentedon Apr 7, 2026
Team Review — v2 Updates
Reviewers: ⚙️ Booster (CI/CD), 🧪 FIDO (Quality), 🔒 RETRO (Security)
Verdict: All 3 APPROVE WITH CHANGES — blockers identified and incorporated below.
Blockers Fixed
permissions:block validationpermissions:pull_request_targetguardpull_request_target(or pair with fork-safety)Additions Incorporated
test/fixtures/known-good-shas.json) — audit trail for SHA changes (RETRO)HAS_COPILOT_TOKENboolean pattern test — verify heartbeat uses!= ''comparison, not raw secret (Booster)dorny/paths-filterwiring validation — checkfilters:block + downstreamneeds.changes.outputs.src(Booster)@main,@latest,@HEAD, not just@vN(FIDO)permissions:required on every template (RETRO)pull_request_targetin shipped templates (RETRO)id-token: writeat workflow level except squad-docs deploy job (Booster)secrets: inheritin reusable workflow calls (RETRO)Revised Test Count: 18 structural assertions (was 14)
Revised Phase Plan
actsmoke testing with Docker + SHA resolution companion scriptfeat(test): CI template hardening tests — 20 structural assertions
docs: update FIDO history with #888 hardening test learnings
diberry commentedon Apr 10, 2026
Closing — descoping high-touch feature work to focus on existing PRs and low-touch fixes.