Merge remote-tracking branch 'origin/dev' into codex-issue-66-channel-details-admin-design

This commit is contained in:
Goon committed 2026-05-29 19:00:44 +07:00
commit 07bdec3c08
5 files changed
+152 -25

No files matched your search

+10
View File
@@ -10,6 +10,16 @@ permissions:
contents: read
jobs:
release-versioning:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Release versioning tests
run: node --test scripts/ci/semantic-beta-version.test.mjs
go:
runs-on: ubuntu-latest
# Postgres service container for integration tests. pgvector/pgvector:pg18
+4
View File
@@ -91,6 +91,8 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Release versioning tests
run: node --test scripts/ci/semantic-beta-version.test.mjs
- name: Fetch upstream release tags
run: git fetch --force --tags https://github.com/nextlevelbuilder/goclaw.git "refs/tags/v*:refs/tags/v*"
- name: Compute semantic beta version
@@ -105,6 +107,8 @@ jobs:
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
if ! git rev-parse "$TAG" >/dev/null 2>&1; then
git tag -a "$TAG" -m "Release $TAG"
fi
if ! git ls-remote --exit-code --tags origin "refs/tags/$TAG" >/dev/null 2>&1; then
git push origin "$TAG"
fi
- name: Upload release notes
+38 -21
View File
@@ -79,23 +79,41 @@ function writeNoRelease(reason) {
console.log(reason);
}
const tags = git(["tag", "--merged", "HEAD", "--list", "v[0-9]*"])
.split(/\r?\n/)
.filter(Boolean)
.map(parseVersion)
.filter(Boolean)
.map((tag) => ({ ...tag, commit: tagCommit(tag.raw) }))
.sort(compareVersion)
.reverse();
function releaseTags(args = []) {
return git(["tag", ...args, "--list", "v[0-9]*"])
.split(/\r?\n/)
.filter(Boolean)
.map(parseVersion)
.filter(Boolean)
.map((tag) => ({ ...tag, commit: tagCommit(tag.raw) }))
.sort(compareVersion)
.reverse();
}
const latest = tags[0];
const latestStable = tags.find((tag) => !tag.preid);
const latestPrerelease = tags.find((tag) => tag.preid === prerelease);
const versionTags = releaseTags();
const mergedTags = releaseTags(["--merged", "HEAD"]);
const latest = versionTags[0];
const latestStable = versionTags.find((tag) => !tag.preid);
function isUsablePrerelease(tag) {
return tag.preid !== prerelease || !latestStable || compareBase(tag, latestStable) > 0;
}
const usableVersionTags = versionTags.filter(isUsablePrerelease);
const usableMergedTags = mergedTags.filter(isUsablePrerelease);
const latestPrerelease = usableVersionTags.find((tag) => tag.preid === prerelease);
const latestMerged = usableMergedTags[0];
const latestMergedStable = usableMergedTags.find((tag) => !tag.preid);
const latestMergedPrerelease = usableMergedTags.find((tag) => tag.preid === prerelease);
const head = git(["rev-parse", "HEAD"]);
const previousTag = tags.find((tag) => tag.raw !== latest?.raw);
const repairTag = latest?.preid === prerelease && latest.commit === head ? latest : null;
const range = latest ? [`${latest.raw}..HEAD`] : [];
const logRange = repairTag && previousTag ? [`${previousTag.raw}..HEAD`] : range;
const zero = { major: 0, minor: 0, patch: 0 };
const initial = parseVersion(initialVersion) || { major: 0, minor: 1, patch: 0 };
const previousMergedTag = usableMergedTags.find((tag) => tag.raw !== latestMerged?.raw);
const repairTag = latestMergedPrerelease?.commit === head
&& (!latestStable || compareBase(latestMergedPrerelease, latestStable) > 0)
? latestMergedPrerelease
: null;
const range = latestMerged ? [`${latestMerged.raw}..HEAD`] : [];
const logRange = repairTag && previousMergedTag ? [`${previousMergedTag.raw}..HEAD`] : range;
const log = git(["log", "--format=%B%x1e", ...logRange]);
const messages = log.split("\x1e").map((message) => message.trim()).filter(Boolean);
@@ -126,6 +144,8 @@ if (repairTag) {
process.exit(0);
}
const releaseBase = maxBase(latestStable || initial || zero, latestMergedStable || initial || zero);
let level = "";
for (const message of messages) {
const next = commitLevel(message);
@@ -138,14 +158,11 @@ for (const message of messages) {
}
if (!level) {
writeNoRelease("No release-worthy conventional commits found since the last release tag.");
writeNoRelease("No release-worthy conventional commits found since the last merged release tag.");
process.exit(0);
}
const zero = { major: 0, minor: 0, patch: 0 };
const initial = parseVersion(initialVersion) || { major: 0, minor: 1, patch: 0 };
const stableBase = latestStable || initial || zero;
const bumpedBase = bump(stableBase, level);
const bumpedBase = bump(releaseBase, level);
const prereleaseBase = latestPrerelease || zero;
let targetBase = maxBase(bumpedBase, prereleaseBase);
if (!latest && compareBase(targetBase, initial) < 0) targetBase = initial;
@@ -156,7 +173,7 @@ const nextNumber = compareBase(targetBase, prereleaseBase) === 0
const version = `${versionText(targetBase)}-${prerelease}.${nextNumber}`;
const tag = `v${version}`;
if (tags.some((existing) => existing.raw === tag)) {
if (versionTags.some((existing) => existing.raw === tag)) {
writeNoRelease(`Computed tag ${tag} already exists.`);
process.exit(0);
}
+100
View File
@@ -0,0 +1,100 @@
import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { copyFileSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import test from "node:test";
const here = dirname(fileURLToPath(import.meta.url));
const repoRoot = resolve(here, "../..");
const scriptPath = join(repoRoot, "scripts/ci/semantic-beta-version.mjs");
function run(cwd, args, env = {}) {
return execFileSync(args[0], args.slice(1), {
cwd,
encoding: "utf8",
env: { ...process.env, ...env },
stdio: ["ignore", "pipe", "pipe"],
}).trim();
}
function git(cwd, ...args) {
return run(cwd, ["git", ...args]);
}
function writeAndCommit(cwd, name, message) {
writeFileSync(join(cwd, name), `${message}\n`);
git(cwd, "add", name);
git(cwd, "commit", "-m", message);
}
function tag(cwd, name) {
git(cwd, "tag", "-a", name, "-m", `Release ${name}`);
}
function createRepo() {
const cwd = mkdtempSync(join(tmpdir(), "goclaw-beta-version-"));
mkdirSync(join(cwd, "scripts/ci"), { recursive: true });
copyFileSync(scriptPath, join(cwd, "scripts/ci/semantic-beta-version.mjs"));
git(cwd, "init", "-b", "main");
git(cwd, "config", "user.name", "Version Test");
git(cwd, "config", "user.email", "version-test@example.com");
writeAndCommit(cwd, "base.txt", "fix(base): initial stable");
tag(cwd, "v3.11.3");
git(cwd, "checkout", "-b", "dev");
writeAndCommit(cwd, "old-beta.txt", "fix(runtime): old beta fix");
tag(cwd, "v3.12.0-beta.41");
git(cwd, "checkout", "main");
writeAndCommit(cwd, "stable.txt", "feat(release): stable milestone");
tag(cwd, "v3.12.0");
git(cwd, "checkout", "dev");
writeAndCommit(cwd, "current.txt", "feat(api): new work after stable");
return cwd;
}
function runVersionScript(cwd) {
return run(cwd, ["node", "scripts/ci/semantic-beta-version.mjs"], {
GITHUB_OUTPUT: "",
INITIAL_VERSION: "3.11.3",
PRERELEASE_ID: "beta",
});
}
test("bumps beta above latest stable even when stable tag is not merged into dev", () => {
const cwd = createRepo();
try {
const output = runVersionScript(cwd);
assert.match(output, /version=3\.13\.0-beta\.1/);
assert.match(output, /tag=v3\.13\.0-beta\.1/);
} finally {
rmSync(cwd, { recursive: true, force: true });
}
});
test("continues prerelease numbering when upstream already has the next beta base", () => {
const cwd = createRepo();
try {
git(cwd, "checkout", "-b", "upstream-dev");
writeAndCommit(cwd, "upstream.txt", "fix(vault): upstream beta work");
tag(cwd, "v3.13.0-beta.2");
git(cwd, "checkout", "dev");
const output = runVersionScript(cwd);
assert.match(output, /version=3\.13\.0-beta\.3/);
assert.match(output, /tag=v3\.13\.0-beta\.3/);
} finally {
rmSync(cwd, { recursive: true, force: true });
}
});
test("dev beta workflow pushes missing origin tags even if a fetched tag exists locally", () => {
const workflow = readFileSync(join(repoRoot, ".github/workflows/dev-beta-release.yaml"), "utf8");
assert.match(workflow, /git rev-parse "\$TAG"/);
assert.match(workflow, /git ls-remote --exit-code --tags origin "refs\/tags\/\$TAG"/);
assert.match(workflow, /git push origin "\$TAG"/);
});
@@ -16,8 +16,6 @@ import (
// while a Chat call is in flight properly closes the HTTP connection.
// Uses a stalling SSE server that only responds if data flows from it.
func TestAnthropicChat_AbortContextCancel(t *testing.T) {
t.Parallel()
// Start a stalling SSE server
server := newStallingSSEServer(t)
defer server.Close()
@@ -68,8 +66,6 @@ func TestAnthropicChat_AbortContextCancel(t *testing.T) {
// TestOpenAIChat_AbortContextCancel mirrors the Anthropic test for OpenAI-compatible providers.
func TestOpenAIChat_AbortContextCancel(t *testing.T) {
t.Parallel()
server := newStallingSSEServer(t)
defer server.Close()