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
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"release": "turbo run release",
"create-gh-release": "turbo run create-gh-release",
"publish-marketplace": "turbo run publish-marketplace",
"version": "ts-node --project ./scripts/tsconfig.json ./scripts/release/BumpVersion.ts",
"version": "pnpm --filter release-utils-internal run version",
"changelog": "pnpm --filter release-utils-internal run changelog",
"validate-staged-widget-versions": "node scripts/validation/validate-versions-staged-files.js"
},
"devDependencies": {
Expand Down
30 changes: 30 additions & 0 deletions packages/tools/release-utils-internal/bin/rui-bump-version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env ts-node-script

import { prompt } from "enquirer";
import { selectPackage } from "../src";
import { getNextVersion, writeVersion } from "../src/bump-version";

import { oraPromise } from "../src/cli-utils";

async function main(): Promise<void> {
const pkg = await selectPackage();
const nextVersion = await getNextVersion(pkg.version);

const { save } = await prompt<{ save: boolean }>({
type: "confirm",
name: "save",
message: "Save changes?"
});

if (save) {
await oraPromise(writeVersion(pkg, nextVersion), "Writing changes...");
console.log("Done.");
} else {
console.log("Exit without changes.");
}
}

main().catch(e => {
console.error(e);
process.exit(1);
});
111 changes: 111 additions & 0 deletions packages/tools/release-utils-internal/bin/rui-changelog-helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#!/usr/bin/env ts-node-script

import { prompt } from "enquirer";
import { getModuleInfo, PackageListing, selectPackage } from "../src";
import { getNextVersion, writeVersion } from "../src/bump-version";
import {
getModuleChangelog,
getWidgetChangelog,
ModuleChangelogFileWrapper,
WidgetChangelogFileWrapper
} from "../src/changelog-parser";
import { LogSection } from "../src/changelog-parser/types";
import { oraPromise } from "../src/cli-utils";

async function getChangelogSections(): Promise<LogSection[]> {
const sections: LogSection[] = [];
let adding = true;

while (adding) {
const { sectionType } = await prompt<{ sectionType: string }>({
type: "autocomplete",
name: "sectionType",
message: "Please select section type",
choices: [
"Added",
"Changed",
"Deprecated",
"Removed",
"Fixed",
"Security",
"Breaking changes",
"Documentation"
]
});

const { message } = await prompt<{ message: string }>({
type: "input",
name: "message",
message: "Message"
});

sections.push({
type: sectionType as LogSection["type"],
logs: [message]
});

const { addMore } = await prompt<{ addMore: boolean }>({
type: "confirm",
name: "addMore",
message: "Add one more record?"
});

adding = addMore;
}

return sections;
}

async function selectNextVersion(currentVersion: string): Promise<string | undefined> {
const { bump } = await prompt<{ bump: boolean }>({
type: "confirm",
name: "bump",
message: "Would you like to bump the package version?"
});

if (bump) {
return getNextVersion(currentVersion);
}

return undefined;
}

async function writeChanges(pkg: PackageListing, sections: LogSection[], nextVersion?: string): Promise<void> {
let changelog: WidgetChangelogFileWrapper | ModuleChangelogFileWrapper;
try {
changelog = await getWidgetChangelog(pkg.path);
} catch {
const module = await getModuleInfo(pkg.path);
changelog = await getModuleChangelog(pkg.path, module.mxpackage.name);
}

changelog.addUnreleasedSections(sections).save();

if (nextVersion) {
await writeVersion(pkg, nextVersion);
}
}

async function main(): Promise<void> {
const pkg = await selectPackage();
const sections = await getChangelogSections();
const nextVersion = await selectNextVersion(pkg.version);

const { save } = await prompt<{ save: boolean }>({
type: "confirm",
name: "save",
message: "Save changes?"
});

if (save) {
await oraPromise(writeChanges(pkg, sections, nextVersion), "Writing changes...");
console.log("Done.");
} else {
console.log("Exit without changes.");
}
}

main().catch(e => {
console.error(e);
process.exit(1);
});
7 changes: 6 additions & 1 deletion packages/tools/release-utils-internal/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,21 @@
"lint": "eslint --config ../../../.eslintrc.js --ext .jsx,.js,.ts,.tsx src/",
"compile:parser:widget": "peggy -o ./src/changelog-parser/parser/module/module.js ./src/changelog-parser/parser/module/module.pegjs",
"compile:parser:module": "peggy -o ./src/changelog-parser/parser/widget/widget.js ./src/changelog-parser/parser/widget/widget.pegjs",
"prepare": "pnpm run compile:parser:widget && pnpm run compile:parser:module && tsc"
"prepare": "pnpm run compile:parser:widget && pnpm run compile:parser:module && tsc",
"changelog": "ts-node bin/rui-changelog-helper.ts",
"version": "ts-node bin/rui-bump-version.ts"
},
"devDependencies": {
"@types/cross-zip": "^4.0.0",
"@types/node-fetch": "2.6.1",
"chalk": "^4.1.2",
"cross-zip": "^4.0.0",
"enquirer": "^2.3.6",
"eslint": "^7.20.0",
"execa": "^5.1.1",
"fast-xml-parser": "^4.0.1",
"node-fetch": "^2.6.1",
"ora": "^5.4.1",
"peggy": "^1.2.0",
"shelljs": "^0.8.4",
"ts-node": "^9.0.0",
Expand Down
82 changes: 82 additions & 0 deletions packages/tools/release-utils-internal/src/bump-version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import chalk from "chalk";
import { spawnSync } from "child_process";
import { prompt } from "enquirer";
import { promises as fs } from "fs";
import { join } from "path";
import { nextTick } from "process";
import { PackageListing } from "./monorepo";

export type BumpVersionType = "patch" | "minor" | "major" | string;

export function getNewVersion(bumpVersionType: BumpVersionType, currentVersion: string): string {
const [major, minor, patch] = currentVersion.split(".");
switch (bumpVersionType) {
case "patch":
return [major, minor, Number(patch) + 1].join(".");
case "minor":
return [major, Number(minor) + 1, 0].join(".");
case "major":
return [Number(major) + 1, 0, 0].join(".");
default:
return bumpVersionType;
}
}

export function bumpPackageJson(path: string, version: string): void {
spawnSync("npm", ["version", version], { cwd: path });
}

export async function bumpXml(path: string, version: string): Promise<boolean> {
const packageXmlFile = join(path, "src", "package.xml");
try {
const content = await fs.readFile(packageXmlFile);
if (content) {
const newContent = content.toString().replace(/version=.+xmlns/, `version="${version}" xmlns`);
await fs.writeFile(packageXmlFile, newContent);
return true;
}
return false;
} catch (e) {
throw new Error("package.xml not found");
}
}

export async function writeVersion(pkg: PackageListing, version: string): Promise<void> {
bumpPackageJson(pkg.path, version);
try {
await bumpXml(pkg.path, version);
} catch {
nextTick(() => {
const msg = `[WARN] Update version: package ${pkg.name} is missing package.xml, skip`;
console.warn(chalk.yellow(msg));
});
}
}

export async function selectBumpVersionType(): Promise<BumpVersionType> {
const { bumpType } = await prompt<{ bumpType: string }>({
type: "autocomplete",
name: "bumpType",
message: "Want to bump?",
choices: ["major", "minor", "patch", "set manually"]
});

if (bumpType === "set manually") {
const { nextVersion } = await prompt<{ nextVersion: string }>({
type: "input",
name: "nextVersion",
message: "Set package version to"
});

return nextVersion;
} else {
return bumpType;
}
}

export async function getNextVersion(currentVersion: string): Promise<string> {
const bumpVersionType = await selectBumpVersionType();
const nextVersion = getNewVersion(bumpVersionType, currentVersion);
console.log(chalk.green(`Version change: ${currentVersion} => ${nextVersion}`));
return nextVersion;
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,28 @@ function formatDate(date: Date): string {
.padStart(2, "0")}`;
}

function mergeUnreleased<T extends UnreleasedVersionEntry>(unreleased: T, sections: LogSection[]): T {
const currentTypes = unreleased.sections.map(s => s.type);
const incomingTypes = sections.map(s => s.type);
const uniqueTypes = new Set([...currentTypes, ...incomingTypes]);

const nextSections = Array.from(uniqueTypes).map(type => {
const section = unreleased.sections.find(s => s.type === type) ?? {
type,
logs: []
};

const incomingLogs = sections.flatMap(s => (s.type === type ? s.logs : []));

return { type: section.type, logs: [...section.logs, ...incomingLogs] };
});

return {
...unreleased,
sections: nextSections
};
}

export class WidgetChangelogFileWrapper {
changelog: WidgetChangelogFile;

Expand Down Expand Up @@ -139,6 +161,18 @@ export class WidgetChangelogFileWrapper {
);
}

addUnreleasedSections(sections: LogSection[]): WidgetChangelogFileWrapper {
const [unreleased, ...rest] = this.changelog.content;

return new WidgetChangelogFileWrapper(
{
header: this.changelog.header,
content: [mergeUnreleased(unreleased, sections), ...rest]
},
this.changelogPath
);
}

static fromFile(filePath: string): WidgetChangelogFileWrapper {
return new WidgetChangelogFileWrapper(
parseWidgetChangelogFile(readFileSync(filePath).toString(), { Version }),
Expand Down Expand Up @@ -215,6 +249,19 @@ export class ModuleChangelogFileWrapper {
);
}

addUnreleasedSections(sections: LogSection[]): ModuleChangelogFileWrapper {
const [unreleased, ...rest] = this.changelog.content;

return new ModuleChangelogFileWrapper(
{
header: this.changelog.header,
content: [mergeUnreleased(unreleased, sections), ...rest],
moduleName: this.moduleName
},
this.changelogPath
);
}

addUnreleasedSubcomponents(subcomponents: SubComponentEntry[]): ModuleChangelogFileWrapper {
const [unreleased, ...releasedContent] = this.changelog.content;

Expand Down
9 changes: 9 additions & 0 deletions packages/tools/release-utils-internal/src/cli-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import ora from "ora";

export async function oraPromise<T>(task: Promise<T>, msg: string): Promise<T> {
const spinner = ora(msg);
spinner.start();
const r = await task;
spinner.stop();
return r;
}
21 changes: 21 additions & 0 deletions packages/tools/release-utils-internal/src/monorepo.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { prompt } from "enquirer";
import { oraPromise } from "./cli-utils";
import { exec, find, mkdir, cp } from "./shell";

type DependencyName = string;
Expand Down Expand Up @@ -37,3 +39,22 @@ export async function copyMpkFiles(packageNames: string[], dest: string): Promis
mkdir("-p", dest);
cp(paths, dest);
}

export async function selectPackage(): Promise<PackageListing> {
const pkgs = await oraPromise(listPackages(["'*'", "!web-widgets"]), "Loading packages...");

const { packageName } = await prompt<{ packageName: string }>({
type: "autocomplete",
name: "packageName",
message: "Please select package",
choices: pkgs.map(pkg => pkg.name)
});

const pkg = pkgs.find(p => p.name === packageName);

if (!pkg) {
throw new Error(`Unable to find package meta for ${packageName}`);
}

return pkg;
}
Loading