scripts/preflight.mjs
scripts/preflight.mjsBrowse 3 files
1,412 tokens
5,591 bytes
Token encoding: o200k_base
Snapshot 506f736
← Back to SKILL.md
1#!/usr/bin/env node2 3import { spawn } from 'node:child_process';4import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises';5import { tmpdir } from 'node:os';6import path from 'node:path';7 8const { console, fetch, process } = globalThis;9 10function readArg(name) {11 const index = process.argv.indexOf(name);12 if (index === -1 || !process.argv[index + 1]) {13 throw new Error(`Missing required argument: ${name}`);14 }15 return process.argv[index + 1];16}17 18async function fetchJson(url) {19 const response = await fetch(url, {20 headers: { 'user-agent': 'openai-agents-js-pnpm-upgrade-preflight' },21 });22 if (!response.ok) {23 throw new Error(`Request failed (${response.status}): ${url}`);24 }25 return response.json();26}27 28function run(command, args, options) {29 return new Promise((resolve, reject) => {30 const child = spawn(command, args, {31 ...options,32 stdio: 'inherit',33 });34 child.on('error', reject);35 child.on('close', (code, signal) => {36 if (code === 0) {37 resolve();38 return;39 }40 reject(41 new Error(42 `${command} exited with ${signal ? `signal ${signal}` : `code ${code}`}`,43 ),44 );45 });46 });47}48 49async function loadBootstrap(actionRef, options) {50 const lock = await fetchJson(51 `https://raw.githubusercontent.com/pnpm/action-setup/${actionRef}/src/install-pnpm/bootstrap/${options.lockFile}`,52 );53 const version = lock.packages?.[options.packageKey]?.version;54 if (!version) {55 throw new Error(56 `Could not resolve the ${options.label} bootstrap version from pnpm/action-setup@${actionRef}.`,57 );58 }59 return { ...options, lock, version };60}61 62async function runBootstrapProbe(bootstrap, targetVersion) {63 const workDir = await mkdtemp(64 path.join(tmpdir(), `pnpm-upgrade-preflight-${bootstrap.label}-`),65 );66 try {67 await writeFile(68 path.join(workDir, 'package.json'),69 `${JSON.stringify({ private: true, dependencies: { [bootstrap.packageName]: bootstrap.version } }, null, 2)}\n`,70 );71 await writeFile(72 path.join(workDir, 'package-lock.json'),73 `${JSON.stringify(bootstrap.lock, null, 2)}\n`,74 );75 await run('npm', ['ci'], {76 cwd: workDir,77 env: process.env,78 shell: process.platform === 'win32',79 });80 81 const pnpmHome =82 bootstrap.label === 'standalone' && process.platform === 'win32'83 ? path.join(workDir, 'node_modules', '@pnpm', 'exe')84 : path.join(workDir, 'node_modules', '.bin');85 const xdgDataHome = path.join(workDir, 'xdg-data');86 await mkdir(xdgDataHome, { recursive: true });87 const bootstrapPnpm =88 bootstrap.label === 'standalone'89 ? path.join(90 workDir,91 'node_modules',92 '@pnpm',93 'exe',94 process.platform === 'win32' ? 'pnpm.exe' : 'pnpm',95 )96 : path.join(workDir, 'node_modules', 'pnpm', 'bin', 'pnpm.mjs');97 const command =98 bootstrap.label === 'standalone' ? bootstrapPnpm : process.execPath;99 const args =100 bootstrap.label === 'standalone'101 ? ['self-update', targetVersion]102 : [bootstrapPnpm, 'self-update', targetVersion];103 await run(command, args, {104 cwd: workDir,105 env: {106 ...process.env,107 PNPM_HOME: pnpmHome,108 XDG_DATA_HOME: xdgDataHome,109 },110 });111 } finally {112 await rm(workDir, { recursive: true, force: true });113 }114 115 console.log(116 `Preflight passed for the ${bootstrap.label} pnpm/action-setup bootstrap ${bootstrap.version}.`,117 );118}119 120function assertDependencyFreeManifest(manifest, version) {121 const dependencies = Object.keys(manifest.dependencies ?? {});122 const devDependencies = Object.keys(manifest.devDependencies ?? {});123 if (dependencies.length === 0 && devDependencies.length === 0) return;124 125 const summarize = (names) =>126 names.length === 0127 ? 'none'128 : `${names.length}: ${names.slice(0, 10).join(', ')}${names.length > 10 ? ', ...' : ''}`;129 throw new Error(130 [131 `pnpm@${version} has an unexpected published manifest.`,132 `dependencies (${summarize(dependencies)})`,133 `devDependencies (${summarize(devDependencies)})`,134 'pnpm bundles its runtime dependencies; aborting before the pnpm/action-setup self-installer runs.',135 ].join('\n'),136 );137}138 139async function main() {140 const version = readArg('--version');141 const actionRef = readArg('--action-ref');142 if (!/^[0-9a-f]{40}$/.test(actionRef)) {143 throw new Error('--action-ref must be a 40-character commit SHA.');144 }145 146 const manifest = await fetchJson(147 `https://registry.npmjs.org/pnpm/${encodeURIComponent(version)}`,148 );149 if (manifest.version !== version) {150 throw new Error(151 `Registry returned pnpm@${manifest.version ?? 'unknown'} instead of pnpm@${version}.`,152 );153 }154 assertDependencyFreeManifest(manifest, version);155 156 const bootstraps = await Promise.all([157 loadBootstrap(actionRef, {158 label: 'regular',159 lockFile: 'pnpm-lock.json',160 packageKey: 'node_modules/pnpm',161 packageName: 'pnpm',162 }),163 loadBootstrap(actionRef, {164 label: 'standalone',165 lockFile: 'exe-lock.json',166 packageKey: 'node_modules/@pnpm/exe',167 packageName: '@pnpm/exe',168 }),169 ]);170 171 for (const bootstrap of bootstraps) {172 await runBootstrapProbe(bootstrap, version);173 }174 175 console.log(176 `Preflight passed: all pnpm/action-setup bootstrap paths can self-update to pnpm ${version}.`,177 );178}179 180main().catch((error) => {181 console.error(error instanceof Error ? error.message : error);182 process.exitCode = 1;183});184