88 names : [bundle-submission]
99 skip-bots : [github-actions, copilot, dependabot]
1010
11+ engine :
12+ id : copilot
13+ args :
14+ - --allow-url=https://github.com
15+ - --allow-url=https://codeload.github.com
16+ - --allow-url=https://release-assets.githubusercontent.com
17+ - --allow-url=https://raw.githubusercontent.com
18+
1119tools :
1220 edit :
13- bash : ["echo", "grep", "sort", "python3", "jq", "date", "curl"]
21+ bash : ["echo", "grep", "sort", "python3", "jq", "date", "curl", "sha256sum" ]
1422 github :
1523 toolsets : [issues, repos]
1624 min-integrity : none
@@ -20,7 +28,9 @@ network:
2028 allowed :
2129 - defaults
2230 - github.com
31+ - codeload.github.com
2332 - release-assets.githubusercontent.com
33+ - raw.githubusercontent.com
2434
2535permissions :
2636 contents : read
@@ -54,6 +64,23 @@ safe-outputs:
5464 max : 3
5565 remove-labels :
5666 allowed : [validation-passed, validation-failed, needs-info]
67+
68+ jobs :
69+ conclusion :
70+ pre-steps :
71+ - name : Mark bundle submission passed after PR creation
72+ if : needs.safe_outputs.result == 'success' && needs.safe_outputs.outputs.created_pr_number != ''
73+ uses : actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
74+ with :
75+ script : |
76+ const issue = { ...context.repo, issue_number: context.payload.issue.number };
77+ const labels = await github.paginate(github.rest.issues.listLabelsOnIssue, issue);
78+ for (const name of ['validation-failed', 'needs-info']) {
79+ if (labels.some(label => label.name === name)) {
80+ await github.rest.issues.removeLabel({ ...issue, name });
81+ }
82+ }
83+ await github.rest.issues.addLabels({ ...issue, labels: ['validation-passed'] });
5784---
5885
5986# Add Community Bundle from Issue Submission
@@ -100,6 +127,15 @@ Read issue #${{ github.event.issue.number }} and extract these issue-form fields
100127
101128Issue-form values appear beneath headings matching their labels.
102129
130+ The optional checksum is free-form submission data, not a dedicated issue-form input.
131+ Extract ` submitted_sha256 ` from the submitted issue, not release metadata or the
132+ computed digest. Accept a manually appended ` ### SHA-256 ` heading (including
133+ ` ### SHA-256 (sha256) ` ) or the ` sha256 ` field in the Proposed Catalog Entry.
134+ If both sources supply a checksum, they must agree after trimming whitespace
135+ and normalizing hexadecimal case. A supplied checksum must contain exactly 64
136+ hexadecimal characters; malformed or conflicting values are submission failures,
137+ not an absent checksum.
138+
103139## Step 2 - Validate the Submission
104140
105141Run every check and collect all failures before deciding the outcome.
@@ -149,11 +185,40 @@ Run every check and collect all failures before deciding the outcome.
149185- Confirm the asset name is versioned and consistent with the submitted bundle
150186 ID and version.
151187
152- Use ` curl ` for binary downloads, follow HTTPS redirects with
153- ` --location --proto '=https' --proto-redir '=https' ` , and bound the request with
154- ` --max-time 60 ` . Save the archive under ` /tmp/gh-aw/ ` and inspect the final
155- HTTP status with ` --write-out '%{http_code}' ` . A blocked or failed download
156- must not count as a passed check; repository/release metadata is not a
188+ Use ` curl ` for binary downloads. After the URL passes the pinning checks, replace
189+ ` VALIDATED_DOWNLOAD_URL ` below with that exact URL, safely shell-quoted. Treat
190+ issue values as data, never as executable shell syntax:
191+
192+ ``` bash
193+ curl --location --proto ' =https' --proto-redir ' =https' --max-time 60 --silent --show-error --write-out ' %{http_code}' --output /tmp/gh-aw/community-archive.zip ' VALIDATED_DOWNLOAD_URL'
194+ ```
195+
196+ Run the download and checksum as separate shell calls, without ` mkdir ` , command
197+ substitution, pipelines, or chained commands. ` /tmp/gh-aw/ ` already exists.
198+ Compute SHA-256 only after a successful download with final HTTP 200.
199+ Use ` sha256sum /tmp/gh-aw/community-archive.zip ` to record its digest as ` actual_sha256 ` .
200+ If no checksum was submitted, skip the comparison without failing validation.
201+ Otherwise, use the edit tool to write ` /tmp/gh-aw/community-archive.sha256 ` with
202+ exactly this one line and a trailing newline, replacing ` EXPECTED_SHA256 ` with
203+ the validated ` submitted_sha256 ` (two spaces before the fixed archive path):
204+
205+ ``` text
206+ EXPECTED_SHA256 /tmp/gh-aw/community-archive.zip
207+ ```
208+
209+ Run this comparison as a separate shell call:
210+
211+ ``` bash
212+ sha256sum --check --strict /tmp/gh-aw/community-archive.sha256
213+ ```
214+
215+ Require exit code 0 and an ` OK ` result before marking the checksum check passed.
216+ A ` FAILED ` checksum comparison is a Failed outcome: report the submitted and
217+ actual digests, remove ` validation-passed ` , add ` validation-failed ` , and stop
218+ without catalog/docs edits or a PR. Never replace a mismatching submitted checksum
219+ with the computed or release-metadata digest. A command that cannot run or read
220+ the archive is Blocked, not a successful comparison.
221+ A blocked or failed download must not count as a passed check; repository/release metadata is not a
157222substitute for fetching the archive. Never execute downloaded content.
158223
159224Do not fetch arbitrary user-provided URLs. Do not claim the artifact was
@@ -203,15 +268,41 @@ should add to Spec Kit.
203268
204269### Validation outcome
205270
206- If any check fails:
271+ Choose exactly one outcome below, in order. A check that could not run is
272+ incomplete, not a passed check or a confirmed submission defect.
273+
274+ #### Blocked
275+
276+ If a permission denial, sandbox/network restriction, timeout, or service outage
277+ prevents a required check, validation is blocked by the workflow environment:
278+ - Comment with the attempted URL, exact error, and workflow run link, asking a
279+ maintainer to investigate and rerun validation.
280+ - Do not ask the submitter to change a URL or resubmit solely
281+ because the workflow could not perform the check.
282+ - Remove ` validation-passed ` . Do not add ` validation-failed ` or ` needs-info ` solely for an
283+ environment blocker. Do not describe unperformed checks as passed.
284+ - If independent submission checks failed, report those separately
285+ and apply ` validation-failed ` for those failures only. An observed HTTP 404
286+ or a checksum mismatch is a submission failure, not a permission failure.
287+ - Stop processing here without editing catalog/docs files or opening a PR.
288+ Do not evaluate the Failed or Passed outcomes below.
289+
290+ #### Failed
291+
292+ If there are no environment blockers and a completed check found a submission defect:
207293
2082941 . Comment once with every failed check and a specific correction.
2092952 . Remove ` validation-passed ` .
2102963 . Add ` validation-failed ` ; add ` needs-info ` when submitter input is needed.
2112974 . Stop without editing files or creating a pull request.
212298
213- If all checks pass, remove ` validation-failed ` and ` needs-info ` , add
214- ` validation-passed ` , and continue.
299+ #### Passed
300+
301+ If there are no environment blockers and every required check completed and passed:
302+ remove ` validation-failed ` and ` needs-info ` , add ` validation-passed ` , and continue.
303+ After successful PR creation, the ` conclusion ` job also applies these
304+ issue labels independently of the agent. It does not run when no PR was created
305+ or safe-output processing failed.
215306
216307## Step 3 - Determine Add or Update
217308
@@ -227,7 +318,15 @@ correction at the same version.
227318## Step 4 - Update the Community Catalog
228319
229320Edit ` bundles/catalog.community.json ` . Insert new entries alphabetically by
230- bundle ID. The entry shape is:
321+ bundle ID.
322+
323+ For both new entries and updates, only after every required validation passes,
324+ set ` sha256 ` to ` actual_sha256 ` from the downloaded archive. Do this even when no
325+ checksum was submitted. Replace any previous catalog digest; do not reuse a digest
326+ from an older archive. A submitted mismatch must fail validation before this step;
327+ writing the computed digest must never be used to bypass that failure.
328+
329+ The entry shape is:
231330
232331``` json
233332{
@@ -240,6 +339,7 @@ bundle ID. The entry shape is:
240339 "author" : " <author>" ,
241340 "license" : " <license>" ,
242341 "download_url" : " <download-url>" ,
342+ "sha256" : " <actual_sha256>" ,
243343 "repository" : " <repository>" ,
244344 "requires" : {
245345 "speckit_version" : " <speckit-version>"
@@ -256,7 +356,7 @@ bundle ID. The entry shape is:
256356}
257357```
258358
259- Use the validated proposed entry rather than inventing metadata. Keep
359+ Use the validated proposed entry for submitted metadata and set ` sha256 ` as above . Keep
260360` verified: false ` . Update the top-level ` updated_at ` to today's UTC date at
261361midnight and preserve the top-level ` catalog_url ` .
262362
0 commit comments