This commit is contained in:
1 parent
6210f34740
commit
8613749de1
49 files changed
+279
-79
No files matched your search
@@ -54,6 +54,12 @@ function formatSemver(parts) {
|
||||
|
||||
function bumpVersion(version, bump) {
|
||||
const parts = parseSemver(version)
|
||||
if (bump === "major") {
|
||||
parts.major += 1
|
||||
parts.minor = 0
|
||||
parts.patch = 0
|
||||
return formatSemver(parts)
|
||||
}
|
||||
if (bump === "minor") {
|
||||
parts.minor += 1
|
||||
parts.patch = 0
|
||||
@@ -66,8 +72,8 @@ function bumpVersion(version, bump) {
|
||||
function parseCommit(subject, body = "") {
|
||||
const match = CONVENTIONAL_RE.exec(subject.trim())
|
||||
const breaking =
|
||||
/BREAKING CHANGE/i.test(body) ||
|
||||
(match?.[1] && subject.includes(`${match[1]}!:`))
|
||||
/BREAKING[ -]CHANGE:/i.test(body) ||
|
||||
(match !== null && /^[^:]+!:\s/.test(subject.trim()))
|
||||
|
||||
if (!match) {
|
||||
return { type: "other", scope: null, description: subject.trim(), bump: "patch" }
|
||||
@@ -78,7 +84,9 @@ function parseCommit(subject, body = "") {
|
||||
const description = match[3].trim()
|
||||
let bump = "patch"
|
||||
|
||||
if (breaking || MINOR_TYPES.has(type)) {
|
||||
if (breaking) {
|
||||
bump = "major"
|
||||
} else if (MINOR_TYPES.has(type)) {
|
||||
bump = "minor"
|
||||
} else if (PATCH_TYPES.has(type)) {
|
||||
bump = "patch"
|
||||
@@ -251,7 +259,9 @@ function main() {
|
||||
return
|
||||
}
|
||||
|
||||
const bump = commits.some((commit) => commit.bump === "minor") ? "minor" : "patch"
|
||||
const bump = commits.some((commit) => commit.bump === "major")
|
||||
? "major"
|
||||
: commits.some((commit) => commit.bump === "minor") ? "minor" : "patch"
|
||||
const version = bumpVersion(baseFromTag, bump)
|
||||
const tag = `v${version}`
|
||||
const publishedAt = new Date().toISOString()
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
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 { basename, dirname, join, resolve } from "node:path"
|
||||
import { fileURLToPath } from "node:url"
|
||||
import test from "node:test"
|
||||
|
||||
const source = fileURLToPath(new URL("./compute-release.mjs", import.meta.url))
|
||||
|
||||
const cases = [
|
||||
{ name: "исправление повышает patch", commits: [["fix(ui): исправить отображение"]], version: "1.47.7", bump: "patch" },
|
||||
{ name: "новая возможность повышает minor", commits: [["feat(ui): добавить раздел"]], version: "1.48.0", bump: "minor" },
|
||||
{ name: "breaking со scope повышает major", commits: [["chore(release)!: подготовить вторую основную версию"]], version: "2.0.0", bump: "major" },
|
||||
{ name: "breaking без scope повышает major", commits: [["feat!: заменить контракт"]], version: "2.0.0", bump: "major" },
|
||||
{ name: "footer со пробелом повышает major", commits: [["fix(api): заменить контракт", "BREAKING CHANGE: изменён контракт"]], version: "2.0.0", bump: "major" },
|
||||
{ name: "footer с дефисом повышает major", commits: [["fix(api): заменить контракт", "BREAKING-CHANGE: изменён контракт"]], version: "2.0.0", bump: "major" },
|
||||
{ name: "major имеет приоритет в смешанной истории", commits: [["chore(release)!: подготовить выпуск"], ["feat(ui): добавить раздел"], ["fix(ui): исправить отображение"]], version: "2.0.0", bump: "major" },
|
||||
{ name: "после второй версии сохраняется patch", base: "2.0.0", commits: [["fix(ui): исправить отображение"]], version: "2.0.1", bump: "patch" },
|
||||
{ name: "после второй версии сохраняется minor", base: "2.0.0", commits: [["feat(ui): добавить раздел"]], version: "2.1.0", bump: "minor" },
|
||||
{ name: "готовый тег не выпускается повторно", base: "2.0.0", commits: [], version: "2.0.0", bump: "none" },
|
||||
]
|
||||
|
||||
for (const scenario of cases) test(scenario.name, () => {
|
||||
const parent = resolve(tmpdir())
|
||||
const root = mkdtempSync(join(parent, "mm-release-"))
|
||||
const git = (...args) => execFileSync("git", args, { cwd: root, stdio: "pipe" })
|
||||
try {
|
||||
mkdirSync(join(root, ".ci/scripts"), { recursive: true })
|
||||
copyFileSync(source, join(root, ".ci/scripts/compute-release.mjs"))
|
||||
git("init", "--initial-branch=main")
|
||||
git("config", "user.name", "Release Test")
|
||||
git("config", "user.email", "release@example.invalid")
|
||||
git("config", "core.hooksPath", ".git/hooks")
|
||||
writeFileSync(join(root, "fixture"), "baseline")
|
||||
git("add", "fixture")
|
||||
git("commit", "-m", "chore(test): создать исходную базу")
|
||||
git("tag", `v${scenario.base ?? "1.47.6"}`)
|
||||
for (const [index, [subject, body]] of scenario.commits.entries()) {
|
||||
writeFileSync(join(root, "fixture"), `change ${index}`)
|
||||
git("add", "fixture")
|
||||
git("commit", "-m", subject, ...(body ? ["-m", body] : []))
|
||||
}
|
||||
execFileSync(process.execPath, [join(root, ".ci/scripts/compute-release.mjs")], {
|
||||
cwd: root, stdio: "pipe",
|
||||
env: { ...process.env, BASE_VERSION: "1.0.0", RELEASE_OUTPUT_DIR: ".ci/release", GITHUB_OUTPUT: join(root, "outputs"), GITEA_REPOSITORY: "test/repo", GITEA_SERVER_URL: "https://example.invalid" },
|
||||
})
|
||||
const manifest = JSON.parse(readFileSync(join(root, ".ci/release/release-manifest.json"), "utf8"))
|
||||
const output = readFileSync(join(root, "outputs"), "utf8")
|
||||
assert.equal(manifest.version, scenario.version)
|
||||
assert.equal(manifest.tag, `v${scenario.version}`)
|
||||
assert.equal(manifest.releaseUrl, `https://example.invalid/test/repo/releases/tag/v${scenario.version}`)
|
||||
assert.match(output, new RegExp(`^bump=${scenario.bump}$`, "m"))
|
||||
assert.match(output, new RegExp(`^should_release=${scenario.bump !== "none"}$`, "m"))
|
||||
if (scenario.bump !== "none") assert.match(readFileSync(join(root, ".ci/release/release_notes.md"), "utf8"), new RegExp(`# v${scenario.version.replaceAll(".", "\\.")}`))
|
||||
} finally {
|
||||
assert.equal(dirname(resolve(root)), parent)
|
||||
assert.ok(basename(root).startsWith("mm-release-"))
|
||||
rmSync(root, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
Reference in new issue
Block a user