Add the agent-driven release process: docs, sync script, tag job

- RELEASING.md documents the lockstep three-package prepare flow and the marked-changelog convention
- CHANGELOG.md starts with an Unreleased section and the marked 0.0.1 entry that becomes the GitHub release body
- AGENTS.md states repo-wide build/test conventions and defers docs-site specifics to docs/AGENTS.md
- scripts/sync-versions.mjs stamps runtime and compiler from the CLI version; the workflow's sync check now hints at it
- release.yml gains a github-release job that tags v<version> after a successful publish, never gating npm
This commit is contained in:
Chris Tate
2026-07-22 21:03:10 -05:00
parent 7fca9785ea
commit 9deedfd074
5 changed files with 144 additions and 1 deletions
+48 -1
View File
@@ -81,7 +81,7 @@ jobs:
# configured on npmjs.com with a GitHub Actions trusted publisher
# pointing at repository vercel-labs/scriptc, workflow release.yml,
# environment Release. A package missing that configuration fails
# loudly with an OIDC authentication error before anything uploads.
# with an OIDC authentication error before anything uploads.
# Trusted publishing requires npm >= 11.5.1 (bundled with Node 24).
- name: Install and build
@@ -96,6 +96,7 @@ jobs:
V=$(node -p "require('./$pkg/package.json').version")
if [ "$V" != "$VERSION" ]; then
echo "Version mismatch: $pkg is $V, expected $VERSION"
echo "Run 'node scripts/sync-versions.mjs' to stamp runtime and compiler from the CLI version, then commit"
exit 1
fi
done
@@ -136,3 +137,49 @@ jobs:
publish_dir packages/cli
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# The GitHub release is a tag and notes only — scriptc has no platform
# binary assets to stage (programs compile on the user's machine) — so
# it runs AFTER a successful npm publish and never gates it. The body is
# the CHANGELOG.md block between the release:start/release:end markers,
# which RELEASING.md keeps on the latest entry only.
github-release:
name: Create GitHub Release
needs: [check-release, publish]
if: needs.check-release.outputs.should_release == 'true'
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Extract changelog entry
run: |
VERSION="${{ needs.check-release.outputs.version }}"
awk '/<!-- release:start -->/{found=1; next} /<!-- release:end -->/{exit} found{print}' CHANGELOG.md > /tmp/release-notes.md
LINES=$(wc -l < /tmp/release-notes.md | tr -d ' ')
if [ "$LINES" -lt 2 ]; then
echo "Error: No release notes found between <!-- release:start --> and <!-- release:end --> markers in CHANGELOG.md"
exit 1
fi
echo "Extracted release notes for $VERSION ($LINES lines)"
- name: Create GitHub Release
run: |
VERSION="${{ needs.check-release.outputs.version }}"
TAG="v$VERSION"
if gh release view "$TAG" &>/dev/null; then
echo "Release $TAG already exists, skipping"
else
echo "Creating release $TAG..."
gh release create "$TAG" \
--target "$GITHUB_SHA" \
--title "$TAG" \
--notes-file /tmp/release-notes.md
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+28
View File
@@ -0,0 +1,28 @@
# Agent Guide
Guidance for agents (and humans) working on this repository. These conventions apply repo-wide; the docs site under `docs/` additionally has its own conventions in `docs/AGENTS.md`.
## Build and test
```bash
pnpm install && pnpm -r build # build the workspace
SCRIPTC_TEST_WORKERS=4 pnpm test # plain lane: differential corpus + diagnostics snapshots
SCRIPTC_SAN=1 pnpm test # sanitized lane: the same corpus under ASan + refcount audit
```
Both lanes green is the bar before shipping any change. `SCRIPTC_TEST_WORKERS` caps the vitest worker pool so concurrent agents don't contend for cores; full-suite runs also queue behind an advisory lock per lane.
Corpus programs are differential tests against Node: every program runs under Node and as a compiled native binary, and stdout, stderr, and exit codes must match byte-for-byte. A new feature lands with corpus programs that pin its behavior both ways.
## Where things live
- `packages/compiler` — the frontend (tsc API to IR), the typed IR with validator and serializer, and the LLVM and C backends.
- `packages/runtime` — the C runtime compiled into every scriptc binary.
- `packages/cli` — `scriptc build | run | coverage`.
- `tests/` — the differential corpus, diagnostics snapshots, and the harness.
- `docs/` — the documentation site (standalone pnpm workspace); see `docs/AGENTS.md`.
- `scripts/` — repo tooling, including the release version stamp.
## Releases
Releases are maintainer-run; see [RELEASING.md](./RELEASING.md).
+22
View File
@@ -0,0 +1,22 @@
# Changelog
All notable changes to scriptc will be documented in this file.
## Unreleased
## 0.0.1
<!-- release:start -->
The first release: `scriptc` on npm, with `@scriptc/compiler` and `@scriptc/runtime` underneath it.
### Features
- **The CLI**: `scriptc build` compiles a TypeScript or JavaScript entry point into a self-contained native executable, `scriptc run` builds and runs it in one step, and `scriptc coverage` reports statement by statement what compiles statically and which specific constructs block, each with a diagnostic code.
- **The static tier**: programs compile to native code with no JavaScript engine in the binary. Type checking is the real TypeScript compiler; what compiles behaves byte-for-byte like Node, enforced by a differential test corpus.
- **`--dynamic`**: an embedded JavaScript engine (quickjs-ng) executes what cannot compile statically. Values crossing back into static code are validated at runtime, so a mismatched type throws a catchable `TypeError`. Static remains the default; a binary never silently grows an engine.
- **npm dependencies** (with `--dynamic`): packages resolve with Node's own resolution algorithm, typecheck against their shipped `.d.ts`, and their JavaScript is embedded into the binary at build time. Binaries never read `node_modules` at runtime.
- **Platforms**: macOS arm64 is the primary platform; Linux and Windows binaries build by cross-compilation, each verified by its own differential test lane.
- **Documentation** at [scriptc.dev](https://scriptc.dev).
<!-- release:end -->
+18
View File
@@ -0,0 +1,18 @@
# Releasing
Releases are manual, single-commit affairs. The maintainer controls the changelog voice and format. The three npm packages — `@scriptc/runtime`, `@scriptc/compiler`, `scriptc` — always publish together at the same version.
To prepare a release:
1. Bump the version in `packages/cli/package.json`
2. Run `node scripts/sync-versions.mjs` to stamp the same version into `packages/runtime` and `packages/compiler`
3. Fold the `## Unreleased` section of `CHANGELOG.md` into a new `## <version>` entry (newest first, below `## Unreleased`), and leave `## Unreleased` empty for the next cycle
4. Wrap the new entry in `<!-- release:start -->` and `<!-- release:end -->` markers; this marked block is also the GitHub release body
5. Remove the `<!-- release:start -->` and `<!-- release:end -->` markers from the previous release entry; only the latest release should have markers
6. Commit to `main`
CI (`.github/workflows/release.yml`) compares the version in `packages/cli/package.json` to what `scriptc` has on npm. If it differs, it builds the workspace, verifies all three package versions match (a mismatch fails with a hint to run `scripts/sync-versions.mjs`), and publishes to npm in dependency order — `@scriptc/runtime`, then `@scriptc/compiler`, then `scriptc` — so each package's dependencies are resolvable the moment it lands. After the publish succeeds, a separate job creates the git tag `v<version>` and the GitHub release with the marked changelog entry as its body.
Two deliberate differences from repositories that ship prebuilt binaries: there are no platform binary assets to build or stage — scriptc compiles programs on the user's machine with the local clang — so the GitHub release is a tag and release notes only, and the npm publish never waits on the GitHub release (the release job runs after the publish, not before it).
Publishing uses npm trusted publishing (OIDC) — there is no npm token secret. The one-time setup is already done: each of the three packages is configured on npmjs.com with a GitHub Actions trusted publisher pointing at repository `vercel-labs/scriptc`, workflow `release.yml`, environment `Release`. A package missing that configuration fails with an OIDC authentication error before anything is uploaded. Re-runs are safe: any package already on the registry at the target version is skipped, so a partially published release can be resumed by re-running the workflow.
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env node
// Stamps the version from packages/cli/package.json into packages/runtime
// and packages/compiler, so the three published packages move in lockstep.
// Usage: node scripts/sync-versions.mjs
import { readFileSync, writeFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
const root = fileURLToPath(new URL("..", import.meta.url));
const manifest = (pkg) => `${root}packages/${pkg}/package.json`;
const read = (path) => JSON.parse(readFileSync(path, "utf8"));
const version = read(manifest("cli")).version;
if (typeof version !== "string" || version.length === 0) {
console.error("packages/cli/package.json has no version");
process.exit(1);
}
for (const pkg of ["runtime", "compiler"]) {
const path = manifest(pkg);
const json = read(path);
if (json.version === version) {
console.log(`packages/${pkg}: already ${version}`);
continue;
}
console.log(`packages/${pkg}: ${json.version} -> ${version}`);
json.version = version;
writeFileSync(path, JSON.stringify(json, null, 2) + "\n");
}