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`
|