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
7 changes: 2 additions & 5 deletions automation/utils/bin/rui-create-gh-release.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { updateChangelogsAndCreatePR } from "../src/changelog.ts";
import { gh } from "../src/github.ts";
import { addRemoteWithAuthentication } from "../src/index.ts";
import { getPublishedInfo } from "../src/package-info.ts";
import { exec, find } from "../src/shell.ts";
import { exec, findFiles } from "../src/shell.ts";

async function resolveChangelogBranch(releaseTag: string, remoteName: string): Promise<string> {
const { stdout } = await exec(`git rev-parse --abbrev-ref HEAD`, { stdio: "pipe" });
Expand Down Expand Up @@ -75,10 +75,7 @@ async function main(): Promise<void> {
// Create release
console.log("Creating Github release...");

const mpk = find(join(path, "dist", version, "*.mpk")).toString();
if (!mpk) {
throw new Error("MPK file not found");
}
const [mpk] = findFiles(join(path, "dist", version, "*.mpk"));

await gh.createGithubReleaseFrom({
title: `${appName} v${version}`,
Expand Down
5 changes: 2 additions & 3 deletions automation/utils/bin/rui-include-oss-in-artifact.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
#!/usr/bin/env node

import { mkdtemp, stat } from "node:fs/promises";
import { mkdtemp, rm, stat } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import chalk from "chalk";
import { gh } from "../src/github.ts";
import { includeReadmeOssIntoMpk } from "../src/oss-clearance.ts";
import { rm } from "../src/shell.ts";

/**
* This script processes GitHub release artifacts to include READMEOSS HTML files into MPK files.
Expand Down Expand Up @@ -107,7 +106,7 @@ async function main(): Promise<void> {
} finally {
// Cleanup temp files
console.log(chalk.gray("🧹 Cleaning up temporary files..."));
await rm("-rf", tmpFolder);
await rm(tmpFolder, { recursive: true, force: true });
}
}

Expand Down
10 changes: 3 additions & 7 deletions automation/utils/bin/rui-oss-clearance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
// Enable quiet mode for fetch calls to reduce logging noise
process.env.FETCH_QUIET = "true";

import { homedir } from "node:os";
import { cp, mkdtemp, rm, stat } from "node:fs/promises";
import { homedir, tmpdir } from "node:os";
import { basename, join } from "path";
import chalk from "chalk";
import enquirer from "enquirer";
Expand Down Expand Up @@ -331,8 +332,6 @@ async function handleIncludeAsEmbeddedAction(release: GitHubDraftRelease, readme
printInfo(`Found MPK: ${mpkAsset.name} (${mpkAsset.size} bytes)`);

// Create temp folder
const { mkdtemp } = await import("node:fs/promises");
const { tmpdir } = await import("node:os");
const tmpFolder = await mkdtemp(join(tmpdir(), "mpk-oss-embed-"));
const mpkPath = join(tmpFolder, mpkAsset.name);
const htmlPath = join(tmpFolder, basename(readmeToInclude));
Expand All @@ -344,7 +343,6 @@ async function handleIncludeAsEmbeddedAction(release: GitHubDraftRelease, readme
printProgressCheck("Download completed");

// Copy HTML to temp folder
const { cp } = await import("../src/shell.ts");
await cp(readmeToInclude, htmlPath);

// Embed HTML into MPK
Expand All @@ -353,7 +351,6 @@ async function handleIncludeAsEmbeddedAction(release: GitHubDraftRelease, readme
printProgressCheck("Merge completed");

// Get modified MPK size
const { stat } = await import("node:fs/promises");
const modifiedMpkStats = await stat(mpkPath);
const sizeDiff = modifiedMpkStats.size - mpkAsset.size;
printInfo(`Modified MPK size: ${modifiedMpkStats.size} bytes (+${sizeDiff} bytes)`);
Expand Down Expand Up @@ -400,8 +397,7 @@ async function handleIncludeAsEmbeddedAction(release: GitHubDraftRelease, readme
} finally {
// Cleanup temp files
printProgress("Cleaning up temporary files...");
const { rm } = await import("../src/shell.ts");
await rm("-rf", tmpFolder);
await rm(tmpFolder, { recursive: true, force: true });
}
}

Expand Down
1 change: 0 additions & 1 deletion automation/utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@
"node-fetch": "^2.7.0",
"ora": "^5.4.1",
"peggy": "5.1.0",
"shelljs": "^0.8.5",
"zod": "^3.25.76"
}
}
13 changes: 8 additions & 5 deletions automation/utils/src/monorepo.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { cp, mkdir } from "node:fs/promises";
import { basename } from "node:path";
import enquirer from "enquirer";
import { oraPromise } from "./cli-utils.ts";
import { cp, exec, find, mkdir } from "./shell.ts";
import { exec, findFiles } from "./shell.ts";

const { prompt } = enquirer;

Expand Down Expand Up @@ -40,14 +42,15 @@ export async function resolvePackagePath(npmPackageName: string): Promise<string

export async function getMpkPaths(packageNames: string[]): Promise<string[]> {
const packages = await listPackages(packageNames);
const paths = [...find(packages.map(p => `${p.path}/dist/${p.version}/*.mpk`))];
return paths;
return findFiles(...packages.map(p => `${p.path}/dist/${p.version}/*.mpk`));
}

export async function copyMpkFiles(packageNames: string[], dest: string): Promise<void> {
const paths = await getMpkPaths(packageNames);
mkdir("-p", dest);
cp(paths, dest);
await mkdir(dest, { recursive: true });
for (const path of paths) {
await cp(path, `${dest}/${basename(path)}`);
}
}

export async function selectPackage(): Promise<PackageListing> {
Expand Down
6 changes: 3 additions & 3 deletions automation/utils/src/mpk.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { basename, join } from "path";
import type { ModuleInfo } from "./package-info.ts";
import { exec, find } from "./shell.ts";
import { exec, findFiles } from "./shell.ts";
import type { Version } from "./version.ts";

async function ensureMxBuildDockerImageExists(version: string): Promise<void> {
Expand Down Expand Up @@ -28,7 +28,7 @@ export async function createModuleMpkInDocker(

console.log(`Creating module ${moduleName} using mxbuild:${version}...`);
// Build testProject via mxbuild
const mprPath = find(`${sourceDir}/*.mpr`)[0];
const [mprPath] = findFiles(`${sourceDir}/*.mpr`);
const projectFile = basename(mprPath);
const args = [
// update widgets
Expand Down Expand Up @@ -61,5 +61,5 @@ export async function createMPK(tmpFolder: string, info: ModuleInfo, excludeFile
info.marketplace.minimumMXVersion,
excludeFilesRegExp
);
return find(join(tmpFolder, info.mpkName))[0];
return findFiles(join(tmpFolder, info.mpkName))[0];
}
18 changes: 9 additions & 9 deletions automation/utils/src/oss-clearance.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { createHash } from "node:crypto";
import { createReadStream } from "node:fs";
import { mkdtemp, stat } from "node:fs/promises";
import { cp, mkdir, mkdtemp, rm, stat } from "node:fs/promises";
import { homedir, tmpdir } from "node:os";
import { pipeline } from "node:stream/promises";
import { basename, join, parse } from "path";
import { globSync } from "glob";
import type { GitHubReleaseAsset } from "./github.ts";
import { chmod, cp, exec, mkdir, mv, rm, unzip, zip } from "./shell.ts";
import { exec, makeWritableRecursive, move, unzip, zip } from "./shell.ts";

export async function computeSha256(filePath: string): Promise<string> {
const hash = createHash("sha256");
Expand Down Expand Up @@ -64,7 +64,7 @@ export async function createSBomGeneratorFolderStructure(
): Promise<[folder: string, assetPath: string]> {
const tmpFolder = await mkdtemp(join(tmpdir(), "tmp_OSS_Clearance_Artifacts_"));
const artifactsFolder = join(tmpFolder, "SBOM_GENERATOR", assetNameAndVersion);
await mkdir("-p", artifactsFolder);
await mkdir(artifactsFolder, { recursive: true });
return [tmpFolder, join(artifactsFolder, `${assetNameAndVersion}.mpk`)];
}

Expand Down Expand Up @@ -92,27 +92,27 @@ export async function generateSBomArtifactsInFolder(
const ossArtifactZip = join(resultsFolder, archiveName);

// move to final destination
await mv(ossArtifactZip, finalPath);
await move(ossArtifactZip, finalPath);

// removing tmp folder
await rm("-rf", tmpFolder);
await rm(tmpFolder, { recursive: true, force: true });
}

export async function includeReadmeOssIntoMpk(readmeOssPath: string, mpkPath: string): Promise<void> {
const mpkEntry = parse(mpkPath);
const unzipTarget = join(mpkEntry.dir, "tmp");

// unzip
rm("-rf", unzipTarget);
await rm(unzipTarget, { recursive: true, force: true });
await unzip(mpkPath, unzipTarget);
chmod("-R", "a+rw", unzipTarget);
await makeWritableRecursive(unzipTarget);

// Copy the READMEOSS file to the target directory
cp(readmeOssPath, unzipTarget);
await cp(readmeOssPath, join(unzipTarget, basename(readmeOssPath)));

// zip it back
await zip(unzipTarget, mpkPath);

// remove tmp folder
rm("-rf", unzipTarget);
await rm(unzipTarget, { recursive: true, force: true });
}
63 changes: 44 additions & 19 deletions automation/utils/src/shell.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,62 @@
import { globSync } from "node:fs";
import { access, chmod, cp, lstat, readdir, rename, rm } from "node:fs/promises";
import { join } from "node:path";
import crossZip from "cross-zip";
import execa from "execa";
import shelljs from "shelljs";

// shelljs is CommonJS, so named imports are not available under native ESM
const { config } = shelljs;

// Enable fast fail for all shelljs commands
config.fatal = true;
// Only the file system helpers in use are exported; commands run through execa (see exec below).
// KNOWN ISSUES:
// - mv don't throw error https://github.com/shelljs/shelljs/issues/878
export const { chmod, cp, find, mkdir, mv, rm } = shelljs;
const { ls, popd, pushd } = shelljs;

export function exec(command: string, options?: execa.Options): execa.ExecaChildProcess {
const { stdio = "inherit", ...execaOptions } = options ?? {};

return execa(command, { shell: true, stdio, ...execaOptions });
}

export function ensureFileExists(file: string): void {
const silentState = config.silent;
config.silent = true;
ls(file);
config.silent = silentState;
export async function ensureFileExists(file: string): Promise<void> {
await access(file);
}

/** Expand glob patterns, failing when a pattern matches nothing. */
export function findFiles(...patterns: string[]): string[] {
return patterns.flatMap(pattern => {
const matches = globSync(pattern);
if (matches.length === 0) {
throw new Error(`No files match '${pattern}'`);
}
return matches;
});
}

/** Move a file or directory, falling back to copy and remove when rename crosses file systems. */
export async function move(source: string, dest: string): Promise<void> {
try {
await rename(source, dest);
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "EXDEV") {
throw error;
}
await cp(source, dest, { recursive: true });
await rm(source, { recursive: true, force: true });
}
}

/** Add read and write permissions for everyone to a directory and everything inside it (`chmod -R a+rw`). */
export async function makeWritableRecursive(dir: string): Promise<void> {
const entries = await readdir(dir, { recursive: true });
for (const path of [dir, ...entries.map(entry => join(dir, entry))]) {
const stats = await lstat(path);
if (!stats.isSymbolicLink()) {
// eslint-disable-next-line no-bitwise -- permission bits are a bitmask
await chmod(path, stats.mode | 0o666);
}
}
}

export async function zip(dir: string, fileName: string): Promise<void> {
return new Promise((resolve, reject) => {
// cross-zip has no cwd option, it zips relative to the process directory
pushd("-q", dir);
const cwd = process.cwd();
process.chdir(dir);
crossZip.zip(".", fileName, err => {
popd("-q");
process.chdir(cwd);
if (err) {
reject(err);
} else {
Expand Down
Loading
Loading