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
18 changes: 3 additions & 15 deletions .github/workflows/PublishNpm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,23 +77,11 @@ jobs:
with:
node-version: '24' # Includes a version of npm higher than 11.5.1, which is needed for OIDC

- name: Release Tag
run: |
package="$(echo "${{matrix.release.package}}" | sed -E '/^test$/! s/^/@mendix\//')" # Prefix all packages except test
Comment thread
alihcsumer marked this conversation as resolved.
Resolved
release="${{ matrix.release.version }}"
latest="$(npm view $package dist-tags.latest)"
older="$(npx semver@7.8.5 "$latest" "$release" 2>/dev/null | head -n 1)"
tag="latest"
if [[ "$latest" != "$older" ]]; then
tag="$(echo "$release" | sed -E 's/^([0-9]+\.[0-9]+).[0-9]+$/\1-latest/')"
fi
printf 'Using release tag %s' "$tag"
printf 'release_tag=%s' "$tag" >> "$GITHUB_ENV"

- name: Publish package
working-directory:
run: |
cmd="npm publish --tag $release_tag ./packages/${{ matrix.release.package }}/mendix-${{ matrix.release.package }}-${{matrix.release.version }}.tgz"
dist_tag=$(./packages/release-tools/releaseTags.ts @mendix/${{ matrix.release.package }} ${{ matrix.release.version }})

cmd="npm publish --tag $dist_tag ./packages/${{ matrix.release.package }}/mendix-${{ matrix.release.package }}-${{matrix.release.version }}.tgz"
if [[ "${{ matrix.release.package }}" == "test" ]]; then
echo "$cmd"
else
Expand Down
22 changes: 13 additions & 9 deletions packages/release-tools/createRelease.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import {
getPackageInfo,
gh,
type PackageInfo,
ChangelogFileWrapper
ChangelogFileWrapper,
parseArgument
} from "./utils/index.ts";
import { ArgumentError, handleError } from "./utils/errors.ts";
import { handleError } from "./utils/errors.ts";
import { format } from "node:util";
import { ensure } from "./utils/assertions.ts";

const packages = new Map([
[
Expand Down Expand Up @@ -40,13 +42,15 @@ main().catch(e => {
});

async function main(): Promise<void> {
const arg = process.argv[2];
const mendixPackage = packages.get(arg);
if (!mendixPackage)
throw new ArgumentError(format("package name of (%s)", packages.keys().toArray().join(", ")), arg);
const branch = process.argv[3];
if (!branch || !/^(master|version\/.*)$/.test(branch))
throw new ArgumentError("branch name of (master, version/*)", branch);
const mendixPackage = parseArgument("package name", 2, input =>
ensure(packages.get(input), "Unknown package, must be one of " + packages.keys().toArray().join(", "))
);
const branch = parseArgument("branch name", 3, input => {
if (/^(master|version\/.*)$/.test(input)) {
return input;
}
throw new Error("Must match master, version/*");
});

const dirname = new URL("./", import.meta.url).pathname;
const pwtPath = join(dirname, "../", mendixPackage.name);
Expand Down
7 changes: 5 additions & 2 deletions packages/release-tools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
"main": "./createRelease.ts",
"type": "module",
"bin": {
"changelog-parser": "./utils/changelog-parser/cli.ts",
"create-release": "./createRelease.ts",
"changelog-parser": "./utils/changelog-parser/cli.ts"
"release-tags": "./releaseTags.ts"
},
"scripts": {
"release:pwt": "./createRelease.ts pluggable-widgets-tools",
Expand All @@ -23,11 +24,13 @@
"@tsconfig/node22": "^22.0.5",
"@types/node": "^22.18.0",
"@types/node-fetch": "2.6.13",
"@types/semver": "^7.7.1",
"peggy": "^5.1.0",
"typescript": "^5.9.3"
},
"dependencies": {
"node-fetch": "^2.7.0"
"node-fetch": "^2.7.0",
"semver": "^7.8.5"
},
"keywords": [],
"author": "",
Expand Down
36 changes: 36 additions & 0 deletions packages/release-tools/releaseTags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#! /usr/bin/env -S node --experimental-strip-types
import { exec } from "node:child_process";
import { format, promisify } from "node:util";
import { parse, gt } from "semver";
import { handleError } from "./utils/errors.ts";
import { parseArgument } from "./utils/shell.ts";
import { ensure } from "./utils/assertions.ts";

const RELEASEABLE_PACKAGES = ["@mendix/pluggable-widgets-tools", "@mendix/generator-widget"] as const;

async function main() {
const releasePackage = parseArgument("package name", 2, input =>
ensure(
RELEASEABLE_PACKAGES.find(p => p === input),
"Unknown package, must be one of " + RELEASEABLE_PACKAGES.join(", ")
)
);
const releaseVersion = parseArgument("release version", 3, input =>
ensure(parse(input), "Must use the semver format")
);

const { stdout } = await promisify(exec)(`npm show ${releasePackage} dist-tags.latest`);
const latest = parse(stdout.trim());
if (!latest) {
throw Error(`Unable to parse latest released version "${stdout}"`);
}

const tag = gt(releaseVersion, latest) ? "latest" : `${releaseVersion.major}.${releaseVersion.minor}-latest`;

console.log(tag);
}

await main().catch(e => {
handleError(e, format("\nUsage: %s <package-name> <release-version>", process.argv[1]));
process.exit(1);
});
6 changes: 6 additions & 0 deletions packages/release-tools/utils/assertions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export function ensure<T>(input: T | undefined | null, message?: string): T {
if (input === undefined || input === null) {
throw Error(message ?? `Expected a value but received ${input}`);
}
return input;
}
4 changes: 2 additions & 2 deletions packages/release-tools/utils/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ export class UsageError extends Error {

export class ArgumentError extends UsageError {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
constructor(argument: string, received: any) {
super(`Argument "${received}" is not a valid ${argument}`);
constructor(argument: string, received: any, reason?: string) {
super(`Argument "${received}" is not a valid ${argument}${reason ? `: ${reason}` : ""}`);
}
}

Expand Down
21 changes: 21 additions & 0 deletions packages/release-tools/utils/shell.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { exec } from "node:child_process";
import { ArgumentError } from "./errors.ts";
import { argv } from "node:process";

export function execShellCommand(cmd: string | string[], workingDirectory: string = process.cwd()): Promise<string> {
const command = Array.isArray(cmd) ? cmd.join(" && ") : cmd;
Expand All @@ -16,3 +18,22 @@ export function execShellCommand(cmd: string | string[], workingDirectory: strin
});
});
}

export function parseArgument<T>(name: string, position: number, parser: (input: string) => T) {
const value = argv[position];
if (!value) {
throw new ArgumentError(name, value, "Cannot be empty");
}
try {
return parser(value);
} catch (e) {
if (e instanceof Error) {
throw new ArgumentError(name, value, e.message);
}
if (typeof e === "string") {
throw new ArgumentError(name, value, e);
}
console.error(e);
throw new ArgumentError(name, value);
}
}
40 changes: 23 additions & 17 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading