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
160 changes: 160 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
name: Release

# Publishes @inkform/framework or @inkform/cli to public npm.
#
# Authentication is npm Trusted Publishing (OIDC) — there is NO npm token
# stored in this repository's secrets. GitHub mints a short-lived OIDC token
# for this specific workflow file, and npm accepts it only because the
# package's "Trusted publisher" settings on npmjs.com name this repo AND
# this exact filename (.github/workflows/release.yml). Renaming this file
# breaks publishing until the npm-side config is updated to match — that is
# the security property, not an accident. Publishes made this way also carry
# a provenance attestation automatically (the "Built and signed on GitHub
# Actions" badge on npm), with no --provenance flag needed.
#
# Trigger: push an annotated tag naming the package and its version.
#
# framework-v0.5.0 → publishes packages/framework
# cli-v0.5.0 → publishes packages/cli
#
# The tag's version MUST equal the version already committed in that
# package's package.json — the job refuses to guess. Bump, commit, push,
# THEN tag. workflow_dispatch runs the whole thing in --dry-run mode by
# default so you can rehearse a release without publishing anything.

on:
push:
tags:
- 'framework-v*'
- 'cli-v*'
workflow_dispatch:
inputs:
package:
description: 'Which package to release'
required: true
type: choice
options: [framework, cli]
dry_run:
description: 'Dry run (pack and validate, publish nothing)'
required: true
type: boolean
default: true

permissions:
contents: read
id-token: write # required: this is what lets npm verify the OIDC claim

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

jobs:
release:
runs-on: ubuntu-latest

# Second gate, independent of npm. Add required reviewers to the
# "npm-publish" environment in Settings → Environments and every publish
# pauses for a human approval, so a tag push alone can never ship.
# Referencing an environment that doesn't exist yet is harmless.
environment: npm-publish

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
registry-url: 'https://registry.npmjs.org'

# setup-node ships npm 10.x with Node 22; trusted publishing needs
# npm >= 11.5.1. Without this the publish falls back to looking for an
# auth token and fails with a confusing ENEEDAUTH.
- name: Use an npm that supports trusted publishing
run: |
npm install -g npm@latest
npm --version

# Every ${{ }} value below is passed through `env:` and read as a
# shell variable, never expanded into the script body. GitHub splices
# expansions in as raw text before bash ever sees the file, so an
# inline `'${{ github.ref_name }}'` in a run block is a script-injection
# sink — a tag name containing a quote and a semicolon would execute.
# Only maintainers can push tags here, but a workflow holding npm
# publish rights shouldn't rely on that as its only defense.
- name: Resolve target package
id: target
env:
EVENT: ${{ github.event_name }}
INPUT_PACKAGE: ${{ inputs.package }}
REF: ${{ github.ref_name }}
run: |
set -euo pipefail
if [ "$EVENT" = 'workflow_dispatch' ]; then
SLUG="$INPUT_PACKAGE"
TAG_VERSION=''
else
SLUG="${REF%%-v*}"
TAG_VERSION="${REF#*-v}"
fi

case "$SLUG" in
framework) DIR='packages/framework' ;;
cli) DIR='packages/cli' ;;
*) echo "::error::Unrecognized release target '$SLUG'"; exit 1 ;;
esac

NAME=$(node -p "require('./$DIR/package.json').name")
VERSION=$(node -p "require('./$DIR/package.json').version")

if [ -n "$TAG_VERSION" ] && [ "$TAG_VERSION" != "$VERSION" ]; then
echo "::error::Tag says v$TAG_VERSION but $DIR/package.json says $VERSION. Commit the version bump before tagging."
exit 1
fi

echo "dir=$DIR" >> "$GITHUB_OUTPUT"
echo "name=$NAME" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Releasing $NAME@$VERSION from $DIR"

# npm rejects a re-publish of an existing version with a bare 403.
# Failing here instead says what actually went wrong.
- name: Refuse to republish an existing version
env:
NAME: ${{ steps.target.outputs.name }}
VERSION: ${{ steps.target.outputs.version }}
run: |
set -euo pipefail
if npm view "$NAME@$VERSION" version >/dev/null 2>&1; then
echo "::error::$NAME@$VERSION is already on npm. Bump the version — published versions are immutable."
exit 1
fi
echo "$NAME@$VERSION is unpublished. Proceeding."

- run: npm ci

# Same gates as ci.yml, re-run here on the exact commit being shipped.
# A green PR check is not proof that the tagged commit is green.
- run: npm run lint
- run: npm run typecheck
- run: npm test
- run: npm run build
- run: npm audit --audit-level=high

- name: Preview tarball contents
env:
DIR: ${{ steps.target.outputs.dir }}
run: npm pack --dry-run --workspace "$DIR"

- name: Publish to npm
if: ${{ github.event_name == 'push' || !inputs.dry_run }}
env:
DIR: ${{ steps.target.outputs.dir }}
run: npm publish --workspace "$DIR"

- name: Dry run only — nothing published
if: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run }}
env:
NAME: ${{ steps.target.outputs.name }}
VERSION: ${{ steps.target.outputs.version }}
run: echo "Dry run complete for $NAME@$VERSION. Re-run with dry_run unchecked, or push a tag, to publish."
57 changes: 57 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,63 @@ All notable changes to this project are documented here. Format loosely
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions
track `packages/framework`'s own `package.json`.

## [0.5.0] — 2026-08-30

### Added

- **Every page is served as Markdown at its own URL** — append `.md` to any
docs page (or content-negotiate) and get the source back as clean Markdown.
Makes the whole site directly consumable by agents and LLM tooling without
scraping rendered HTML.
- **`@inkform/framework/markdown`** — a structural MDX-to-Markdown converter
that preserves link URLs and component labels instead of flattening them
away, plus `@inkform/framework/page-actions` (`<PageActions>`): a per-page
*Copy Markdown* / *Open in…* control rendered above the page title.
- **Expanded AI tool menu** — a two-column menu driven by a single data
registry (`ai-tools.ts`) rather than hardcoded links: ChatGPT, Claude,
Google (AI Overview), and copy-the-command entries for Claude Code,
OpenCode, Codex, and Antigravity. Monochrome brand icons throughout.
- **`@inkform/framework/secondary-top-nav` and `/scrollable-top-nav`** —
unified secondary navigation with mobile scroll hints and de-duplicated
anchors/navbar links.

### Changed

- Copy actions give real feedback — copied-state on the button, with a
confetti flourish on success (tokenized colors, no hardcoded hex).
- Glyph and clipboard helpers deduplicated into shared modules.

### Fixed

- The VS Code MCP install link pointed at the wrong handler.
- The ChatGPT share link now uses the `prompt` parameter.
- Mobile *Open* menu is capped at `80vw` instead of overflowing the viewport.
- The left column of the AI menu now shares the right column's gutter off the
divider.
- **`@inkform/framework/reactions` resolves again.** The subpath export was
dropped from the exports map in 0.4.0's development while
`src/reactions.tsx` kept shipping, so the export documented in the package
README and in the guides resolved to nothing. Every export present in 0.4.0
is present in 0.5.0 — this release is purely additive.
- **Scaffolded projects get the current framework.** Every template and
example declared `"@inkform/framework": "^0.3.0"`. For a 0.x package that
range means `>=0.3.0 <0.4.0`, so `npx @inkform/cli init` followed by
`npm install` resolved to 0.3.0 — no native API reference renderer, no MCP
server, no AI ask-box, no `llms.txt`. The CLI rewrites a scaffolded
project's `name` and `version` but never touched this range. Now `^0.5.0`.
- **Workspace shadowing fixed at the root.** The same stale range meant the
local `packages/framework` no longer satisfied what the templates and
examples asked for, so npm fetched a real 0.3.0 from the registry into each
of the six workspaces' own `node_modules` — shadowing the live source. This
is what `scripts/prune-workspace-shadows.mjs` had been deleting on every
`postinstall`; the lockfile is 139 lines lighter without those entries. The
script stays as a safety net, with its root-cause note corrected.

### Security

- 4 lockfile advisories patched (3 high, 1 moderate); archived templates
bumped to Next 16.2.12, clearing 54 Dependabot alerts.

## [0.4.0] — 2026-07-21

### Added
Expand Down
15 changes: 15 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ documentation theme that lands in the CLI's theme picker.
- [Contributing to the CLI](#contributing-to-the-cli)
- [Pull request process](#pull-request-process)
- [Conventions](#conventions)
- [Releases (maintainers)](#releases-maintainers)
- [Source mirror (maintainers)](#source-mirror-maintainers)

---
Expand Down Expand Up @@ -388,6 +389,20 @@ chore: bump @inkform/framework to 0.4.1

---

## Releases (maintainers)

`@inkform/framework` and `@inkform/cli` are published to npm by
`.github/workflows/release.yml`, triggered by pushing a version tag
(`framework-v0.5.0`, `cli-v0.5.0`). It re-runs the full CI gate against the
tagged commit, then publishes with npm Trusted Publishing — a short-lived
OIDC token, no npm secret stored in this repository, and a provenance
attestation on every release.

Nobody publishes from a laptop. Full procedure and the one-time npm/GitHub
setup: [`packages/framework/PUBLISHING.md`](packages/framework/PUBLISHING.md).

---

## Source mirror (maintainers)

This public repo is kept in sync with a private working monorepo via
Expand Down
2 changes: 1 addition & 1 deletion examples/inkform-docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@inkform/framework": "^0.3.0",
"@inkform/framework": "^0.5.0",
"lucide-react": "^0.483.0",
"next": "16.2.12",
"react": "19.2.1",
Expand Down
2 changes: 1 addition & 1 deletion examples/markdown-docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@inkform/framework": "^0.3.0",
"@inkform/framework": "^0.5.0",
"lucide-react": "^0.483.0",
"next": "16.2.12",
"react": "19.2.1",
Expand Down
2 changes: 1 addition & 1 deletion examples/pokeapi-docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"test:e2e": "playwright test"
},
"dependencies": {
"@inkform/framework": "^0.3.0",
"@inkform/framework": "^0.5.0",
"lucide-react": "^0.483.0",
"next": "16.2.12",
"react": "19.2.1",
Expand Down
Loading
Loading