feat: changelog-driven versioning and idempotent Gitea registry release scripts
This commit is contained in:
1 parent
f10b885d1c
commit
023e012eda
5 files changed
+326
-6
No files matched your search
@@ -0,0 +1,134 @@
|
||||
import assert from "node:assert/strict"
|
||||
import { test } from "node:test"
|
||||
import { encodeName, ensureLinked, ensureTag, isPublished, publishBody } from "../scripts/release.ts"
|
||||
import { buildEntry, bumpMinor, insertEntry, latestVersion } from "../scripts/version.ts"
|
||||
|
||||
const CHANGELOG = `# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.4.0] - 2026-09-01
|
||||
|
||||
### Changed
|
||||
|
||||
- Upstream sync: mattpocock/skills \`aaaaaaaaa\` -> \`bbbbbbbbb\`.
|
||||
|
||||
## [0.1.0] - 2026-08-01
|
||||
`
|
||||
|
||||
test("latestVersion finds the first release heading", () => {
|
||||
assert.equal(latestVersion(CHANGELOG), "1.4.0")
|
||||
assert.throws(() => latestVersion("# empty\n"), /no release heading/)
|
||||
})
|
||||
|
||||
test("bumpMinor: minor bump, prerelease untouched", () => {
|
||||
assert.equal(bumpMinor("1.4.0"), "1.5.0")
|
||||
assert.equal(bumpMinor("0.1.0"), "0.2.0")
|
||||
assert.equal(bumpMinor("1.5.0-rc.1"), "1.5.0-rc.1")
|
||||
})
|
||||
|
||||
test("buildEntry: minor bump + upstream diff lines; null when commit already released", () => {
|
||||
const entry = buildEntry({
|
||||
changelog: CHANGELOG,
|
||||
fromCommit: "bbbbbbbbb123",
|
||||
toCommit: "ccccccccc456",
|
||||
toVersion: "1.4.0",
|
||||
date: "2026-10-15",
|
||||
diff: { added: ["engineering/new"], removed: [], changed: ["engineering/tdd"] },
|
||||
})
|
||||
assert.equal(entry?.version, "1.5.0")
|
||||
assert.match(entry!.body, /^## \[1\.5\.0\] - 2026-10-15/)
|
||||
assert.match(entry!.body, /`bbbbbbbbb` -> `ccccccccc`/)
|
||||
assert.match(entry!.body, /added: engineering\/new/)
|
||||
assert.match(entry!.body, /changed: engineering\/tdd/)
|
||||
|
||||
const again = buildEntry({
|
||||
changelog: CHANGELOG + entry!.body,
|
||||
fromCommit: "b",
|
||||
toCommit: "ccccccccc456",
|
||||
toVersion: "1.4.0",
|
||||
date: "2026-10-15",
|
||||
diff: { added: [], removed: [], changed: [] },
|
||||
})
|
||||
assert.equal(again, null, "idempotent for an already-released upstream commit")
|
||||
})
|
||||
|
||||
test("insertEntry places the new version under Unreleased", () => {
|
||||
const entry = { version: "1.5.0", date: "2026-10-15", body: "## [1.5.0] - 2026-10-15" }
|
||||
const out = insertEntry(CHANGELOG, entry)
|
||||
assert.match(out, /## \[Unreleased\]\n\n## \[1\.5\.0\] - 2026-10-15\n\n## \[1\.4\.0\]/)
|
||||
assert.throws(() => insertEntry("# nothing\n", entry), /no `## \[Unreleased\]` heading/)
|
||||
})
|
||||
|
||||
test("publish helpers: name encoding, published check, body hashes", () => {
|
||||
assert.equal(encodeName("@haylan/opencode-pocock"), "@haylan%2Fopencode-pocock")
|
||||
assert.equal(isPublished({ versions: { "1.0.0": {} } }, "1.0.0"), true)
|
||||
assert.equal(isPublished({ versions: {} }, "1.0.0"), false)
|
||||
assert.equal(isPublished({}, "1.0.0"), false)
|
||||
|
||||
const body = publishBody({ name: "@haylan/opencode-pocock", version: "1.0.0" }, Buffer.from("tar")) as {
|
||||
"dist-tags": Record<string, string>
|
||||
versions: Record<string, { dist: { shasum: string; integrity: string } }>
|
||||
_attachments: Record<string, { data: string; length: number }>
|
||||
}
|
||||
assert.equal(body["dist-tags"].latest, "1.0.0")
|
||||
assert.equal(body.versions["1.0.0"].dist.shasum.length, 40)
|
||||
assert.match(body.versions["1.0.0"].dist.integrity, /^sha512-/)
|
||||
const att = body._attachments["@haylan/opencode-pocock-1.0.0.tgz"]
|
||||
assert.equal(att.data, Buffer.from("tar").toString("base64"))
|
||||
assert.equal(att.length, 3)
|
||||
})
|
||||
|
||||
function fakeFetch(routes: Record<string, { status: number; body?: unknown }>, seen: string[]) {
|
||||
return (async (url: string | URL | Request, init?: RequestInit) => {
|
||||
const u = String(url)
|
||||
seen.push(`${init?.method ?? "GET"} ${u}`)
|
||||
const match = Object.entries(routes).find(([suffix]) => u.endsWith(suffix))
|
||||
const route = match?.[1] ?? { status: 404 }
|
||||
return new Response(route.body !== undefined ? JSON.stringify(route.body) : null, {
|
||||
status: route.status,
|
||||
})
|
||||
}) as typeof fetch
|
||||
}
|
||||
|
||||
test("ensureTag: creates on 404, no-ops when it exists, fails loudly otherwise", async () => {
|
||||
const base = "https://gitea.test/api/v1"
|
||||
const creating = {
|
||||
base,
|
||||
headers: {},
|
||||
fetchFn: (async (_url: string | URL | Request, init?: RequestInit) => {
|
||||
if ((init?.method ?? "GET") === "GET") return new Response(null, { status: 404 })
|
||||
return new Response(null, { status: 201 })
|
||||
}) as typeof fetch,
|
||||
}
|
||||
assert.equal(await ensureTag(creating, "o/r", "1.0.0", "sha123"), "created")
|
||||
|
||||
const exists = {
|
||||
base,
|
||||
headers: {},
|
||||
fetchFn: (async () => new Response(null, { status: 200 })) as typeof fetch,
|
||||
}
|
||||
assert.equal(await ensureTag(exists, "o/r", "1.0.0", "sha123"), "exists")
|
||||
|
||||
const broken = {
|
||||
base,
|
||||
headers: {},
|
||||
fetchFn: (async () => new Response("boom", { status: 500 })) as typeof fetch,
|
||||
}
|
||||
await assert.rejects(() => ensureTag(broken, "o/r", "1.0.0", "sha123"), /tag lookup failed: 500/)
|
||||
})
|
||||
|
||||
test("ensureLinked: links once, then reports already linked", async () => {
|
||||
let linked = false
|
||||
const fetchFn = (async (url: string | URL | Request, init?: RequestInit) => {
|
||||
const method = init?.method ?? "GET"
|
||||
if (method === "GET") {
|
||||
return Response.json(linked ? { repository: { id: 1 } } : {}, { status: 200 })
|
||||
}
|
||||
linked = true
|
||||
return new Response(null, { status: 204 })
|
||||
}) as typeof fetch
|
||||
const api = { base: "https://gitea.test/api/v1", headers: {}, fetchFn }
|
||||
assert.equal(await ensureLinked(api, "haylan", "@haylan/opencode-pocock", "0.2.0", "r"), "linked")
|
||||
assert.equal(await ensureLinked(api, "haylan", "@haylan/opencode-pocock", "0.2.0", "r"), "already linked")
|
||||
})
|
||||
Reference in new issue
Block a user