Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
name: Release

on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+*"

permissions:
contents: write
id-token: write

concurrency:
group: release-${{ github.ref_name }}
cancel-in-progress: false

jobs:
publish:
name: Publish npm package and GitHub release
runs-on: ubuntu-latest
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
steps:
- uses: actions/checkout@v5

- uses: actions/setup-node@v5
with:
node-version: 24
registry-url: https://registry.npmjs.org
cache: npm

- name: Read package metadata
id: package
shell: bash
run: |
name="$(node -p "require('./package.json').name")"
version="$(node -p "require('./package.json').version")"
tag="${GITHUB_REF_NAME}"

if [ "$tag" != "v$version" ]; then
echo "Tag $tag does not match package version $version." >&2
exit 1
fi

{
echo "name=$name"
echo "version=$version"
echo "tag=$tag"
} >> "$GITHUB_OUTPUT"

- run: npm ci
- run: npm test
- run: npm run build
- run: npm run lint
- run: npm run typecheck
- run: npm run format:check

- name: Create release tarball
id: pack
shell: bash
run: |
mkdir -p .release
npm pack --pack-destination .release
tarball="$(find .release -maxdepth 1 -name '*.tgz' -print -quit)"

if [ -z "$tarball" ]; then
echo "npm pack did not create a tarball." >&2
exit 1
fi

echo "tarball=$tarball" >> "$GITHUB_OUTPUT"

- name: Check npm version
id: npm
shell: bash
run: |
if npm view "${{ steps.package.outputs.name }}@${{ steps.package.outputs.version }}" version --registry=https://registry.npmjs.org >/dev/null 2>&1; then
echo "published=true" >> "$GITHUB_OUTPUT"
else
echo "published=false" >> "$GITHUB_OUTPUT"
fi

- name: Configure npm token fallback
if: ${{ env.NPM_TOKEN != '' }}
run: npm config set //registry.npmjs.org/:_authToken "$NPM_TOKEN"

- name: Publish to npm
if: steps.npm.outputs.published == 'false'
run: npm publish "${{ steps.pack.outputs.tarball }}" --access public --provenance

- name: Create or update GitHub release
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
tag="${{ steps.package.outputs.tag }}"
tarball="${{ steps.pack.outputs.tarball }}"

if gh release view "$tag" >/dev/null 2>&1; then
gh release upload "$tag" "$tarball" --clobber
else
gh release create "$tag" "$tarball" \
--verify-tag \
--title "SpecGov $tag" \
--generate-notes
fi
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ coverage/
.specgov.trace.json
.specgov.report.json
*.log
*.tgz
28 changes: 28 additions & 0 deletions .specgov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,26 @@ version: 1
mode: advisory

artifacts:
- path: ".specgov.yml"
kind: configuration
owner: maintainers
status: active
- path: "README.md"
kind: documentation
owner: maintainers
status: active
- path: "CHANGELOG.md"
kind: documentation
owner: maintainers
status: active
- path: "CONTRIBUTING.md"
kind: documentation
owner: maintainers
status: active
- path: "RELEASING.md"
kind: documentation
owner: maintainers
status: active
- path: "docs/**/*"
kind: documentation
owner: maintainers
Expand All @@ -32,6 +48,17 @@ mappings:
specs:
- ".specs/features/specgov-core/**"
- "README.md"
- "RELEASING.md"
- code: "package.json"
specs:
- ".specs/project/**"
- "README.md"
- "RELEASING.md"
- code: "package-lock.json"
specs:
- ".specs/project/**"
- "README.md"
- "RELEASING.md"

rules:
require_spec_impact_for_code_changes: true
Expand All @@ -44,3 +71,4 @@ ignore:
- "dist/**"
- ".git/**"
- "coverage/**"
- ".gitignore"
1 change: 1 addition & 0 deletions .specs/project/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- README quickstart.
- GitHub Pages product site for global project adoption.
- Contribution and security docs.
- npm package distribution with tag-driven GitHub Releases.

## Later

Expand Down
3 changes: 3 additions & 0 deletions .specs/project/STATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
- GitHub's official checkout and setup-node v5 tags exist and avoid the Node 20 runtime warning emitted by v4.
- Public project education lives in the README and the static GitHub Pages site
under `docs/`. The Pages workflow deploys the `docs` directory from `main`.
- Public distribution is npm-first. Version tags drive `.github/workflows/release.yml`,
which validates the package, publishes npm when needed, and mirrors the
version as a GitHub Release.

## Deferred Ideas

Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Changelog

## 0.1.0 - 2026-06-28

- Initial public CLI with `init`, `scan`, `check-pr`, `trace`, and `drift`.
- GitHub Action for advisory or strict pull request governance checks.
- Example manifests for docs-only, ADR-heavy, and framework-folder projects.
- Static documentation site for adoption guidance.
6 changes: 6 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@ npm test
npm run build
npm run lint
npm run typecheck
npm run format:check
```

## Releases

Releases are tag-driven. Follow [`RELEASING.md`](RELEASING.md) when publishing
a new npm version or GitHub Release.

## Spec Governance

SpecGov uses TLC Spec Driven internally, but the product itself is framework
Expand Down
24 changes: 10 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,27 +101,22 @@ declared mappings, and reports whether the review has enough spec context.

## Installation

SpecGov is pre-release. Until the first npm package and version tag are
published, install it from source:
Install the CLI from npm:

```bash
git clone https://github.com/paladini/specgov.git
cd specgov
npm ci
npm run build
npm link
npm install -g specgov
```

After `npm link`, the `specgov` command is available on your machine:
You can also run it without a global install:

```bash
specgov --help
npx specgov --help
```

You can also run the local build directly:
After installation, the `specgov` command is available on your machine:

```bash
node dist/cli.js --help
specgov --help
```

## Quick start
Expand Down Expand Up @@ -260,15 +255,14 @@ jobs:
with:
fetch-depth: 0

- uses: paladini/specgov@main
- uses: paladini/specgov@v0.1.0
with:
mode: advisory
base-ref: ${{ github.event.pull_request.base.sha }}
head-ref: ${{ github.event.pull_request.head.sha }}
```

Use `paladini/specgov@main` only while the project is pre-release. After a
version tag exists, pin the Action to that tag.
Pin the Action to a version tag for repeatable CI behavior.

Use `mode: strict` when governance findings should block the pull request.

Expand Down Expand Up @@ -388,6 +382,8 @@ are usable today, but may still change before the first tagged release.
Current project links:

- Website: <https://paladini.github.io/specgov/>
- npm: <https://www.npmjs.com/package/specgov>
- Repository: <https://github.com/paladini/specgov>
- Roadmap: [`.specs/project/ROADMAP.md`](.specs/project/ROADMAP.md)
- Release process: [`RELEASING.md`](RELEASING.md)
- Contribution guide: [`CONTRIBUTING.md`](CONTRIBUTING.md)
50 changes: 50 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Releasing SpecGov

SpecGov releases are tag-driven. A pushed `vX.Y.Z` tag runs the release
workflow, validates the package, publishes the npm version when it is not
already present, and creates or updates the matching GitHub Release.

## One-time npm setup

The release workflow supports both npm Trusted Publishing and an `NPM_TOKEN`
repository secret. Trusted Publishing is preferred because it uses GitHub OIDC
instead of a long-lived token.

For Trusted Publishing, configure the npm package with:

- Package: `specgov`
- Repository: `paladini/specgov`
- Workflow: `release.yml`
- Environment: leave blank unless the workflow is later changed to use one

Docs: <https://docs.npmjs.com/trusted-publishers/>

If Trusted Publishing is not configured, add an `NPM_TOKEN` secret to the
GitHub repository before pushing a release tag.

## Release a new version

From a clean `main` branch:

```bash
npm version patch
git push origin main --follow-tags
```

Use `npm version minor` or `npm version major` when the change warrants it. The
version commit updates `package.json` and `package-lock.json`, and the tag
triggers `.github/workflows/release.yml`.

## Verify a release

After the workflow completes, check:

```bash
npm view specgov version
gh release view vX.Y.Z --repo paladini/specgov
npx specgov@latest --help
```

The first release can be bootstrapped locally with `npm publish --access public`
when the package has not yet been created on npm. After that, prefer the tag
workflow.
18 changes: 5 additions & 13 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -294,23 +294,15 @@ <h3>Check pull requests</h3>
>
<div class="section-heading compact">
<p class="eyebrow">Quick start</p>
<h2 id="quickstart-title">
Install from source, then govern any repo.
</h2>
<h2 id="quickstart-title">Install from npm, then govern any repo.</h2>
<p>
SpecGov is pre-release. Until the first npm package and version tag
are published, use the source build.
Use the CLI locally or in CI without adopting a new spec format.
</p>
</div>
<div class="code-panel">
<div class="panel-title">Install</div>
<pre
class="copy-block"
><code>git clone https://github.com/paladini/specgov.git
cd specgov
npm ci
npm run build
npm link</code></pre>
<pre class="copy-block"><code>npm install -g specgov
npx specgov --help</code></pre>
<div class="panel-title">Use in your repository</div>
<pre class="copy-block"><code>specgov init
specgov scan
Expand Down Expand Up @@ -414,7 +406,7 @@ <h2 id="action-title">Put governance in the pull request.</h2>
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: paladini/specgov@main
- uses: paladini/specgov@v0.1.0
with:
mode: advisory
base-ref: ${{ github.event.pull_request.base.sha }}
Expand Down
15 changes: 15 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@
"dist",
"action.yml",
"README.md",
"RELEASING.md",
"SECURITY.md",
"CHANGELOG.md",
"LICENSE"
],
"scripts": {
"clean": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\"",
"build": "npm run clean && tsc -p tsconfig.json && npm run build:action",
"build:action": "ncc build src/action.ts -o dist/action -m --license licenses.txt",
"prepack": "npm run build",
"test": "vitest run",
"lint": "eslint .",
"typecheck": "tsc -p tsconfig.json --noEmit",
Expand All @@ -32,6 +36,17 @@
],
"author": "Fernando Paladini",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/paladini/specgov.git"
},
"bugs": {
"url": "https://github.com/paladini/specgov/issues"
},
"homepage": "https://github.com/paladini/specgov#readme",
"publishConfig": {
"access": "public"
},
"engines": {
"node": ">=20"
},
Expand Down