Skip to content

Commit 0f77711

Browse files
authored
Merge pull request #20 from shellui-dev/chore/release-0.1.0-alpha
chore: release 0.1.0-alpha : Trusted Publishing workflow, CHANGELOG, pack dry-run tooling
2 parents 5537370 + f3e3a6c commit 0f77711

6 files changed

Lines changed: 404 additions & 1 deletion

File tree

.github/workflows/release.yml

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
name: Release
2+
3+
# Fires on any v*.*.* tag push (and prerelease suffixes -alpha/-beta/-rc).
4+
# Version comes from Directory.Build.props. Publishes via NuGet Trusted
5+
# Publishing (OIDC — no long-lived API key). Setup steps in docs/RELEASING.md.
6+
on:
7+
push:
8+
tags:
9+
- 'v[0-9]+.[0-9]+.[0-9]+*'
10+
workflow_dispatch:
11+
inputs:
12+
dry_run:
13+
description: "Pack and validate only — skip nuget push"
14+
type: boolean
15+
default: true
16+
17+
jobs:
18+
release:
19+
runs-on: ubuntu-latest
20+
environment: release
21+
permissions:
22+
contents: write # for creating the GitHub Release
23+
id-token: write # required for Trusted Publishing OIDC exchange
24+
steps:
25+
- uses: actions/checkout@v4
26+
27+
- name: Setup .NET
28+
uses: actions/setup-dotnet@v4
29+
with:
30+
global-json-file: global.json
31+
32+
- name: Restore
33+
run: dotnet restore shelldocs.slnx
34+
35+
- name: Build
36+
run: dotnet build shelldocs.slnx --configuration Release --no-restore
37+
38+
- name: Test
39+
run: dotnet test shelldocs.slnx --configuration Release --no-build --verbosity normal
40+
41+
- name: Pack
42+
run: dotnet pack shelldocs.slnx --configuration Release --no-build --output nupkgs
43+
44+
- name: List produced packages
45+
run: ls -la nupkgs/
46+
47+
# Runs immediately before push — the temp API key is valid only 1 hour.
48+
# `user` is the nuget.org profile name (NOT email, NOT the GH org name),
49+
# kept as a secret so it never lives in the workflow file.
50+
- name: Login to NuGet via Trusted Publishing
51+
if: github.event_name == 'push' || inputs.dry_run == false
52+
id: nuget-login
53+
uses: NuGet/login@v1
54+
with:
55+
user: ${{ secrets.NUGET_USER }}
56+
57+
- name: Push to NuGet
58+
if: github.event_name == 'push' || inputs.dry_run == false
59+
env:
60+
NUGET_API_KEY: ${{ steps.nuget-login.outputs.NUGET_API_KEY }}
61+
run: |
62+
for pkg in nupkgs/*.nupkg; do
63+
echo "Publishing $pkg"
64+
dotnet nuget push "$pkg" \
65+
--api-key "$NUGET_API_KEY" \
66+
--source https://api.nuget.org/v3/index.json \
67+
--skip-duplicate
68+
done
69+
70+
- name: Create GitHub Release
71+
if: github.event_name == 'push'
72+
uses: softprops/action-gh-release@v2
73+
with:
74+
generate_release_notes: true
75+
files: nupkgs/*.nupkg

CHANGELOG.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Changelog
2+
3+
All notable changes to ShellDocs land here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning is [SemVer](https://semver.org/spec/v2.0.0.html) with prerelease suffixes (`-alpha`, `-beta`, `-rc`) — the alpha window explicitly reserves the right to break APIs on minor bumps.
4+
5+
## [Unreleased]
6+
7+
## [0.1.0-alpha] — 2026-07-25
8+
9+
First public release. The whole Phase 1 target is shipped, plus most of Phase 2's primitives + consumer DX polish. See [ROADMAP.md](docs/ROADMAP.md).
10+
11+
### Packages
12+
13+
Published to NuGet:
14+
15+
- `ShellDocs.CLI` — global tool: `dotnet tool install -g ShellDocs.CLI --prerelease`. Commands: `init`, `add`, `dev`, `build`, `preview`
16+
- `ShellDocs.Components` — RCL with `<DocsLayout>`, `<DocsHeader>`, `<DocsSidebar>`, `<TableOfContents>`, `<PrevNextNav>`, `<DocsBreadcrumb>`, `<SearchDialog>`, content primitives, API-reference primitives
17+
- `ShellDocs.Core` — navigation graph, search index model, routing helpers, markdown plain-text extractor
18+
- `ShellDocs.Markdown` — Markdig pipeline with frontmatter, `razor:preview` fenced blocks, inline Razor component tags
19+
- `ShellDocs.Templates` — starter markdown + Program.cs snippets for `shelldocs init` scaffolding
20+
- `ShellDocs.Tokens` — RCL with `tokens.css` — shadcn-compatible palette + spacing scale, single source of truth for `--background`, `--foreground`, `--primary`, `--radius`, dark mode
21+
22+
### Added
23+
24+
**Markdown pipeline (`ShellDocs.Markdown`)**
25+
- YAML frontmatter parsing via YamlDotNet
26+
- ` ```razor:preview ` fenced blocks — live-rendered previews with source-view toggle
27+
- Inline Razor component tags mid-markdown (`<Callout />`, `<Card ... />`)
28+
- Component type registry (`RegisterComponent<T>()`) with per-type tag aliases (`RegisterComponent<Button>("Btn")`)
29+
- Bulk `RegisterComponentsFromAssembly<TMarker>()` scan + `[ShellDocsIgnore]` opt-out attribute
30+
- Automatic string→typed coercion for `bool`, `int`, `enum` attribute values
31+
32+
**Content primitives (`ShellDocs.Components`)**
33+
- `<Callout Variant="info|warning|danger|tip">` — coloured info box with icon + title + body
34+
- `<Card>` / `<CardGrid Columns="1|2|3">` / `<LinkCard>` — responsive card family
35+
- `<Steps>` / `<Step>` — CSS-counter numbered list with badge-on-rail spine
36+
- `<FileTree>` / `<FileTreeItem>` — recursive project-layout diagram
37+
- `<CodeGroup SyncKey>` / `<CodeTab>` — tabbed code samples with cross-page sync
38+
39+
**API-reference primitives (`ShellDocs.Components`)**
40+
- `<TypeTable>` / `<TypeRow Name Type Default Description Required>` — props/API reference table
41+
- `<ComponentPreview Component="..." ...props>` — declarative-prop single-component demos
42+
43+
**Chrome (`ShellDocs.Components`)**
44+
- `<DocsLayout>` with two variants (`TopNav`, `Sidebar` floating card)
45+
- `<DocsHeader>` with primary nav mega-menu, GitHub link, theme toggle
46+
- `<DocsSidebar>` with grouped nav, collapsible sections (animated grid-rows), auto-open on active path
47+
- `<TableOfContents>` — right-rail, h2/h3 auto-extraction, scroll-spy indicator with smooth slide
48+
- `<PrevNextNav>` — auto-derived from nav-graph adjacency
49+
- `<DocsBreadcrumb>` — auto-generated from nav path; sections render as text, current page as `aria-current`, only leaf pages become links
50+
- `<PackageSelector>` — consumer-configurable multi-package selector; hides when 0 or 1 packages declared
51+
- `<BrandLogo>` — consumer-configurable logo with three modes: `LogoSvg` (inline SVG, tints via `currentColor`), `LogoLight`/`LogoDark` (theme-paired image URLs), or dot placeholder fallback
52+
- `<SearchDialog>` — Cmd+K modal, client-side substring scoring against title / description / section / body, snippet extraction for body-only matches
53+
- `<DocsFooter>` / `<DocsMobileBar>` / `<ThemeToggle>`
54+
55+
**Auto-chrome via `DocsPageState`**
56+
- Consumer's docs page collapses to just `<MarkdownContent Document="_document" />` — TOC, PrevNext, Breadcrumb all auto-render from a shared scoped service
57+
- Recomputes on `NavigationManager.LocationChanged`
58+
59+
**Search (`ShellDocs.Core`)**
60+
- `SearchIndex.FromGraph()` — page + heading entries with URL, title, description, section
61+
- Page entries carry extracted plain-text `Body` (frontmatter / fences / HTML / Razor tags / images / links / inline code / emphasis / heading `#` all stripped)
62+
- `MarkdownPlainText.Extract()` — reusable helper for body extraction, 8KB default cap
63+
64+
**Code highlighting (`ShellDocs.Components`)**
65+
- Shiki via WASM (bundle configurable)
66+
- Dual-theme via `--shiki-light` / `--shiki-dark` CSS custom properties
67+
68+
**Design tokens (`ShellDocs.Tokens`)**
69+
- Standalone RCL with `tokens.css` (base + full variants)
70+
- Shadcn-compatible variable names for interop with ShellUI and other consumers
71+
72+
**CLI (`ShellDocs.CLI`)**
73+
- `shelldocs init` — two modes: create (default, scaffolds a fresh Blazor Web App) and attach (`--attach`, augments existing project via `SHELLDOCS_SETUP.md`)
74+
- `shelldocs add <component|guide|page> <name>` — scaffolds starter `.md` from template into `content/`
75+
- `shelldocs dev` — dotnet watch with .md hot-reload
76+
- `shelldocs build` — publishes static site, handles base-href rewrite + SPA 404 fallback
77+
78+
**Animation polish (Phase 2)**
79+
- Native view-transitions API for cross-fade on route change (Chromium — silent no-op elsewhere)
80+
- Sidebar section collapse animates via `grid-template-rows: 0fr → 1fr`
81+
- Copy-icon success bounce
82+
- Global `@media (prefers-reduced-motion: reduce)` guard — all animations collapse to instant
83+
84+
**Consumer configuration (`ShellDocsOptions`)**
85+
- `RegisterComponentsFromAssembly<TMarker>(filter?)` — bulk-register a whole component library in one line
86+
- `AddPackage(id, title, description, rootUrl, iconPath?)` — declares consumer's package family for the sidebar selector
87+
- `SetLogo(url)` / `SetLogo(light, dark, alt?)` / `LogoSvg` — brand logo
88+
- `AddNavLink` / `AddNavMenu` — top-nav wiring
89+
- `LayoutVariant` — TopNav or Sidebar
90+
91+
### Known limitations
92+
93+
- Body-text search uses substring scoring, not an inverted index — fine for docs-sized corpora (~100 pages), will need rebuilding at 1000+
94+
- Search snippets don't yet highlight the matched substring
95+
- `<TypeTable>` is hand-authored today; XML-doc auto-generation ships in `ShellDocs.Xml` (Phase 4)
96+
- No `<DocsBreadcrumb>` opt-out — currently hides when the trail has ≤ 1 node, otherwise always renders
97+
98+
[Unreleased]: https://github.com/shellui-dev/shelldocs/compare/v0.1.0-alpha...HEAD
99+
[0.1.0-alpha]: https://github.com/shellui-dev/shelldocs/releases/tag/v0.1.0-alpha

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
**The docs framework for .NET.** Beautiful, animated, Cmd+K-searchable documentation sites, powered by Blazor and Tailwind. Compose with ShellUI (or any Blazor component library) — like fumadocs composes with shadcn/ui.
44

5-
> Status: **`0.1.0-alpha` in progress.** Not yet published to NuGet. See [ROADMAP](docs/ROADMAP.md).
5+
> Status: **`0.1.0-alpha`** — first public release. See [CHANGELOG](CHANGELOG.md) and [ROADMAP](docs/ROADMAP.md). Publish steps live in [docs/RELEASING.md](docs/RELEASING.md).
66
77
## Why ShellDocs
88

docs/RELEASING.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Releasing ShellDocs
2+
3+
Runbook for cutting a NuGet release. First time = read start to finish; steady state = jump to "Steady-state release" at the bottom.
4+
5+
## One-time setup (before the very first release)
6+
7+
We use **NuGet Trusted Publishing** — the workflow requests a short-lived (1-hour) API key from nuget.org via OIDC on each run. No long-lived API key stored as a secret. Official docs: <https://learn.microsoft.com/nuget/nuget-org/trusted-publishing>.
8+
9+
### 1. Verify package IDs are available on NuGet
10+
11+
Run once, before you register anything, so you don't discover a naming collision at t=publish:
12+
13+
```powershell
14+
foreach ($id in "ShellDocs.CLI","ShellDocs.Components","ShellDocs.Core","ShellDocs.Markdown","ShellDocs.Templates","ShellDocs.Tokens") {
15+
Write-Host "-- $id"
16+
dotnet nuget search $id --exact-match --source https://api.nuget.org/v3/index.json | Select-String $id
17+
}
18+
```
19+
20+
If any ID is taken by another author, decide: rename (`ShellUI.ShellDocs.*`?) or reach out to the owner. Do NOT publish under a different-looking name and hope no one notices — that's how brand-confusion issues start.
21+
22+
### 2. Create the `release` GitHub environment
23+
24+
Repo → Settings → Environments → **New environment** → name it exactly `release`.
25+
26+
Nothing to configure inside (optional: add required reviewers if you want a manual gate on each publish). The environment's existence is what the workflow's `environment: release` line references, and matching against the TP policy in step 3 is what proves this workflow is what it says it is.
27+
28+
### 3. Register a Trusted Publishing policy on NuGet
29+
30+
1. Sign in at [nuget.org](https://www.nuget.org/) → click your username → **Trusted Publishing****Add**
31+
2. Choose the owner (individual user OR organization — the policy applies to all packages owned by that account)
32+
3. Fill (all values are case-insensitive):
33+
- **Repository Owner:** `shellui-dev` (the GitHub organization/user name)
34+
- **Repository:** `shelldocs`
35+
- **Workflow File:** `release.yml`**filename only**, no `.github/workflows/` prefix
36+
- **Environment:** `release` — must match `environment: release` in our workflow. If you skip this, remove `environment: release` from the workflow too, or the policy match will fail.
37+
4. Save.
38+
39+
**Note on private repos:** first-time policies for private GitHub repos are provisional for 7 days. NuGet needs to see one successful publish (which carries GitHub's repository + owner IDs in the OIDC token) to lock the policy permanently. If no publish happens in 7 days, the policy goes inactive — you'd re-activate it and try again.
40+
41+
### 4. Add the `NUGET_USER` secret
42+
43+
The workflow's `NuGet/login@v1` action needs your **nuget.org profile username** (NOT email, NOT the GitHub org name — the visible profile name you sign in with, e.g. what shows on `nuget.org/profiles/<name>`).
44+
45+
Repo → Settings → Secrets and variables → Actions → New repository secret:
46+
- **Name:** `NUGET_USER`
47+
- **Value:** your nuget.org profile name
48+
49+
### 5. Local pack dry-run
50+
51+
Confirm the pack works locally before trusting CI. From repo root:
52+
53+
```powershell
54+
./scripts/pack-dry-run.ps1
55+
```
56+
57+
The script packs every `IsPackable=true` project into `./nupkgs-dryrun/`, prints IDs + sizes, and verifies `README.md` is embedded in each. Any missing README or unexpected package = fix before releasing.
58+
59+
### 6. First release — expect the 7-day provisional window
60+
61+
The very first `git push origin v0.1.0-alpha` triggers the workflow, which does OIDC exchange, publishes, and locks the policy permanently. Watch the Actions tab — if OIDC exchange fails, the most likely causes (in order) are: `NUGET_USER` secret missing or wrong, TP policy's `Workflow File` field includes a path prefix (should be just `release.yml`), or workflow's `environment: release` doesn't match the policy's Environment field.
62+
63+
## Steady-state release
64+
65+
Once the one-time setup is done, cutting a release is three commands.
66+
67+
### 1. Bump the version
68+
69+
Edit `Directory.Build.props``<Version>0.X.Y[-suffix]</Version>`. That propagates to every packable project via the shared props file.
70+
71+
For a prerelease bump: `0.1.0-alpha``0.1.1-alpha` (patch) or `0.2.0-alpha` (minor).
72+
For the first stable: strip the `-alpha` suffix → `1.0.0`.
73+
74+
### 2. Update `CHANGELOG.md`
75+
76+
Move the entries out of `[Unreleased]` into a new dated section (`[0.1.1-alpha] — YYYY-MM-DD`). Update the comparison links at the bottom.
77+
78+
### 3. Commit, tag, push
79+
80+
```bash
81+
git add Directory.Build.props CHANGELOG.md
82+
git commit -m "chore: release 0.X.Y[-suffix]"
83+
git tag "v0.X.Y[-suffix]"
84+
git push
85+
git push origin "v0.X.Y[-suffix]"
86+
```
87+
88+
The tag push triggers `.github/workflows/release.yml`:
89+
1. Builds Release
90+
2. Runs the test suite
91+
3. Packs every `IsPackable=true` project
92+
4. Pushes each `.nupkg` to nuget.org (`--skip-duplicate` so re-runs are safe)
93+
5. Creates a GitHub Release from the tag with auto-generated notes
94+
95+
Watch the run under Actions. If NuGet push fails on one package (e.g. `409 Conflict — already exists`), `--skip-duplicate` handles it silently; a real failure (bad API key, network) will surface as a red X.
96+
97+
## Dry-run without publishing
98+
99+
To validate the whole workflow without shipping to NuGet, go to Actions → Release → Run workflow → check "Pack and validate only". Runs build + pack, skips the push step.
100+
101+
## After the release
102+
103+
- Verify the packages appear at `https://www.nuget.org/packages/ShellDocs.CLI/`, etc. (indexing takes a few minutes)
104+
- Test the install locally: `dotnet tool install -g ShellDocs.CLI --prerelease` in a scratch directory
105+
- Announce as appropriate (blog post / X / whatever). Alpha releases are usually announced only internally

scripts/pack-dry-run.ps1

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Packs every IsPackable=true project into ./nupkgs-dryrun/ and validates each
2+
# .nupkg — verifies README embed, checks size, prints package ID + version.
3+
# Run from repo root.
4+
#
5+
# ./scripts/pack-dry-run.ps1
6+
7+
$ErrorActionPreference = "Stop"
8+
$repoRoot = Split-Path -Parent $PSScriptRoot
9+
Set-Location $repoRoot
10+
11+
$outDir = Join-Path $repoRoot "nupkgs-dryrun"
12+
if (Test-Path $outDir) { Remove-Item $outDir -Recurse -Force }
13+
New-Item -ItemType Directory -Path $outDir | Out-Null
14+
15+
Write-Host ""
16+
Write-Host "-> dotnet pack shelldocs.slnx -c Release -o $outDir"
17+
Write-Host ""
18+
dotnet pack shelldocs.slnx --configuration Release --output $outDir
19+
if ($LASTEXITCODE -ne 0) {
20+
Write-Host "PACK FAILED" -ForegroundColor Red
21+
exit 1
22+
}
23+
24+
Write-Host ""
25+
Write-Host "Produced packages:"
26+
Write-Host ""
27+
28+
$fail = 0
29+
foreach ($nupkg in Get-ChildItem $outDir -Filter *.nupkg | Sort-Object Name) {
30+
$sizeKB = [math]::Round($nupkg.Length / 1KB, 1)
31+
Write-Host (" {0} ({1} KB)" -f $nupkg.Name, $sizeKB)
32+
33+
# A .nupkg is a zip — extract to a temp dir to inspect.
34+
$tmp = Join-Path ([IO.Path]::GetTempPath()) ("nupkg-check-" + [guid]::NewGuid().ToString("N").Substring(0, 8))
35+
Expand-Archive -Path $nupkg.FullName -DestinationPath $tmp -Force
36+
37+
# Every packable project ships README.md via <PackageReadmeFile>.
38+
$readme = Get-ChildItem $tmp -Filter README.md -Recurse | Select-Object -First 1
39+
if (-not $readme) {
40+
Write-Host " MISSING README.md" -ForegroundColor Red
41+
$fail++
42+
}
43+
44+
# Sanity: nuspec present with expected version.
45+
$nuspec = Get-ChildItem $tmp -Filter *.nuspec | Select-Object -First 1
46+
if ($nuspec) {
47+
$xml = [xml](Get-Content $nuspec.FullName)
48+
$id = $xml.package.metadata.id
49+
$ver = $xml.package.metadata.version
50+
Write-Host " id=$id version=$ver"
51+
}
52+
53+
Remove-Item $tmp -Recurse -Force
54+
}
55+
56+
Write-Host ""
57+
if ($fail -gt 0) {
58+
Write-Host "$fail package(s) failed validation" -ForegroundColor Red
59+
exit 1
60+
}
61+
Write-Host "OK — all packages passed validation" -ForegroundColor Green
62+
Write-Host ""
63+
Write-Host "Ship it with:"
64+
Write-Host " git tag v<version>"
65+
Write-Host " git push origin v<version>"

0 commit comments

Comments
 (0)