Skip to content

Commit

Permalink
feat: support manual version updates (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
kt3k authored May 7, 2024
1 parent ac20c5c commit 6739ab3
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 6 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ jobs:
- uses: denoland/setup-deno@v1
- run: deno fmt --check
- run: deno lint
- run: |
- name: Test
run: |
git fetch origin # some branches are necessary for testing
deno task cov
- uses: codecov/codecov-action@v4
Expand Down
18 changes: 15 additions & 3 deletions mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,18 +80,27 @@ export async function bumpWorkspaces(
}: BumpWorkspaceOptions = {},
) {
const now = new Date();
const [configPath, modules] = await getWorkspaceModules(root);
start ??= await $`git describe --tags --abbrev=0`.text();
const newBranchName = createReleaseBranchName(now);
base ??= await $`git branch --show-current`.text();
if (!base) {
console.error("The current branch is not found.");
Deno.exit(1);
}

await $`git checkout ${start}`;
const [_oldConfigPath, oldModules] = await getWorkspaceModules(root);
await $`git checkout -`;
await $`git checkout ${base}`;
const [configPath, modules] = await getWorkspaceModules(root);
await $`git checkout -`;

const newBranchName = createReleaseBranchName(now);
releaseNotePath = join(root, releaseNotePath);

const text =
await $`git --no-pager log --pretty=format:${separator}%H%B ${start}..${base}`
.text();

const commits = text.split(separator).map((commit) => {
const hash = commit.slice(0, 40);
commit = commit.slice(40);
Expand All @@ -103,7 +112,8 @@ export async function bumpWorkspaces(
const body = commit.slice(i + 1).trim();
return { hash, subject, body };
});
commits.shift();
commits.shift(); // drop the first empty item

console.log(
`Found ${cyan(commits.length.toString())} commits between ${
magenta(start)
Expand Down Expand Up @@ -147,9 +157,11 @@ export async function bumpWorkspaces(
let denoJson = await Deno.readTextFile(configPath);
for (const summary of summaries) {
const module = getModule(summary.module, modules)!;
const oldModule = getModule(summary.module, oldModules);
const [denoJson_, versionUpdate] = await applyVersionBump(
summary,
module,
oldModule,
denoJson,
dryRun === true,
);
Expand Down
56 changes: 54 additions & 2 deletions util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import {
increment,
parse as parseSemVer,
} from "@std/semver";
import { red } from "@std/fmt/colors";

export type VersionUpdate = "major" | "minor" | "patch";
export type VersionUpdate = "major" | "minor" | "patch" | "prerelease";

export type Commit = {
subject: string;
Expand Down Expand Up @@ -234,7 +235,7 @@ export async function getWorkspaceModules(
const workspaces = denoConfig.workspaces;

if (!Array.isArray(workspaces)) {
console.log("deno.json doesn't have workspaces field.");
console.log(red("Error") + " deno.json doesn't have workspaces field.");
Deno.exit(1);
}

Expand Down Expand Up @@ -277,13 +278,64 @@ export function checkModuleName(
};
}

export function calcVersionDiff(
newVersionStr: string,
oldVersionStr: string,
): VersionUpdate {
const newVersion = parseSemVer(newVersionStr);
const oldVersion = parseSemVer(oldVersionStr);
if (newVersion.prerelease && newVersion.prerelease.length > 0) {
return "prerelease";
} else if (newVersion.major !== oldVersion.major) {
return "major";
} else if (newVersion.minor !== oldVersion.minor) {
return "minor";
} else if (newVersion.patch !== oldVersion.patch) {
return "patch";
} else {
throw new Error(
`Unexpected manual version update: ${oldVersion} -> ${newVersion}`,
);
}
}

/** Apply the version bump to the file system. */
export async function applyVersionBump(
summary: VersionBumpSummary,
module: WorkspaceModule,
oldModule: WorkspaceModule | undefined,
denoJson: string,
dryRun = false,
): Promise<[denoJson: string, VersionUpdateResult]> {
if (!oldModule) {
// The module is newly added
console.info(`New module ${module.name} detected.`);
const diff = calcVersionDiff(module.version, "0.0.0");
summary.version = diff;
return [denoJson, {
from: "0.0.0",
to: module.version,
diff,
summary,
path: module[pathProp],
}];
}
if (oldModule.version !== module.version) {
// The version is manually updated
console.info(
`Manaul version update detected for ${module.name}: ${oldModule.version} -> ${module.version}`,
);

const diff = calcVersionDiff(module.version, oldModule.version);
summary.version = diff;
return [denoJson, {
from: oldModule.version,
to: module.version,
diff,
summary,
path: module[pathProp],
}];
}
const oldVersionStr = module.version;
const oldVersion = parseSemVer(oldVersionStr);
let diff = summary.version;
Expand Down
69 changes: 69 additions & 0 deletions util_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,7 @@ Deno.test("applyVersionBump() updates the version of the given module", async ()
commits: [],
},
{ name: "@scope/foo", version: "1.0.0", [pathProp]: "foo/deno.json" },
{ name: "@scope/foo", version: "1.0.0", [pathProp]: "foo/deno.json" },
`{
"imports": {
"scope/foo": "jsr:@scope/foo@^1.0.0",
Expand Down Expand Up @@ -722,6 +723,7 @@ Deno.test("applyVersionBump() consider major bump for 0.x version as minor bump"
commits: [],
},
{ name: "@scope/foo", version: "0.0.0", [pathProp]: "foo/deno.jsonc" },
{ name: "@scope/foo", version: "0.0.0", [pathProp]: "foo/deno.jsonc" },
`{
"imports": {
"scope/foo": "jsr:@scope/foo@^0.0.0",
Expand All @@ -748,6 +750,72 @@ Deno.test("applyVersionBump() consider major bump for 0.x version as minor bump"
);
});

Deno.test("applyVersionBump() respect manual version upgrade if the version between start and base is different", async () => {
const [denoJson, updateResult] = await applyVersionBump(
{
module: "foo",
version: "minor", // This version is ignored, instead manually given version is used for calculating actual version diff
commits: [],
},
{ name: "@scope/foo", version: "1.0.0-rc.1", [pathProp]: "foo/deno.jsonc" },
{ name: "@scope/foo", version: "0.224.0", [pathProp]: "foo/deno.jsonc" },
`{
"imports": {
"scope/foo": "jsr:@scope/foo@^1.0.0-rc.1",
"scope/bar": "jsr:@scope/bar@^1.0.0"
}
}`,
true,
);
assertEquals(updateResult.from, "0.224.0");
assertEquals(updateResult.to, "1.0.0-rc.1");
assertEquals(updateResult.diff, "prerelease");
assertEquals(
denoJson,
`{
"imports": {
"scope/foo": "jsr:@scope/foo@^1.0.0-rc.1",
"scope/bar": "jsr:@scope/bar@^1.0.0"
}
}`,
);
});

Deno.test("applyVersionBump() works for new module (the case when oldModule is undefined)", async () => {
const [denoJson, updateResult] = await applyVersionBump(
{
module: "foo",
version: "patch", // <= this version is ignored, instead manually given version is used for calculating actual version diff
commits: [],
},
{ name: "@scope/foo", version: "0.1.0", [pathProp]: "foo/deno.jsonc" },
undefined,
`{
"imports": {
"scope/foo": "jsr:@scope/foo@^0.1.0",
"scope/foo/": "jsr:@scope/foo@^0.1.0/",
"scope/bar": "jsr:@scope/bar@^1.0.0",
"scope/bar/": "jsr:@scope/bar@^1.0.0/"
}
}`,
true,
);
assertEquals(updateResult.from, "0.0.0");
assertEquals(updateResult.to, "0.1.0");
assertEquals(updateResult.diff, "minor");
assertEquals(
denoJson,
`{
"imports": {
"scope/foo": "jsr:@scope/foo@^0.1.0",
"scope/foo/": "jsr:@scope/foo@^0.1.0/",
"scope/bar": "jsr:@scope/bar@^1.0.0",
"scope/bar/": "jsr:@scope/bar@^1.0.0/"
}
}`,
);
});

async function createVersionUpdateResults(
versionBumps: VersionBump[],
modules: WorkspaceModule[],
Expand All @@ -763,6 +831,7 @@ async function createVersionUpdateResults(
const [_denoJson, versionUpdate] = await applyVersionBump(
summary,
getModule(summary.module, modules)!,
getModule(summary.module, modules)!,
"",
true,
);
Expand Down

0 comments on commit 6739ab3

Please sign in to comment.