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: 5 additions & 15 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ name: Publish
# One workflow publishes every channel, since npm trusted publishing names a single workflow file.
# - canary / dev: every push to that branch
# - latest: run by hand on `main`; publishes each package whose version is not on npm yet
# dev and latest also build the standalone binary and attach it to a GitHub Release: `devel`, one
# prerelease replaced on every dev push, or `cli-v<version>`, marked latest. canary stays npm-only.
# dev and latest also make GitHub Releases (scripts/github.mjs): `devel`, one prerelease with the
# standalone binary replaced on every dev push, or `<pkg>@v<version>` per package released on
# latest, the CLI's with the binary and marked latest. canary stays npm-only.
on:
push:
branches: [canary, dev]
Expand Down Expand Up @@ -192,16 +193,5 @@ jobs:
path: release
merge-multiple: true
# codework.sh/install verifies each download against SHA256SUMS.
- run: |
(cd release && sha256sum *.tar.gz > SHA256SUMS)
version="$(node scripts/version.mjs "$CHANNEL" packages/codework)"
if [ "$CHANNEL" = dev ]; then
gh release delete devel --cleanup-tag --yes || true
gh release create devel release/* --prerelease --target "$GITHUB_SHA" \
--title "CLI $version" --notes "Development build $version, from $GITHUB_SHA."
elif gh release view "cli-v$version" > /dev/null 2>&1; then
echo "cli-v$version is already released"
else
gh release create "cli-v$version" release/* --latest --target "$GITHUB_SHA" \
--title "CLI $version" --generate-notes
fi
- run: (cd release && sha256sum *.tar.gz > SHA256SUMS)
- run: node scripts/github.mjs "$CHANNEL" release
4 changes: 3 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ Every package is published by CI from `.github/workflows/publish.yml`, which run
- **dev**: every push to `dev`. Versions are `<version>-dev.<YYYYMMDDHHMMSS>` (UTC).
- **latest**: run the workflow by hand on `main`. It publishes each package whose manifest version is not on npm yet and skips the rest.

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.
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.

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.

Flow:

Expand Down
6 changes: 3 additions & 3 deletions packages/aikit/bump.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { defineConfig } from "bumpp";

// Release config for @codeworksh/aikit, loaded automatically by `bumpp`.
// - Tags follow the repo's `@codeworksh/<pkg>@<version>` convention.
// - Tags follow the repo's `<pkg>@v<version>` convention.
// - Pushing stays manual; publishing runs in CI (.github/workflows/publish.yml).
export default defineConfig({
tag: "@codeworksh/aikit@%s",
commit: "release: @codeworksh/aikit@%s",
tag: "aikit@v%s",
commit: "release: aikit@v%s",
push: false,
});
4 changes: 2 additions & 2 deletions packages/codework/bump.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { defineConfig } from "bumpp";
// Release config for @codeworksh/codework, loaded automatically by `bumpp`.
// Pushing and publishing stay manual so prerelease validation can happen first.
export default defineConfig({
tag: "@codeworksh/cli@%s",
commit: "release: @codeworksh/cli@%s",
tag: "cli@v%s",
commit: "release: cli@v%s",
push: false,
});
4 changes: 2 additions & 2 deletions packages/harness/bump.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { defineConfig } from "bumpp";
// Release config for @codeworksh/harness, loaded automatically by `bumpp`.
// Pushing and publishing stay manual so prerelease validation can happen first.
export default defineConfig({
tag: "@codeworksh/harness@%s",
commit: "release: @codeworksh/harness@%s",
tag: "harness@v%s",
commit: "release: harness@v%s",
push: false,
});
4 changes: 2 additions & 2 deletions packages/plugin/bump.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { defineConfig } from "bumpp";
// Release config for @codeworksh/plugin, loaded automatically by `bumpp`.
// Pushing and publishing stay manual so prerelease validation can happen first.
export default defineConfig({
tag: "@codeworksh/plugin@%s",
commit: "release: @codeworksh/plugin@%s",
tag: "plugin@v%s",
commit: "release: plugin@v%s",
push: false,
});
4 changes: 2 additions & 2 deletions packages/tui/bump.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { defineConfig } from "bumpp";
// packages ship in lockstep with the launcher, so all four versions move together.
export default defineConfig({
files: ["package.json", "npm/*/package.json"],
tag: "@codeworksh/tui@%s",
commit: "release: @codeworksh/tui@%s",
tag: "tui@v%s",
commit: "release: tui@v%s",
push: false,
});
2 changes: 1 addition & 1 deletion scripts/bump.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// `vp run bump <package> [bumpp args]`: runs bumpp in the package, which reads its bump.config.ts
// to commit and tag `@codeworksh/<package>@<version>` without pushing.
// to commit and tag `<package>@v<version>` without pushing.
import { spawnSync } from "node:child_process";

import { findPackage } from "./workspace.mjs";
Expand Down
101 changes: 101 additions & 0 deletions scripts/github.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// GitHub Releases for one publish run: `node scripts/github.mjs <dev|latest> <assets dir>`. The
// release job of .github/workflows/publish.yml runs it after npm, so a release never names a
// version npm does not have. canary is npm only.
//
// dev: `devel`, one prerelease replaced on every push, carrying the CLI binaries, so the installer
// has a fixed URL for the newest development build.
// latest: `<pkg>@v<version>` for each package not released yet; the CLI's carries the binaries
// and is marked latest. The tag is the one `vp run bump` made, or a new one at this commit.
import { spawnSync } from "node:child_process";
import { globSync } from "node:fs";
import { basename, dirname, resolve } from "node:path";

import { releaseId, releaseVersion } from "./version.mjs";
import { publishablePackages, repoRoot as root } from "./workspace.mjs";

const [channel, assetsDir] = process.argv.slice(2);
if (!["dev", "latest"].includes(channel) || !assetsDir) {
console.error("Usage: node scripts/github.mjs <dev|latest> <assets dir>");
process.exit(1);
}
const assets = globSync("*", { cwd: assetsDir }).map((file) => resolve(assetsDir, file));
const sha = process.env.GITHUB_SHA;
if (!sha) throw new Error("GITHUB_SHA is not set; GitHub Releases are made in CI");

function gh(...args) {
const result = spawnSync("gh", args, { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "inherit"] });
if (result.error) throw result.error;
return { ok: result.status === 0, stdout: result.stdout };
}

function ghOrThrow(...args) {
const result = gh(...args);
if (!result.ok) throw new Error(`gh ${args.join(" ")} failed`);
return result.stdout;
}

/** `@codeworksh/cli` at `0.3.0` → `cli@v0.3.0`. */
const tagName = (name, version) => `${name.replace("@codeworksh/", "")}@v${version}`;

/** The newest stable tag of the package below `version`, so its notes cover only its own changes. */
function previousTag(name, version) {
const prefix = tagName(name, "");
const compare = (a, b) => a.localeCompare(b, "en", { numeric: true });
const refs = JSON.parse(ghOrThrow("api", `repos/{owner}/{repo}/git/matching-refs/tags/${prefix}`));
const previous = refs
.map(({ ref }) => ref.slice(`refs/tags/${prefix}`.length))
.filter((v) => /^\d+\.\d+\.\d+$/.test(v) && compare(v, version) < 0)
.sort(compare)
.at(-1);
return previous && `${prefix}${previous}`;
}

const cli = publishablePackages().find(({ manifest }) => manifest.name === "@codeworksh/cli").manifest;

if (channel === "dev") {
const version = releaseVersion(cli.version, channel, releaseId(channel));
gh("release", "delete", "devel", "--cleanup-tag", "--yes");
ghOrThrow(
"release",
"create",
"devel",
...assets,
"--prerelease",
"--target",
sha,
"--title",
`cli@v${version}`,
"--notes",
`Development build ${version}, from ${sha}.`,
);
} else {
// The platform packages under packages/tui/npm ship with the launcher, which is the one release.
const packages = publishablePackages()
.filter(({ dir }) => basename(dirname(dir)) === "packages")
.map(({ manifest }) => manifest)
// The CLI goes last so it is the newest release, the one `releases/latest` points at.
.sort((a, b) => (a.name === cli.name) - (b.name === cli.name));
for (const { name, version } of packages) {
const tag = tagName(name, version);
if (gh("release", "view", tag).ok) {
console.error(`${tag} is already released`);
continue;
}
const isCli = name === cli.name;
const start = previousTag(name, version);
ghOrThrow(
"release",
"create",
tag,
...(isCli ? assets : []),
isCli ? "--latest" : "--latest=false",
"--target",
sha,
"--title",
tag,
"--generate-notes",
...(start ? ["--notes-start-tag", start] : []),
);
console.error(`Released ${tag}`);
}
}
2 changes: 1 addition & 1 deletion sites/www/public/install
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ while [ $# -gt 0 ]; do
--dev) release="download/devel" ;;
--version)
[ $# -ge 2 ] || fail "--version needs a version, such as 0.3.0"
release="download/cli-v${2#v}"
release="download/cli@v${2#v}"
shift
;;
--no-modify-path) modify_path=0 ;;
Expand Down
Loading