mirror of
https://github.com/github/spec-kit.git
synced 2026-07-03 12:28:06 +08:00
* Require preset-usage README with Spec Kit CLI syntax in submissions Tighten the community preset submission workflow so it validates the README referenced by the documentation field rather than merely checking for a root README. The workflow now fails submissions whose linked README lacks a valid 'specify preset add ...' command and flags monorepo submissions that point documentation at a generic root README. - Add a required Documentation URL field to the preset issue template - Add validation step 2d (documentation README + CLI-syntax check) to .github/workflows/add-community-preset.md and recompile the lock file - Document the stricter usage-README requirement and reviewer content check in presets/PUBLISHING.md Closes #3103 Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Align preset README docs with workflow's actual enforcement Address PR review feedback on #3104: - PUBLISHING.md: clarify that only README resolution + a valid 'specify preset add ...' command are mechanically enforced; the preset-scoped-README and minimum-structure items are reviewer expectations, not automated checks. - PUBLISHING.md: state that a missing 'specify preset add ...' command is a hard validation failure (check 2d), not just 'flagged for changes'. - preset_submission.yml: require 'specify preset add ...' (not the looser 'specify preset ...') to match the workflow validation. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Tighten preset README validation and docs per PR review Address PR review feedback on #3104: - Workflow Step 2c: drop the generic repo-root README.md check so the README requirement is enforced exactly once, in Step 2d, against the file the documentation field points to (avoids monorepo false-positive). - Workflow Step 2d: restrict the documentation URL to GitHub-hosted README URLs (github.com/.../blob/... or raw.githubusercontent.com/...) before fetching user-provided input. - PUBLISHING.md: add the required 'id' field to the example catalog entry. - preset_submission.yml: fix the Documentation URL placeholder to match the recommended monorepo presets/<id>/README.md pattern. - Recompile add-community-preset.lock.yml (body hash only). Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Refine preset README validation rules per PR review Address PR review feedback on #3104: - Workflow Step 2d: broaden the documentation URL allowlist to also accept github.com/.../raw/... URLs; strip any fragment/query before fetching so the target is deterministic; clarify that a 'specify preset add --from <url>' command only counts when its URL matches the submitted Download URL (a different --from URL does not satisfy the requirement, though other accepted forms still can). - PUBLISHING.md: show both accepted download URL shapes (tag archive and release asset) in the README install example instead of implying only the releases/download form. - preset_submission.yml: remove the ambiguous generic 'README.md with description and usage instructions' checkbox; the linked-README requirement is the single source of truth. - Recompile add-community-preset.lock.yml (body hash only). Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Clarify install-command requirement wording per PR review Address PR review feedback on #3104: the previous 'matching the download URL' wording overstated the requirement. Only the 'specify preset add --from <url>' form needs an exact download-URL match; other accepted forms ('specify preset add <id>' / '--dev <path>') don't reference the download URL at all. - preset_submission.yml: reword the Documentation URL description and the Submission Requirements checkbox to reflect what's enforced vs preferred. - PUBLISHING.md: clarify the reviewer note so the exact-match rule is scoped to the --from form. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Require README.md target and fix release-ZIP wording per PR review Address PR review feedback on #3104: - Workflow Step 2d: add an explicit check that the documentation URL path ends with README.md (case-insensitive) after stripping fragment/query, so a non-README markdown file is rejected before fetching. - PUBLISHING.md: reword the release-ZIP note, which conflicted with the earlier preset structure guidance. The real requirement is that the README is reachable at the documentation URL before download; it's fine for the same file to also ship inside the release ZIP. - Recompile add-community-preset.lock.yml (body hash only). Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use stable unnumbered anchor for Usage README Requirements Address PR review feedback on #3104: drop the '6.' prefix from the 'Usage README Requirements' heading so its GitHub anchor isn't tied to a section number (brittle under renumbering, and avoids confusion with the top-level 'Best Practices' TOC item). Update the Prerequisites cross-link to the new #usage-readme-requirements anchor. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Align README requirement wording with enforced checks per PR review Address PR review feedback on #3104: - PUBLISHING.md: the 'mechanically enforces' summary now lists all Step 2d checks (GitHub-hosted URL, path ends with README.md, resolves, contains a valid 'specify preset add ...' command), instead of only two. - PUBLISHING.md: reword the PR checklist item so a usage README + install command is the requirement, with preset-scoped README recommended for monorepos (matches the workflow's flag-not-fail behavior). - preset_submission.yml: include the full 'specify preset add' prefix on the --dev and --from forms in the field description and checklist so submitters copy the exact syntax. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix grammar in Usage README Requirements intro Address PR review feedback on #3104: remove the incorrect colon after 'the linked README' so the sentence reads naturally. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Avoid lossy raw URL rewrite for slash-containing refs per PR review Address PR review feedback on #3104: rewriting documentation URLs into the raw.githubusercontent.com/<owner>/<repo>/<ref>/<path> form can't reliably represent refs that contain slashes (e.g. a feature/foo branch). Step 2d now fetches github.com blob URLs by swapping only /blob/ -> /raw/, and fetches github.com/.../raw/... and raw.githubusercontent.com/... URLs as-is, instead of reconstructing the raw host form. Recompile add-community-preset.lock.yml (body hash only). Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
338 lines
12 KiB
Markdown
338 lines
12 KiB
Markdown
---
|
|
description: "Process community preset submission issues — validate, add to catalog, and open a PR for maintainer review"
|
|
emoji: "🎨"
|
|
|
|
on:
|
|
issues:
|
|
types: [labeled]
|
|
names: [preset-submission]
|
|
skip-bots: [github-actions, copilot, dependabot]
|
|
|
|
tools:
|
|
edit:
|
|
bash: ["echo", "cat", "head", "tail", "grep", "wc", "sort", "python3", "jq", "date"]
|
|
github:
|
|
toolsets: [issues, repos]
|
|
min-integrity: none
|
|
web-fetch:
|
|
|
|
permissions:
|
|
contents: read
|
|
issues: read
|
|
|
|
checkout:
|
|
fetch-depth: 0
|
|
|
|
safe-outputs:
|
|
noop:
|
|
report-as-issue: false
|
|
create-pull-request:
|
|
title-prefix: "[preset] "
|
|
labels: [preset-submission, automated]
|
|
draft: true
|
|
max: 1
|
|
protected-files:
|
|
policy: blocked
|
|
exclude:
|
|
- README.md
|
|
- CHANGELOG.md
|
|
add-comment:
|
|
max: 2
|
|
add-labels:
|
|
allowed: [preset-submission, validation-passed, validation-failed, needs-info]
|
|
max: 3
|
|
---
|
|
|
|
# Add Community Preset from Issue Submission
|
|
|
|
You are a catalog maintenance agent for the Spec Kit project. Your job is to
|
|
process community preset submission issues and create pull requests that add
|
|
or update entries in the community preset catalog.
|
|
|
|
## Triggering Conditions
|
|
|
|
This workflow is triggered by any `issues: labeled` event, but a job-level
|
|
condition gates the agent run so it only proceeds when the label that was just
|
|
added is `preset-submission`. By the time you run, that condition has already
|
|
passed. Before processing, verify that the issue title starts with `[Preset]:`.
|
|
If it does not, stop without commenting.
|
|
|
|
## Step 1 — Read and Parse the Issue
|
|
|
|
Read issue #${{ github.event.issue.number }}.
|
|
|
|
Extract the following fields from the structured issue body (GitHub issue form
|
|
fields):
|
|
|
|
| Field | Issue Form ID | Required |
|
|
|-------|--------------|----------|
|
|
| Preset ID | `preset-id` | Yes |
|
|
| Preset Name | `preset-name` | Yes |
|
|
| Version | `version` | Yes |
|
|
| Description | `description` | Yes |
|
|
| Author | `author` | Yes |
|
|
| Repository URL | `repository` | Yes |
|
|
| Download URL | `download-url` | Yes |
|
|
| Documentation URL | `documentation` | Yes |
|
|
| License | `license` | Yes |
|
|
| Required Spec Kit Version | `speckit-version` | Yes |
|
|
| Required Extensions | `required-extensions` | No |
|
|
| Templates Provided | `templates-provided` | Yes |
|
|
| Commands Provided | `commands-provided` | Yes |
|
|
| Number of Scripts | `scripts-count` | No (default 0) |
|
|
| Tags | `tags` | Yes |
|
|
|
|
The issue body uses GitHub's issue form format. Each field appears under a
|
|
heading matching the field label (e.g., `### Preset ID` followed by the
|
|
value). Parse accordingly.
|
|
|
|
## Step 2 — Validate the Submission
|
|
|
|
Run **all** of the following validation checks. Collect all results before
|
|
deciding pass/fail:
|
|
|
|
### 2a. Preset ID format
|
|
- Must match regex: `^[a-z][a-z0-9-]*$`
|
|
- Must be lowercase with hyphens only
|
|
|
|
### 2b. Version format
|
|
- Must follow semver: `X.Y.Z` (digits only, no `v` prefix)
|
|
|
|
### 2c. Repository validation
|
|
- Fetch the repository URL — confirm it exists and is publicly accessible
|
|
- Confirm the repository contains a `preset.yml` file
|
|
- Confirm the repository contains a `LICENSE` file
|
|
|
|
> The README requirement is enforced once, in **Step 2d**, against the specific file the
|
|
> `documentation` field points to — not a generic repository-root `README.md`. This avoids
|
|
> the monorepo false-positive where a root README exists but isn't the preset-usage doc.
|
|
|
|
### 2d. Documentation README validation
|
|
|
|
The `documentation` field must point to the README that explains **how to use this
|
|
preset** — not just any file named `README.md`, and not a product/framework pitch.
|
|
|
|
- **Restrict the URL to GitHub before fetching.** The `documentation` value is
|
|
user-provided input. Only accept GitHub-hosted README URLs:
|
|
- `https://github.com/<owner>/<repo>/blob/<ref>/<path>`
|
|
- `https://github.com/<owner>/<repo>/raw/<ref>/<path>`
|
|
- `https://raw.githubusercontent.com/<owner>/<repo>/<ref>/<path>`
|
|
|
|
If the URL points anywhere else (or isn't a URL), **fail this check** and do not fetch it.
|
|
- **Require the URL to point at a README file.** After stripping any fragment/query (see
|
|
below), the URL path must end with `README.md` (case-insensitive). If it points at some
|
|
other Markdown file, **fail this check** and ask the submitter to link the preset's README.
|
|
- Fetch the **exact URL** in the `documentation` field. First strip any fragment (`#...`)
|
|
or query string (`?...`) — these are common when copying from the browser UI and must be
|
|
ignored so the fetch target is deterministic. Then resolve the raw content to fetch:
|
|
- For a `github.com/<owner>/<repo>/blob/<ref>/<path>` URL, fetch the equivalent
|
|
`github.com/<owner>/<repo>/raw/<ref>/<path>` URL (only swap `/blob/` → `/raw/`).
|
|
- Fetch `github.com/.../raw/...` and `raw.githubusercontent.com/...` URLs as-is.
|
|
|
|
Do **not** rewrite into `raw.githubusercontent.com/<owner>/<repo>/<ref>/<path>` form — that
|
|
format can't reliably represent refs containing slashes (e.g. a `feature/foo` branch).
|
|
Confirm the fetched URL resolves to a readable Markdown file.
|
|
- **Validate that the README contains a valid Spec Kit CLI install command.** The fetched
|
|
README must contain at least one `specify preset add ...` invocation. The strongest
|
|
signal is the catalog-install form whose URL matches the submitted **Download URL**:
|
|
- `specify preset add --from <download-url>` (preferred), or
|
|
- `specify preset add <preset-id>`, or
|
|
- `specify preset add --dev <path>`
|
|
|
|
A `specify preset add --from <url>` command only counts when its `<url>` **matches the
|
|
submitted Download URL exactly**. A `--from` command pointing at a *different* URL does
|
|
**not** satisfy the install-command requirement (treat it as if absent) — but the README
|
|
may still pass on one of the other accepted forms (`specify preset add <preset-id>` or
|
|
`specify preset add --dev <path>`).
|
|
|
|
If **no** accepted `specify preset add ...` command is present, the README is treated as a
|
|
generic description/pitch rather than preset-usage documentation — **fail this check** and
|
|
tell the submitter to add a valid install command (ideally
|
|
`specify preset add --from <download-url>`).
|
|
- **Prefer a preset-scoped README in monorepos.** If `documentation` resolves to a generic
|
|
repository-root README in a monorepo (the preset lives in a subdirectory such as
|
|
`presets/<id>/` and a preset-scoped README exists there), **flag it** in your comment and
|
|
recommend the submitter point `documentation` at the preset-scoped README
|
|
(e.g. `presets/<id>/README.md`) so the catalog surfaces usage instead of marketing. Treat
|
|
this as a flag rather than a hard failure **only if** the root README still contains a valid
|
|
`specify preset add ...` command for this preset; otherwise it fails check 2d above.
|
|
|
|
### 2e. Release and download URL validation
|
|
- The download URL should follow the pattern
|
|
`https://github.com/<owner>/<repo>/archive/refs/tags/v<version>.zip`
|
|
or
|
|
`https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>.zip`
|
|
- Verify a GitHub release exists matching the submitted version
|
|
|
|
### 2f. Submission checklists
|
|
- Confirm that all required checkboxes in the Testing Checklist and Submission
|
|
Requirements sections are checked (`[x]`)
|
|
|
|
### Validation outcome
|
|
|
|
If **any** validation fails:
|
|
1. Add a comment on the issue listing each failed check with a clear explanation
|
|
of what's wrong and how to fix it
|
|
2. Add the `validation-failed` label
|
|
3. **Stop — do not proceed further**
|
|
|
|
If all validations pass:
|
|
1. Add the `validation-passed` label
|
|
2. Continue to Step 3
|
|
|
|
## Step 3 — Determine Add vs Update
|
|
|
|
Search `presets/catalog.community.json` for the preset ID.
|
|
|
|
- **Not found** → this is a **new addition**
|
|
- **Found** → this is an **update** — replace the existing entry in-place;
|
|
preserve `created_at` from the existing entry
|
|
|
|
## Step 4 — Update `presets/catalog.community.json`
|
|
|
|
Edit `presets/catalog.community.json` to add or update the preset entry.
|
|
|
|
### For a new preset
|
|
|
|
Insert the entry in **alphabetical order by preset ID** within the
|
|
`"presets"` object. Use this structure:
|
|
|
|
```json
|
|
{
|
|
"<id>": {
|
|
"name": "<name>",
|
|
"id": "<id>",
|
|
"version": "<version>",
|
|
"description": "<description>",
|
|
"author": "<author>",
|
|
"repository": "<repository>",
|
|
"download_url": "<download_url>",
|
|
"homepage": "<homepage or repository>",
|
|
"documentation": "<documentation URL — the validated preset-usage README>",
|
|
"license": "<license>",
|
|
"requires": {
|
|
"speckit_version": "<speckit_version>"
|
|
},
|
|
"provides": {
|
|
"templates": <N>,
|
|
"commands": <N>
|
|
},
|
|
"tags": ["<tag1>", "<tag2>"],
|
|
"created_at": "<today>T00:00:00Z",
|
|
"updated_at": "<today>T00:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
If the preset has required extensions, add an `"extensions"` array inside
|
|
`"requires"`:
|
|
|
|
```json
|
|
"requires": {
|
|
"speckit_version": "<speckit_version>",
|
|
"extensions": ["<extension-id>"]
|
|
}
|
|
```
|
|
|
|
If the preset provides scripts, add `"scripts": <N>` inside `"provides"`.
|
|
|
|
### For an update
|
|
|
|
Replace only the changed fields (typically `version`, `download_url`,
|
|
`description`, `provides`, `requires`, `tags`, `updated_at`). **Preserve**
|
|
`created_at` from the existing entry.
|
|
|
|
### Counting templates and commands
|
|
|
|
Parse the "Templates Provided" and "Commands Provided" issue fields:
|
|
- Count the number of list items (lines starting with `-`)
|
|
- If the field says "None", the count is 0
|
|
|
|
### After editing
|
|
|
|
Update the **top-level `"updated_at"` timestamp** in the catalog to today's date
|
|
in ISO 8601 format.
|
|
|
|
Validate the JSON by running:
|
|
|
|
```bash
|
|
python3 -c "import json; json.load(open('presets/catalog.community.json')); print('Valid JSON')"
|
|
```
|
|
|
|
If validation fails, fix the JSON and re-validate before continuing.
|
|
|
|
## Step 5 — Update `docs/community/presets.md`
|
|
|
|
Edit `docs/community/presets.md` to add or update a row in the Community
|
|
Presets table.
|
|
|
|
### For a new preset
|
|
|
|
Insert a new row in **alphabetical order by preset name**:
|
|
|
|
```
|
|
| <Name> | <Description> | <N> templates, <N> commands | <Requires> | [<repo-name>](<repository-url>) |
|
|
```
|
|
|
|
For the Requires column:
|
|
- Use `—` if no extensions are required
|
|
- List required extension names if any (e.g., `AIDE extension`)
|
|
|
|
If the preset provides scripts, include them: `<N> templates, <N> commands, <N> scripts`
|
|
|
|
### For an update
|
|
|
|
Find the existing row and update any changed fields in-place.
|
|
|
|
## Step 6 — Create Pull Request
|
|
|
|
Create a pull request with the changes. Use this branch naming convention:
|
|
|
|
- **New preset:** `add-<preset-id>-preset`
|
|
- **Update:** `update-<preset-id>-preset`
|
|
|
|
### Commit message
|
|
|
|
For a new preset:
|
|
```
|
|
Add <Name> preset to community catalog
|
|
|
|
Add <id> preset submitted by @<issue-author> to:
|
|
- presets/catalog.community.json (alphabetical order)
|
|
- docs/community/presets.md community presets table
|
|
|
|
Closes #<issue-number>
|
|
```
|
|
|
|
For an update:
|
|
```
|
|
Update <Name> preset to v<version>
|
|
|
|
Update <id> preset submitted by @<issue-author>:
|
|
- presets/catalog.community.json (version, download_url, etc.)
|
|
- docs/community/presets.md community presets table
|
|
|
|
Closes #<issue-number>
|
|
```
|
|
|
|
### PR description
|
|
|
|
Include:
|
|
- A summary of what changed
|
|
- Validation results (all checks passed)
|
|
- `Closes #${{ github.event.issue.number }}`
|
|
- `cc @<issue-author>` — mention the submitter
|
|
|
|
## Important Rules
|
|
|
|
- **Alphabetical order matters** — entries must be sorted by ID in the JSON and
|
|
by name in the docs table
|
|
- **Always validate JSON** after editing — a trailing comma or missing brace
|
|
will break the catalog
|
|
- **Use `Closes` not `Fixes`** — `Closes #N` is the correct keyword for
|
|
submission issues
|
|
- **Preserve `created_at` on updates** — keep the original value; only update
|
|
`updated_at`
|
|
- **Do not modify any other files** — only `presets/catalog.community.json`
|
|
and `docs/community/presets.md`
|