Skip to content

Commit 102e692

Browse files
sanchitrkclaude
andauthored
chore: name release tags <pkg>@v<version> (#88)
Bump tags become `<pkg>@v<version>`, and latest makes a GitHub Release per package released, the CLI's carrying the binaries and marked latest. dev keeps the rolling `devel` prerelease; canary stays npm only. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent c623a3e commit 102e692

10 files changed

Lines changed: 122 additions & 29 deletions

File tree

‎.github/workflows/publish.yml‎

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ name: Publish
33
# One workflow publishes every channel, since npm trusted publishing names a single workflow file.
44
# - canary / dev: every push to that branch
55
# - latest: run by hand on `main`; publishes each package whose version is not on npm yet
6-
# dev and latest also build the standalone binary and attach it to a GitHub Release: `devel`, one
7-
# prerelease replaced on every dev push, or `cli-v<version>`, marked latest. canary stays npm-only.
6+
# dev and latest also make GitHub Releases (scripts/github.mjs): `devel`, one prerelease with the
7+
# standalone binary replaced on every dev push, or `<pkg>@v<version>` per package released on
8+
# latest, the CLI's with the binary and marked latest. canary stays npm-only.
89
on:
910
push:
1011
branches: [canary, dev]
@@ -192,16 +193,5 @@ jobs:
192193
path: release
193194
merge-multiple: true
194195
# codework.sh/install verifies each download against SHA256SUMS.
195-
- run: |
196-
(cd release && sha256sum *.tar.gz > SHA256SUMS)
197-
version="$(node scripts/version.mjs "$CHANNEL" packages/codework)"
198-
if [ "$CHANNEL" = dev ]; then
199-
gh release delete devel --cleanup-tag --yes || true
200-
gh release create devel release/* --prerelease --target "$GITHUB_SHA" \
201-
--title "CLI $version" --notes "Development build $version, from $GITHUB_SHA."
202-
elif gh release view "cli-v$version" > /dev/null 2>&1; then
203-
echo "cli-v$version is already released"
204-
else
205-
gh release create "cli-v$version" release/* --latest --target "$GITHUB_SHA" \
206-
--title "CLI $version" --generate-notes
207-
fi
196+
- run: (cd release && sha256sum *.tar.gz > SHA256SUMS)
197+
- run: node scripts/github.mjs "$CHANNEL" release

‎CLAUDE.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ Every package is published by CI from `.github/workflows/publish.yml`, which run
8484
- **dev**: every push to `dev`. Versions are `<version>-dev.<YYYYMMDDHHMMSS>` (UTC).
8585
- **latest**: run the workflow by hand on `main`. It publishes each package whose manifest version is not on npm yet and skips the rest.
8686

87-
Versions are bumped locally with `vp run bump <aikit|plugin|harness|tui|cli> [version]`, which runs `bumpp` with the package's `bump.config.ts` and commits and tags `@codeworksh/<pkg>@<version>` without pushing. `vp run bump tui` moves the launcher and its three platform packages together.
87+
GitHub Releases (`scripts/github.mjs`): dev replaces the `devel` prerelease with the CLI binaries; latest makes a `<pkg>@v<version>` release per package not released yet, the CLI's carrying the binaries. canary has none.
88+
89+
Versions are bumped locally with `vp run bump <aikit|plugin|harness|tui|cli> [version]`, which runs `bumpp` with the package's `bump.config.ts` and commits and tags `<pkg>@v<version>` without pushing. `vp run bump tui` moves the launcher and its three platform packages together.
8890

8991
Flow:
9092

‎packages/aikit/bump.config.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { defineConfig } from "bumpp";
22

33
// Release config for @codeworksh/aikit, loaded automatically by `bumpp`.
4-
// - Tags follow the repo's `@codeworksh/<pkg>@<version>` convention.
4+
// - Tags follow the repo's `<pkg>@v<version>` convention.
55
// - Pushing stays manual; publishing runs in CI (.github/workflows/publish.yml).
66
export default defineConfig({
7-
tag: "@codeworksh/aikit@%s",
8-
commit: "release: @codeworksh/aikit@%s",
7+
tag: "aikit@v%s",
8+
commit: "release: aikit@v%s",
99
push: false,
1010
});

‎packages/codework/bump.config.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { defineConfig } from "bumpp";
33
// Release config for @codeworksh/codework, loaded automatically by `bumpp`.
44
// Pushing and publishing stay manual so prerelease validation can happen first.
55
export default defineConfig({
6-
tag: "@codeworksh/cli@%s",
7-
commit: "release: @codeworksh/cli@%s",
6+
tag: "cli@v%s",
7+
commit: "release: cli@v%s",
88
push: false,
99
});

‎packages/harness/bump.config.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { defineConfig } from "bumpp";
33
// Release config for @codeworksh/harness, loaded automatically by `bumpp`.
44
// Pushing and publishing stay manual so prerelease validation can happen first.
55
export default defineConfig({
6-
tag: "@codeworksh/harness@%s",
7-
commit: "release: @codeworksh/harness@%s",
6+
tag: "harness@v%s",
7+
commit: "release: harness@v%s",
88
push: false,
99
});

‎packages/plugin/bump.config.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { defineConfig } from "bumpp";
33
// Release config for @codeworksh/plugin, loaded automatically by `bumpp`.
44
// Pushing and publishing stay manual so prerelease validation can happen first.
55
export default defineConfig({
6-
tag: "@codeworksh/plugin@%s",
7-
commit: "release: @codeworksh/plugin@%s",
6+
tag: "plugin@v%s",
7+
commit: "release: plugin@v%s",
88
push: false,
99
});

‎packages/tui/bump.config.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { defineConfig } from "bumpp";
44
// packages ship in lockstep with the launcher, so all four versions move together.
55
export default defineConfig({
66
files: ["package.json", "npm/*/package.json"],
7-
tag: "@codeworksh/tui@%s",
8-
commit: "release: @codeworksh/tui@%s",
7+
tag: "tui@v%s",
8+
commit: "release: tui@v%s",
99
push: false,
1010
});

‎scripts/bump.mjs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// `vp run bump <package> [bumpp args]`: runs bumpp in the package, which reads its bump.config.ts
2-
// to commit and tag `@codeworksh/<package>@<version>` without pushing.
2+
// to commit and tag `<package>@v<version>` without pushing.
33
import { spawnSync } from "node:child_process";
44

55
import { findPackage } from "./workspace.mjs";

‎scripts/github.mjs‎

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// GitHub Releases for one publish run: `node scripts/github.mjs <dev|latest> <assets dir>`. The
2+
// release job of .github/workflows/publish.yml runs it after npm, so a release never names a
3+
// version npm does not have. canary is npm only.
4+
//
5+
// dev: `devel`, one prerelease replaced on every push, carrying the CLI binaries, so the installer
6+
// has a fixed URL for the newest development build.
7+
// latest: `<pkg>@v<version>` for each package not released yet; the CLI's carries the binaries
8+
// and is marked latest. The tag is the one `vp run bump` made, or a new one at this commit.
9+
import { spawnSync } from "node:child_process";
10+
import { globSync } from "node:fs";
11+
import { basename, dirname, resolve } from "node:path";
12+
13+
import { releaseId, releaseVersion } from "./version.mjs";
14+
import { publishablePackages, repoRoot as root } from "./workspace.mjs";
15+
16+
const [channel, assetsDir] = process.argv.slice(2);
17+
if (!["dev", "latest"].includes(channel) || !assetsDir) {
18+
console.error("Usage: node scripts/github.mjs <dev|latest> <assets dir>");
19+
process.exit(1);
20+
}
21+
const assets = globSync("*", { cwd: assetsDir }).map((file) => resolve(assetsDir, file));
22+
const sha = process.env.GITHUB_SHA;
23+
if (!sha) throw new Error("GITHUB_SHA is not set; GitHub Releases are made in CI");
24+
25+
function gh(...args) {
26+
const result = spawnSync("gh", args, { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "inherit"] });
27+
if (result.error) throw result.error;
28+
return { ok: result.status === 0, stdout: result.stdout };
29+
}
30+
31+
function ghOrThrow(...args) {
32+
const result = gh(...args);
33+
if (!result.ok) throw new Error(`gh ${args.join(" ")} failed`);
34+
return result.stdout;
35+
}
36+
37+
/** `@codeworksh/cli` at `0.3.0` → `cli@v0.3.0`. */
38+
const tagName = (name, version) => `${name.replace("@codeworksh/", "")}@v${version}`;
39+
40+
/** The newest stable tag of the package below `version`, so its notes cover only its own changes. */
41+
function previousTag(name, version) {
42+
const prefix = tagName(name, "");
43+
const compare = (a, b) => a.localeCompare(b, "en", { numeric: true });
44+
const refs = JSON.parse(ghOrThrow("api", `repos/{owner}/{repo}/git/matching-refs/tags/${prefix}`));
45+
const previous = refs
46+
.map(({ ref }) => ref.slice(`refs/tags/${prefix}`.length))
47+
.filter((v) => /^\d+\.\d+\.\d+$/.test(v) && compare(v, version) < 0)
48+
.sort(compare)
49+
.at(-1);
50+
return previous && `${prefix}${previous}`;
51+
}
52+
53+
const cli = publishablePackages().find(({ manifest }) => manifest.name === "@codeworksh/cli").manifest;
54+
55+
if (channel === "dev") {
56+
const version = releaseVersion(cli.version, channel, releaseId(channel));
57+
gh("release", "delete", "devel", "--cleanup-tag", "--yes");
58+
ghOrThrow(
59+
"release",
60+
"create",
61+
"devel",
62+
...assets,
63+
"--prerelease",
64+
"--target",
65+
sha,
66+
"--title",
67+
`cli@v${version}`,
68+
"--notes",
69+
`Development build ${version}, from ${sha}.`,
70+
);
71+
} else {
72+
// The platform packages under packages/tui/npm ship with the launcher, which is the one release.
73+
const packages = publishablePackages()
74+
.filter(({ dir }) => basename(dirname(dir)) === "packages")
75+
.map(({ manifest }) => manifest)
76+
// The CLI goes last so it is the newest release, the one `releases/latest` points at.
77+
.sort((a, b) => (a.name === cli.name) - (b.name === cli.name));
78+
for (const { name, version } of packages) {
79+
const tag = tagName(name, version);
80+
if (gh("release", "view", tag).ok) {
81+
console.error(`${tag} is already released`);
82+
continue;
83+
}
84+
const isCli = name === cli.name;
85+
const start = previousTag(name, version);
86+
ghOrThrow(
87+
"release",
88+
"create",
89+
tag,
90+
...(isCli ? assets : []),
91+
isCli ? "--latest" : "--latest=false",
92+
"--target",
93+
sha,
94+
"--title",
95+
tag,
96+
"--generate-notes",
97+
...(start ? ["--notes-start-tag", start] : []),
98+
);
99+
console.error(`Released ${tag}`);
100+
}
101+
}

‎sites/www/public/install‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ while [ $# -gt 0 ]; do
3535
--dev) release="download/devel" ;;
3636
--version)
3737
[ $# -ge 2 ] || fail "--version needs a version, such as 0.3.0"
38-
release="download/cli-v${2#v}"
38+
release="download/cli@v${2#v}"
3939
shift
4040
;;
4141
--no-modify-path) modify_path=0 ;;

0 commit comments

Comments
 (0)