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
20 changes: 7 additions & 13 deletions .archgate/adrs/ARCH-013-version-synchronization.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,33 @@ files: ["package.json", "docs/**"]
The CLI version appears in multiple locations that must stay in sync:

1. `package.json` `version` — canonical source of truth
2. `package.json` `optionalDependencies` — platform-specific npm packages (`archgate-darwin-arm64`, `archgate-linux-x64`, `archgate-win32-x64`) must match the CLI version
3. `docs/astro.config.mjs` — `softwareVersion` in the JSON-LD structured data
2. `docs/astro.config.mjs` — `softwareVersion` in the JSON-LD structured data

When versions diverge, npm installs pull mismatched platform binaries and search engines display outdated version info. This was discovered during a consistency review where `package.json` was at `0.16.0` but `docs/astro.config.mjs` was still at `0.11.0`.
When versions diverge, search engines display outdated version info. This was discovered during a consistency review where `package.json` was at `0.16.0` but `docs/astro.config.mjs` was still at `0.11.0`.

## Decision

`package.json` `version` is the single source of truth. All other version references MUST match it.

**Automated via release process:** The `.simple-release.js` bump hook already syncs `optionalDependencies` versions during the release workflow — it reads `package.json` after the version bump and updates all `optionalDependencies` entries to match. This is fully automated and requires no manual intervention.

**Automated via release process:** The `.simple-release.js` bump hook also updates `softwareVersion` in `docs/astro.config.mjs` to match `package.json`. Both syncs are fully automated.
**Automated via release process:** The `.simple-release.js` bump hook updates `softwareVersion` in `docs/astro.config.mjs` to match `package.json`. This is fully automated and requires no manual intervention.

## Do's and Don'ts

### Do

- Rely on `.simple-release.js` for both `optionalDependencies` and `softwareVersion` sync (do not update manually)
- Rely on `.simple-release.js` for `softwareVersion` sync (do not update manually)
- Use the companion rules to catch version drift in CI as a safety net

### Don't

- Don't manually edit `optionalDependencies` versions — the release hook handles this
- Don't manually edit `softwareVersion` in `docs/astro.config.mjs` — the release hook handles this

## Consequences

### Positive

- Consistent version information across npm packages and user-facing surfaces
- Consistent version information across user-facing surfaces
- CI catches version drift before it reaches production
- Platform-specific npm packages always match the CLI version

### Negative

Expand All @@ -54,11 +49,10 @@ When versions diverge, npm installs pull mismatched platform binaries and search

### Automated Enforcement

- **Release hook** `.simple-release.js`: Syncs `optionalDependencies` versions and `docs/astro.config.mjs` `softwareVersion` during `bump()`. Fully automated.
- **Release hook** `.simple-release.js`: Syncs `docs/astro.config.mjs` `softwareVersion` during `bump()`. Fully automated.
- **Archgate rule** `ARCH-013/docs-version-sync`: Checks that `softwareVersion` in `docs/astro.config.mjs` matches `package.json` version. Severity: `error`.
- **Archgate rule** `ARCH-013/optional-deps-version-sync`: Checks that all `optionalDependencies` versions match `package.json` version. Severity: `error`.

## References

- [GEN-001 — Documentation Site](./GEN-001-documentation-site.md) — Docs site structure and configuration
- [`.simple-release.js`](../../.simple-release.js) — Release bump hook that syncs optionalDependencies
- [`.simple-release.js`](../../.simple-release.js) — Release bump hook that syncs softwareVersion
21 changes: 0 additions & 21 deletions .archgate/adrs/ARCH-013-version-synchronization.rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,5 @@ export default {
}
},
},
"optional-deps-version-sync": {
description:
"optionalDependencies versions must match package.json version",
severity: "error",
async check(ctx) {
const pkgJson = await ctx.readJSON("package.json");
if (!pkgJson.version || !pkgJson.optionalDependencies) return;

for (const [dep, depVersion] of Object.entries(
pkgJson.optionalDependencies
)) {
if (depVersion !== pkgJson.version) {
ctx.report.violation({
message: `optionalDependencies "${dep}" version "${depVersion}" does not match package.json version "${pkgJson.version}"`,
file: "package.json",
fix: `Update ${dep} to "${pkgJson.version}" in optionalDependencies (normally handled by .simple-release.js during release)`,
});
}
}
},
},
},
} satisfies RuleSet;
5 changes: 1 addition & 4 deletions .claude/agent-memory/archgate-developer/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,8 @@ Skipping steps 2 or 3 is a workflow violation. The user should NEVER have to inv

## Distribution / Packaging

- **npm binary distribution (Sentry pattern)** — Binaries are distributed as platform npm packages (`archgate-darwin-arm64`, `archgate-linux-x64`) listed as `optionalDependencies` of the main `archgate` package. Main package has a `bin` field pointing to `bin/archgate.cjs`.
- **npm shim + GitHub Releases** — The npm package is a thin shim (`bin/archgate.cjs` + `scripts/postinstall.cjs`). On first run, the shim downloads the platform binary from GitHub Releases and caches it to `~/.archgate/bin/`. No platform-specific npm packages.
- **`.cjs` extension is mandatory** — Root `package.json` has `"type": "module"`. Any Node.js CJS wrapper script placed at the package root MUST use `.cjs`, not `.js`, or Node.js will attempt to parse it as ESM and fail.
- **Platform package version placeholder** — Platform `package.json` files start at version `0.0.0`. CI runs `npm version "$VERSION" --no-git-tag-version` inside the package directory before publishing.
- **Gitignore and prettierignore rules** — `packages/*/bin/archgate` is gitignored (binary injected by CI); `packages/*/bin/` is prettier-ignored (binary files must not be formatted).
- **npm OIDC publishing** — Uses `id-token: write` permission + `actions/setup-node@v4` with `registry-url: 'https://registry.npmjs.org'`. No `NODE_AUTH_TOKEN` secret needed for OIDC trusted publishing — matches the pattern in `release.yml`.

## Telemetry Strategy

Expand Down
28 changes: 2 additions & 26 deletions .github/workflows/release-binaries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ on:

permissions:
contents: write
id-token: write

jobs:
build-and-publish:
name: Build and publish ${{ matrix.artifact }}
build:
name: Build ${{ matrix.artifact }}
timeout-minutes: 15
strategy:
fail-fast: false
Expand Down Expand Up @@ -58,29 +57,6 @@ jobs:
run: |
bun build src/cli.ts --compile --bytecode --minify --target ${{ matrix.target }} --outfile dist/${{ matrix.artifact }}

- name: Copy binary into npm package (Unix)
if: runner.os != 'Windows'
run: |
TAG="${{ github.event.release.tag_name || inputs.tag }}"
VERSION="${TAG#v}"
cp dist/${{ matrix.artifact }} packages/${{ matrix.artifact }}/bin/${{ matrix.binary_name }}
chmod +x packages/${{ matrix.artifact }}/bin/${{ matrix.binary_name }}
cd packages/${{ matrix.artifact }}
npm version "$VERSION" --no-git-tag-version

- name: Copy binary into npm package (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$tag = "${{ github.event.release.tag_name || inputs.tag }}"
$version = $tag -replace '^v', ''
Copy-Item "dist/${{ matrix.artifact }}.exe" "packages/${{ matrix.artifact }}/bin/${{ matrix.binary_name }}"
Set-Location "packages/${{ matrix.artifact }}"
npm version "$version" --no-git-tag-version

- name: Publish platform package
run: npm publish ./packages/${{ matrix.artifact }} --access public --provenance

- name: Prepare release asset (Unix)
if: runner.os != 'Windows'
run: |
Expand Down
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ coverage
*.tgz
.npm

# Platform package binaries (injected by CI)
packages/*/bin/archgate

# Docs site build artifacts
docs/.astro/

Expand Down
16 changes: 0 additions & 16 deletions .simple-release.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { execSync } from "node:child_process";
import { existsSync, readFileSync, writeFileSync } from "node:fs";

import { NpmProject } from "@simple-release/npm";
Expand All @@ -11,21 +10,6 @@ class ArchgateProject extends NpmProject {
const pkgPath = "package.json";
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
const version = pkg.version;
let changed = false;

// Sync optionalDependencies (platform-specific npm packages)
for (const dep of Object.keys(pkg.optionalDependencies || {})) {
if (pkg.optionalDependencies[dep] !== version) {
pkg.optionalDependencies[dep] = version;
changed = true;
}
}

if (changed) {
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
execSync("bun install", { stdio: "inherit" });
this.changedFiles.push("bun.lock");
}

// Sync docs/astro.config.mjs softwareVersion
const astroConfigPath = "docs/astro.config.mjs";
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Zod schemas are the single source of truth. Types derived via `z.infer<>` — ne

## npm Distribution

The npm package is a **thin shim** — it contains only `bin/archgate.cjs` and `scripts/postinstall.cjs`. The postinstall script downloads the prebuilt platform binary. All runtime dependencies (commander, inquirer, zod) are bundled into the compiled binary via `bun build --compile`, so they belong in `devDependencies`, not `dependencies`. The `optionalDependencies` (archgate-darwin-arm64, etc.) are platform-specific binary packages synced to the CLI version by `.simple-release.js` during release.
The npm package is a **thin shim** — it contains only `bin/archgate.cjs` and `scripts/postinstall.cjs`. The shim downloads the platform binary from GitHub Releases on first run and caches it to `~/.archgate/bin/`. All runtime dependencies (commander, inquirer, zod) are bundled into the compiled binary via `bun build --compile`, so they belong in `devDependencies`, not `dependencies`.

## Conventions

Expand Down
Loading
Loading