mirror of
https://github.com/github/spec-kit.git
synced 2026-07-03 12:28:06 +08:00
* feat(extensions): verify catalog archive sha256 before install Extension and preset archives were downloaded over HTTPS and unpacked (with Zip-Slip protection) but their bytes were never checked against a known digest. Trust rested entirely on TLS and the integrity of the release host, so a tampered or swapped archive from a compromised third-party release would be installed silently. Maintainers do not audit extension code, so consumer-side integrity is the only available defence. Catalog entries may now pin an optional `sha256` digest. When present, the downloaded archive is verified before it is written to disk and installed; a mismatch aborts with a clear error. Entries without `sha256` keep working unchanged (a DEBUG line records that the download was unverified), so the change is backwards compatible. The check runs on both download paths (extensions and presets) via a single shared helper so the two stay in parity. - Add `verify_archive_sha256` helper in shared_infra (digest match, `sha256:` prefix, case-insensitive; DEBUG log when no digest declared) - Enforce it in ExtensionCatalog.download_extension and PresetCatalog.download_pack, before the archive is written to disk - Document the optional `sha256` field in the publishing guides - Tests: helper unit tests + matching/mismatch/no-digest on both paths Signed-off-by: Zied Jlassi <6190550+zied-jlassi@users.noreply.github.com> Assisted-by: AI * fix(extensions): harden sha256 parsing and tidy download test mocks Follow-up to the review on #3080: - shared_infra.verify_archive_sha256: strip only a literal `sha256:` algorithm prefix (case-insensitive) instead of `split(':', 1)[-1]`, which silently dropped any prefix — so `md5:<64-hex>` was accepted as if it were a valid SHA-256. Validate that the declared value is exactly 64 hex characters and raise a clear error otherwise, and compare with `hmac.compare_digest` for a constant-time check. Add tests covering a malformed digest and a non-`sha256:` prefix (both previously accepted). - Download test helpers: configure the context-manager mock via `__enter__.return_value`/`__exit__.return_value` rather than assigning a `lambda s: s`, which is clearer and independent of the invocation arity. Assisted-by: AI Signed-off-by: Zied Jlassi (Architect AI) <6190550+zied-jlassi@users.noreply.github.com> * fix(extensions): reject a declared-but-empty sha256 instead of skipping verification verify_archive_sha256 skipped on any falsy expected value, so a present-but-empty digest (e.g. sha256: "" reached via ...get("sha256")) silently disabled the integrity check instead of surfacing the authoring error. Guard on expected is None so only an absent digest skips; blank/whitespace/bare-prefix values fall through to the 64-hex validation and are rejected. Adds a regression test. Signed-off-by: Zied Jlassi <6190550+zied-jlassi@users.noreply.github.com> * docs(shared_infra): clarify _SHA256_HEX_RE accepts and normalizes uppercase The comment described the regex as matching '64 lowercase' hex characters, but verify_archive_sha256 lowercases the declared value (raw.lower()) before matching, so an uppercase digest is accepted and normalized rather than rejected. Clarify the comment to avoid misleading future readers. Addresses Copilot review feedback on shared_infra.py. Signed-off-by: Zied Jlassi <6190550+zied-jlassi@users.noreply.github.com> * test(presets): cover the no-sha256 backwards-compatible path Address Copilot review: download_pack's optional sha256 verification was tested for match/mismatch but not the backwards-compatible path where a catalog entry has no sha256 (pack_info.get("sha256") is None). Add a no-sha256 test mirroring the extensions coverage so the helper never silently becomes mandatory for presets. Signed-off-by: Zied Jlassi <6190550+zied-jlassi@users.noreply.github.com> --------- Signed-off-by: Zied Jlassi <6190550+zied-jlassi@users.noreply.github.com> Signed-off-by: Zied Jlassi (Architect AI) <6190550+zied-jlassi@users.noreply.github.com>
367 lines
11 KiB
Markdown
367 lines
11 KiB
Markdown
# Extension Publishing Guide
|
|
|
|
This guide explains how to publish your extension to the Spec Kit extension catalog, making it discoverable by `specify extension search`.
|
|
|
|
## Table of Contents
|
|
|
|
1. [Prerequisites](#prerequisites)
|
|
2. [Prepare Your Extension](#prepare-your-extension)
|
|
3. [Submit to Catalog](#submit-to-catalog)
|
|
4. [Release Workflow](#release-workflow)
|
|
5. [Best Practices](#best-practices)
|
|
|
|
---
|
|
|
|
## Prerequisites
|
|
|
|
Before publishing an extension, ensure you have:
|
|
|
|
1. **Valid Extension**: A working extension with a valid `extension.yml` manifest
|
|
2. **Git Repository**: Extension hosted on GitHub (or other public git hosting)
|
|
3. **Documentation**: README.md with installation and usage instructions
|
|
4. **License**: Open source license file (MIT, Apache 2.0, etc.)
|
|
5. **Versioning**: Semantic versioning (e.g., 1.0.0)
|
|
6. **Testing**: Extension tested on real projects
|
|
|
|
---
|
|
|
|
## Prepare Your Extension
|
|
|
|
### 1. Extension Structure
|
|
|
|
Ensure your extension follows the standard structure:
|
|
|
|
```text
|
|
your-extension/
|
|
├── extension.yml # Required: Extension manifest
|
|
├── README.md # Required: Documentation
|
|
├── LICENSE # Required: License file
|
|
├── CHANGELOG.md # Recommended: Version history
|
|
├── .gitignore # Recommended: Git ignore rules
|
|
│
|
|
├── commands/ # Extension commands
|
|
│ ├── command1.md
|
|
│ └── command2.md
|
|
│
|
|
├── config-template.yml # Config template (if needed)
|
|
│
|
|
└── docs/ # Additional documentation
|
|
├── usage.md
|
|
└── examples/
|
|
```
|
|
|
|
### 2. extension.yml Validation
|
|
|
|
Verify your manifest is valid:
|
|
|
|
```yaml
|
|
schema_version: "1.0"
|
|
|
|
extension:
|
|
id: "your-extension" # Unique lowercase-hyphenated ID
|
|
name: "Your Extension Name" # Human-readable name
|
|
version: "1.0.0" # Semantic version
|
|
description: "Brief description (one sentence)"
|
|
author: "Your Name or Organization"
|
|
repository: "https://github.com/your-org/spec-kit-your-extension"
|
|
license: "MIT"
|
|
homepage: "https://github.com/your-org/spec-kit-your-extension"
|
|
|
|
requires:
|
|
speckit_version: ">=0.1.0" # Required spec-kit version
|
|
|
|
provides:
|
|
commands: # List all commands
|
|
- name: "speckit.your-extension.command"
|
|
file: "commands/command.md"
|
|
description: "Command description"
|
|
|
|
tags: # 2-5 relevant tags
|
|
- "category"
|
|
- "tool-name"
|
|
```
|
|
|
|
**Validation Checklist**:
|
|
|
|
- ✅ `id` is lowercase with hyphens only (no underscores, spaces, or special characters)
|
|
- ✅ `version` follows semantic versioning (X.Y.Z)
|
|
- ✅ `description` is concise (under 100 characters)
|
|
- ✅ `repository` URL is valid and public
|
|
- ✅ All command files exist in the extension directory
|
|
- ✅ Tags are lowercase and descriptive
|
|
|
|
### 3. Create GitHub Release
|
|
|
|
Create a GitHub release for your extension version:
|
|
|
|
```bash
|
|
# Tag the release
|
|
git tag v1.0.0
|
|
git push origin v1.0.0
|
|
|
|
# Create release on GitHub
|
|
# Go to: https://github.com/your-org/spec-kit-your-extension/releases/new
|
|
# - Tag: v1.0.0
|
|
# - Title: v1.0.0 - Release Name
|
|
# - Description: Changelog/release notes
|
|
```
|
|
|
|
The release archive URL will be:
|
|
|
|
```text
|
|
https://github.com/your-org/spec-kit-your-extension/archive/refs/tags/v1.0.0.zip
|
|
```
|
|
|
|
### 4. Test Installation
|
|
|
|
Test that users can install from your release:
|
|
|
|
```bash
|
|
# Test dev installation
|
|
specify extension add --dev /path/to/your-extension
|
|
|
|
# Test from GitHub archive
|
|
specify extension add <extension-name> --from https://github.com/your-org/spec-kit-your-extension/archive/refs/tags/v1.0.0.zip
|
|
```
|
|
|
|
---
|
|
|
|
## Submit to Catalog
|
|
|
|
### Understanding the Catalogs
|
|
|
|
Spec Kit uses a dual-catalog system. For details about how catalogs work, see the main [Extensions README](README.md#extension-catalogs).
|
|
|
|
**For extension publishing**: All community extensions are listed in `extensions/catalog.community.json`. Users browse this catalog and copy extensions they trust into their own `catalog.json`.
|
|
|
|
### How to Submit
|
|
|
|
To submit your extension to the community catalog, file a new issue using the **[Extension Submission](https://github.com/github/spec-kit/issues/new?template=extension_submission.yml)** template. The template collects all required metadata, including:
|
|
|
|
- Extension ID, name, and version
|
|
- Description, author, and license
|
|
- Repository, download URL, and documentation links
|
|
- Required Spec Kit version and any tool dependencies
|
|
- Number of commands and hooks
|
|
- Tags and key features
|
|
- Testing confirmation
|
|
|
|
> [!IMPORTANT]
|
|
> Do **not** open a pull request directly to edit `extensions/catalog.community.json`. All community extension submissions must go through the issue template so a maintainer can review the entry and update the catalog.
|
|
|
|
### What Happens After You Submit
|
|
|
|
1. Your issue is automatically labeled and assigned to a maintainer for review
|
|
2. A maintainer verifies that the catalog entry is complete and correctly formatted
|
|
3. Once approved, the maintainer adds your extension to `extensions/catalog.community.json` and the Community Extensions table in the README
|
|
4. Your extension becomes discoverable via `specify extension search`
|
|
|
|
### What Maintainers Check
|
|
|
|
- The catalog entry fields are complete and correctly formatted
|
|
- The download URL is accessible
|
|
- The repository exists and contains an `extension.yml` manifest
|
|
|
|
> [!NOTE]
|
|
> Maintainers do **not** review, audit, or test the extension code itself.
|
|
|
|
### Typical Review Timeline
|
|
|
|
- **Review**: 3-7 business days
|
|
|
|
### Updating an Existing Extension
|
|
|
|
To update an extension that is already in the catalog (e.g., for a new version), file a new **[Extension Submission](https://github.com/github/spec-kit/issues/new?template=extension_submission.yml)** issue with the updated version, download URL, and any other changed fields. Mention in the issue that this is an update to an existing entry.
|
|
|
|
---
|
|
|
|
## Release Workflow
|
|
|
|
### Publishing New Versions
|
|
|
|
When releasing a new version:
|
|
|
|
1. **Update version** in `extension.yml`:
|
|
|
|
```yaml
|
|
extension:
|
|
version: "1.1.0" # Updated version
|
|
```
|
|
|
|
2. **Update CHANGELOG.md**:
|
|
|
|
```markdown
|
|
## [1.1.0] - 2026-02-15
|
|
|
|
### Added
|
|
- New feature X
|
|
|
|
### Fixed
|
|
- Bug fix Y
|
|
```
|
|
|
|
3. **Create GitHub release**:
|
|
|
|
```bash
|
|
git tag v1.1.0
|
|
git push origin v1.1.0
|
|
# Create release on GitHub
|
|
```
|
|
|
|
4. **File an update submission** using the [Extension Submission](https://github.com/github/spec-kit/issues/new?template=extension_submission.yml) template with the new version and download URL. Mention in the issue that this is an update to an existing entry.
|
|
|
|
---
|
|
|
|
## Best Practices
|
|
|
|
### Extension Design
|
|
|
|
1. **Single Responsibility**: Each extension should focus on one tool/integration
|
|
2. **Clear Naming**: Use descriptive, unambiguous names
|
|
3. **Minimal Dependencies**: Avoid unnecessary dependencies
|
|
4. **Backward Compatibility**: Follow semantic versioning strictly
|
|
|
|
### Documentation
|
|
|
|
1. **README.md Structure**:
|
|
- Overview and features
|
|
- Installation instructions
|
|
- Configuration guide
|
|
- Usage examples
|
|
- Troubleshooting
|
|
- Contributing guidelines
|
|
|
|
2. **Command Documentation**:
|
|
- Clear description
|
|
- Prerequisites listed
|
|
- Step-by-step instructions
|
|
- Error handling guidance
|
|
- Examples
|
|
|
|
3. **Configuration**:
|
|
- Provide template file
|
|
- Document all options
|
|
- Include examples
|
|
- Explain defaults
|
|
|
|
### Security
|
|
|
|
1. **Input Validation**: Validate all user inputs
|
|
2. **No Hardcoded Secrets**: Never include credentials
|
|
3. **Safe Dependencies**: Only use trusted dependencies
|
|
4. **Audit Regularly**: Check for vulnerabilities
|
|
|
|
### Maintenance
|
|
|
|
1. **Respond to Issues**: Address issues within 1-2 weeks
|
|
2. **Regular Updates**: Keep dependencies updated
|
|
3. **Changelog**: Maintain detailed changelog
|
|
4. **Deprecation**: Give advance notice for breaking changes
|
|
|
|
### Community
|
|
|
|
1. **License**: Use permissive open-source license (MIT, Apache 2.0)
|
|
2. **Contributing**: Welcome contributions
|
|
3. **Code of Conduct**: Be respectful and inclusive
|
|
4. **Support**: Provide ways to get help (issues, discussions, email)
|
|
|
|
---
|
|
|
|
## FAQ
|
|
|
|
### Q: Can I publish private/proprietary extensions?
|
|
|
|
A: The main catalog is for public extensions only. For private extensions:
|
|
|
|
- Host your own catalog.json file
|
|
- Users add your catalog: `specify extension add-catalog https://your-domain.com/catalog.json`
|
|
- Not yet implemented - coming in Phase 4
|
|
|
|
### Q: How long does review take?
|
|
|
|
A: Typically 3-7 business days. Updates to existing extensions are usually faster.
|
|
|
|
### Q: What if my extension is rejected?
|
|
|
|
A: You'll receive feedback on what needs to be fixed. Make the changes and resubmit.
|
|
|
|
### Q: Can I update my extension anytime?
|
|
|
|
A: Yes, file a new [Extension Submission](https://github.com/github/spec-kit/issues/new?template=extension_submission.yml) issue with the updated version and download URL. Mention that it is an update to an existing entry.
|
|
|
|
### Q: Do I need to be verified to be in the catalog?
|
|
|
|
A: No. All community extensions are listed in the catalog once their submission is reviewed and accepted.
|
|
|
|
### Q: Can extensions have paid features?
|
|
|
|
A: Extensions should be free and open-source. Commercial support/services are allowed, but core functionality must be free.
|
|
|
|
---
|
|
|
|
## Support
|
|
|
|
- **Catalog Issues**: <https://github.com/statsperform/spec-kit/issues>
|
|
- **Extension Template**: <https://github.com/statsperform/spec-kit-extension-template> (coming soon)
|
|
- **Development Guide**: See EXTENSION-DEVELOPMENT-GUIDE.md
|
|
- **Community**: Discussions and Q&A
|
|
|
|
---
|
|
|
|
## Appendix: Catalog Schema
|
|
|
|
### Complete Catalog Entry Schema
|
|
|
|
```json
|
|
{
|
|
"name": "string (required)",
|
|
"id": "string (required, unique)",
|
|
"description": "string (required, <200 chars)",
|
|
"author": "string (required)",
|
|
"version": "string (required, semver)",
|
|
"download_url": "string (required, valid URL)",
|
|
"sha256": "string (optional, SHA-256 hex digest of the archive at download_url; verified before install)",
|
|
"repository": "string (required, valid URL)",
|
|
"homepage": "string (optional, valid URL)",
|
|
"documentation": "string (optional, valid URL)",
|
|
"changelog": "string (optional, valid URL)",
|
|
"license": "string (required)",
|
|
"requires": {
|
|
"speckit_version": "string (required, version specifier)",
|
|
"tools": [
|
|
{
|
|
"name": "string (required)",
|
|
"version": "string (optional, version specifier)",
|
|
"required": "boolean (default: false)"
|
|
}
|
|
]
|
|
},
|
|
"provides": {
|
|
"commands": "integer (optional)",
|
|
"hooks": "integer (optional)"
|
|
},
|
|
"tags": ["array of strings (2-10 tags)"],
|
|
"verified": "boolean (default: false, set by maintainers)",
|
|
"downloads": "integer (auto-updated)",
|
|
"stars": "integer (auto-updated)",
|
|
"created_at": "string (ISO 8601 datetime)",
|
|
"updated_at": "string (ISO 8601 datetime)"
|
|
}
|
|
```
|
|
|
|
### Valid Tags
|
|
|
|
Recommended tag categories:
|
|
|
|
- **Integration**: jira, linear, github, gitlab, azure-devops
|
|
- **Category**: issue-tracking, vcs, ci-cd, documentation, testing
|
|
- **Platform**: atlassian, microsoft, google
|
|
- **Feature**: automation, reporting, deployment, monitoring
|
|
|
|
Use 2-5 tags that best describe your extension.
|
|
|
|
---
|
|
|
|
*Last Updated: 2026-01-28*
|
|
*Catalog Format Version: 1.0*
|