Skip to content

Commit e0659bf

Browse files
feat(035): publish to NuGet.org instead of GitHub Packages
Move Spectra package distribution to nuget.org so install requires zero configuration. Three packages (Core, CLI, MCP) now ship with full nuget.org metadata (Apache-2.0 license, repo URL, tags, embedded README). Version is derived solely from the git tag via -p:PackageVersion=, no hardcoded <Version> in csproj. publish.yml runs tests before packing, pushes to api.nuget.org with NUGET_API_KEY, and uses --skip-duplicate for idempotent re-runs. Bundled deploy-dashboard.yml template drops the GitHub Packages source registration and the GH_PACKAGES_TOKEN secret; install becomes a one-line dotnet tool install. docs/getting-started.md install command casing fixed (spectra -> Spectra.CLI). Legacy docs/deployment/github-packages-setup.md deleted along with the deployment.md reference. All 1453 existing tests pass.
1 parent 6823f0c commit e0659bf

File tree

17 files changed

+950
-96
lines changed

17 files changed

+950
-96
lines changed

.github/workflows/deploy-dashboard.yml.template

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ jobs:
5151
dotnet-version: '8.0'
5252

5353
- name: Install SPECTRA CLI
54-
run: dotnet tool install -g spectra-cli
54+
run: dotnet tool install -g Spectra.CLI
5555

5656
- name: Run coverage analysis with auto-linking
5757
run: spectra ai analyze --coverage --auto-link

.github/workflows/publish.yml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ on:
66

77
permissions:
88
contents: write
9-
packages: write
109

1110
jobs:
1211
publish:
@@ -35,16 +34,19 @@ jobs:
3534
run: dotnet build --configuration Release --no-restore
3635

3736
- name: Test
38-
run: dotnet test --configuration Release --no-restore
37+
run: dotnet test --configuration Release --no-build
38+
39+
- name: Pack Spectra.Core
40+
run: dotnet pack src/Spectra.Core/Spectra.Core.csproj --configuration Release --no-build --output ./nupkg -p:PackageVersion=${{ steps.version.outputs.VERSION }}
3941

4042
- name: Pack Spectra.CLI
41-
run: dotnet pack src/Spectra.CLI/Spectra.CLI.csproj --configuration Release --no-build --output ./nupkg /p:Version=${{ steps.version.outputs.VERSION }}
43+
run: dotnet pack src/Spectra.CLI/Spectra.CLI.csproj --configuration Release --no-build --output ./nupkg -p:PackageVersion=${{ steps.version.outputs.VERSION }}
4244

4345
- name: Pack Spectra.MCP
44-
run: dotnet pack src/Spectra.MCP/Spectra.MCP.csproj --configuration Release --no-build --output ./nupkg /p:Version=${{ steps.version.outputs.VERSION }}
46+
run: dotnet pack src/Spectra.MCP/Spectra.MCP.csproj --configuration Release --no-build --output ./nupkg -p:PackageVersion=${{ steps.version.outputs.VERSION }}
4547

46-
- name: Push to GitHub Packages
47-
run: dotnet nuget push ./nupkg/*.nupkg --api-key ${{ secrets.GITHUB_TOKEN }} --source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" --skip-duplicate
48+
- name: Push to NuGet.org
49+
run: dotnet nuget push ./nupkg/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
4850

4951
- name: Create GitHub Release
5052
uses: softprops/action-gh-release@v2

docs/deployment.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ permalink: /deployment
77

88
# Deployment
99

10-
Guides for deploying the SPECTRA dashboard and configuring external integrations (Cloudflare Pages, GitHub Packages, Copilot Spaces).
10+
Guides for deploying the SPECTRA dashboard and configuring external integrations (Cloudflare Pages, Copilot Spaces).

docs/deployment/github-packages-setup.md

Lines changed: 0 additions & 73 deletions
This file was deleted.

docs/getting-started.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Related: [CLI Reference](cli-reference.md) | [Configuration](configuration.md)
2525
## Install
2626

2727
```bash
28-
dotnet tool install -g spectra
28+
dotnet tool install -g Spectra.CLI
2929
```
3030

3131
## Initialize a Repository
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Specification Quality Checklist: Publish to NuGet.org
2+
3+
**Purpose**: Validate specification completeness and quality before proceeding to planning
4+
**Created**: 2026-04-10
5+
**Feature**: [spec.md](../spec.md)
6+
7+
## Content Quality
8+
9+
- [x] No implementation details (languages, frameworks, APIs)
10+
- [x] Focused on user value and business needs
11+
- [x] Written for non-technical stakeholders
12+
- [x] All mandatory sections completed
13+
14+
## Requirement Completeness
15+
16+
- [x] No [NEEDS CLARIFICATION] markers remain
17+
- [x] Requirements are testable and unambiguous
18+
- [x] Success criteria are measurable
19+
- [x] Success criteria are technology-agnostic (no implementation details)
20+
- [x] All acceptance scenarios are defined
21+
- [x] Edge cases are identified
22+
- [x] Scope is clearly bounded
23+
- [x] Dependencies and assumptions identified
24+
25+
## Feature Readiness
26+
27+
- [x] All functional requirements have clear acceptance criteria
28+
- [x] User scenarios cover primary flows
29+
- [x] Feature meets measurable outcomes defined in Success Criteria
30+
- [x] No implementation details leak into specification
31+
32+
## Notes
33+
34+
- The original `/speckit.specify` input was already implementation-rich (full YAML/XML snippets, file paths, exact commands). The spec deliberately abstracts away from those details and re-states the intent in user/business terms — implementation specifics will return in `/speckit.plan`.
35+
- "Public default .NET package feed" is used in place of "nuget.org" within FRs to keep them tool-agnostic; the Assumptions section names the concrete target.
36+
- Items marked incomplete require spec updates before `/speckit.clarify` or `/speckit.plan`.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Contract: Publish Workflow
2+
3+
**Feature**: 035-nuget-org-publish
4+
**Date**: 2026-04-10
5+
6+
This is the external contract between a maintainer pushing a tag and the resulting state on nuget.org.
7+
8+
## Trigger
9+
10+
| Input | Format | Example |
11+
|-------|--------|---------|
12+
| Git tag push to `AutomateThePlanet/Spectra` | `v<MAJOR>.<MINOR>.<PATCH>` | `v1.36.0` |
13+
14+
The workflow file `.github/workflows/publish.yml` MUST declare:
15+
16+
```yaml
17+
on:
18+
push:
19+
tags: ['v*']
20+
```
21+
22+
## Inputs (provided by environment)
23+
24+
| Input | Source | Required |
25+
|-------|--------|----------|
26+
| `GITHUB_REF` | GitHub Actions runtime | Yes (used to derive version) |
27+
| `secrets.NUGET_API_KEY` | Repository secret | Yes (push step fails without it) |
28+
29+
## Steps (ordered, each MUST succeed for the next to run)
30+
31+
1. Checkout (`actions/checkout@v6`, `fetch-depth: 0`).
32+
2. Setup .NET 8 SDK (`actions/setup-dotnet@v5`).
33+
3. Extract version: `VERSION=${GITHUB_REF#refs/tags/v}`.
34+
4. `dotnet restore`.
35+
5. `dotnet build --configuration Release --no-restore`.
36+
6. `dotnet test --configuration Release --no-build`. **GATE**: failure here aborts the workflow with no packages published.
37+
7. `dotnet pack src/Spectra.Core/Spectra.Core.csproj --configuration Release --no-build --output ./nupkg -p:PackageVersion=${VERSION}`.
38+
8. `dotnet pack src/Spectra.CLI/Spectra.CLI.csproj --configuration Release --no-build --output ./nupkg -p:PackageVersion=${VERSION}`.
39+
9. `dotnet pack src/Spectra.MCP/Spectra.MCP.csproj --configuration Release --no-build --output ./nupkg -p:PackageVersion=${VERSION}`.
40+
10. `dotnet nuget push ./nupkg/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate`.
41+
11. Create GitHub Release (`softprops/action-gh-release@v2`) — preserved from existing workflow, attaches the three `.nupkg` files.
42+
43+
## Outputs (observable post-conditions)
44+
45+
| Output | Location | Verification |
46+
|--------|----------|--------------|
47+
| `Spectra.Core.${VERSION}.nupkg` | `https://www.nuget.org/packages/Spectra.Core/${VERSION}` | Page exists, metadata matches data-model.md |
48+
| `Spectra.CLI.${VERSION}.nupkg` | `https://www.nuget.org/packages/Spectra.CLI/${VERSION}` | Page exists, README rendered, install command works |
49+
| `Spectra.MCP.${VERSION}.nupkg` | `https://www.nuget.org/packages/Spectra.MCP/${VERSION}` | Page exists, README rendered |
50+
| GitHub Release `v${VERSION}` | `https://github.com/AutomateThePlanet/Spectra/releases/tag/v${VERSION}` | Auto-generated notes + 3 attached `.nupkg` |
51+
52+
## Error contract
53+
54+
| Failure | Workflow exit | nuget.org state |
55+
|---------|---------------|-----------------|
56+
| Build fails | Step 5 fails, workflow red | Unchanged |
57+
| Tests fail | Step 6 fails, workflow red | Unchanged |
58+
| Pack fails (e.g., missing README) | Steps 7–9 fail, workflow red | Unchanged |
59+
| Push fails for one package, succeeds for two | Step 10 fails, workflow red | Partial — visible in nuget.org listing; maintainer bumps patch and re-tags |
60+
| Tag re-pushed (version already on nuget.org) | Step 10 succeeds via `--skip-duplicate`, workflow green | Unchanged (existing version preserved) |
61+
| `NUGET_API_KEY` missing/invalid | Step 10 fails with auth error | Unchanged |
62+
63+
## Idempotency contract
64+
65+
Re-running the workflow on a tag whose version is already on nuget.org MUST:
66+
- Produce a green workflow run (`--skip-duplicate` makes the push a no-op).
67+
- Leave the existing published versions byte-identical (no overwrite).
68+
- Re-create / update the GitHub Release if `softprops/action-gh-release@v2` is configured to do so (it is — `tag_name` matches existing).
69+
70+
## Out of contract
71+
72+
- This workflow does not sign packages.
73+
- This workflow does not produce symbol packages (.snupkg).
74+
- This workflow does not push to any feed other than nuget.org.
75+
- This workflow does not bump version files in the repo (version comes from the tag, period).
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Phase 1 — Data Model: Publish to NuGet.org
2+
3+
**Feature**: 035-nuget-org-publish
4+
**Date**: 2026-04-10
5+
6+
This feature has no runtime data model. The "entities" are build/release artifacts and configuration. They are documented here as schemas so the implementation tasks have an unambiguous target shape.
7+
8+
## Entity 1 — Published Package
9+
10+
A `.nupkg` file uploaded to nuget.org. One per publishable Spectra project.
11+
12+
| Field | Source | Spectra.Core | Spectra.CLI | Spectra.MCP |
13+
|-------|--------|--------------|-------------|-------------|
14+
| `PackageId` | `.csproj` | `Spectra.Core` | `Spectra.CLI` | `Spectra.MCP` |
15+
| `Version` | git tag (workflow) | `${VERSION}` | `${VERSION}` | `${VERSION}` |
16+
| `Authors` | `.csproj` | `Anton Angelov` | `Anton Angelov` | `Anton Angelov` |
17+
| `Company` | `.csproj` | `Automate The Planet` | `Automate The Planet` | `Automate The Planet` |
18+
| `Description` | `.csproj` | "Core library for SPECTRA — AI-native test generation framework. Shared models, parsing, validation, and coverage analysis." | "AI-native test generation CLI. Generate test cases from documentation with dual-model grounding verification, coverage analysis, and visual dashboards." | "MCP execution server for SPECTRA. Deterministic AI-orchestrated test execution with state machine, pause/resume, and multi-format reports." |
19+
| `PackageLicenseExpression` | `.csproj` | `Apache-2.0` | `Apache-2.0` | `Apache-2.0` |
20+
| `PackageProjectUrl` | `.csproj` | `https://github.com/AutomateThePlanet/Spectra` | same | same |
21+
| `RepositoryUrl` | `.csproj` | same | same | same |
22+
| `RepositoryType` | `.csproj` | `git` | `git` | `git` |
23+
| `PackageTags` | `.csproj` | `testing;test-generation;ai;mcp;qa;test-automation;spectra` | `testing;test-generation;ai;mcp;copilot;qa;test-automation;spectra;dotnet-tool` | `testing;mcp;test-execution;ai;qa;spectra;dotnet-tool` |
24+
| `PackageReadmeFile` | `.csproj` | `README.md` | `README.md` | `README.md` |
25+
| `PackAsTool` | `.csproj` | _(absent)_ | `true` | `true` |
26+
| `ToolCommandName` | `.csproj` | _(N/A)_ | `spectra` | `spectra-mcp` |
27+
| Embedded README | `<None Include="../../README.md" Pack="true" PackagePath="/" />` | required | required | required |
28+
29+
### Validation rules
30+
31+
- `PackageId` MUST match the casing in README install commands and the `nuget.org` badge URLs.
32+
- `<Version>` element MUST NOT appear in any of the three project files. Version is supplied at pack time via `-p:PackageVersion=`.
33+
- `PackageLicenseExpression` MUST be `Apache-2.0` (matches the `LICENSE` file at repo root). Not `MIT`.
34+
- `PackageReadmeFile` MUST resolve to a file actually included in the .nupkg via the `<None>` item. If the include is missing, `dotnet pack` fails with NU5039.
35+
- All three projects MUST list identical `Authors`, `Company`, `RepositoryUrl`, `PackageProjectUrl`, `RepositoryType`, `PackageLicenseExpression`, `PackageReadmeFile`. Differences allowed only on `PackageId`, `Description`, `PackageTags`, `PackAsTool`, `ToolCommandName`.
36+
37+
## Entity 2 — Release Tag
38+
39+
A git tag matching the pattern `v*` pushed to the `AutomateThePlanet/Spectra` repository.
40+
41+
| Field | Format | Example | Validation |
42+
|-------|--------|---------|------------|
43+
| Raw ref | `refs/tags/v<semver>` | `refs/tags/v1.36.0` | Workflow trigger filter `tags: ['v*']` |
44+
| Stripped version | `<semver>` | `1.36.0` | Computed by `${GITHUB_REF#refs/tags/v}`; passed to `dotnet pack -p:PackageVersion=...` |
45+
46+
### State transitions
47+
48+
```
49+
(no tag) --[git tag v1.36.0 && git push]--> TAG_PUSHED
50+
TAG_PUSHED --[publish.yml runs]--> BUILDING
51+
BUILDING --[tests pass]--> PACKING
52+
BUILDING --[tests fail]--> FAILED (no publish)
53+
PACKING --[3 .nupkg produced]--> PUSHING
54+
PUSHING --[push success]--> PUBLISHED
55+
PUSHING --[version already exists]--> PUBLISHED (--skip-duplicate, idempotent)
56+
PUSHING --[push error (auth/network)]--> FAILED (partial state visible in run log)
57+
```
58+
59+
## Entity 3 — Publish Credential
60+
61+
A repository secret authorizing the workflow to push to nuget.org.
62+
63+
| Field | Value |
64+
|-------|-------|
65+
| Secret name | `NUGET_API_KEY` |
66+
| Scope | nuget.org API key, glob `Spectra.*` |
67+
| Permissions | Push new packages and new versions of existing packages |
68+
| Consumer | `dotnet nuget push --api-key ${{ secrets.NUGET_API_KEY }}` step in `publish.yml` |
69+
| Provisioned by | Repository admin (already done — out of scope for this feature) |
70+
71+
### Decommissioning
72+
73+
The legacy secret `GH_PACKAGES_TOKEN` MUST be removable from the repo secret store after this feature ships. No workflow file may reference it. This is verified post-implementation by grepping the entire `.github/workflows/` and `src/Spectra.CLI/Templates/` directories for the string `GH_PACKAGES_TOKEN` and finding zero matches.
74+
75+
---
76+
77+
**Status**: Phase 1 data model complete.

0 commit comments

Comments
 (0)