From ba94f068c4b341f8c5dec28f7e05eaad94a97d83 Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Wed, 11 Dec 2024 13:04:11 +0100 Subject: [PATCH] Build: Always cleanup verdaccio ports before running registry --- scripts/run-registry.ts | 20 +++++++++--- scripts/utils/kill-port.ts | 67 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 scripts/utils/kill-port.ts diff --git a/scripts/run-registry.ts b/scripts/run-registry.ts index 82d6513f40a2..a72a2a75ab14 100755 --- a/scripts/run-registry.ts +++ b/scripts/run-registry.ts @@ -15,6 +15,7 @@ import { parseConfigFile, runServer } from 'verdaccio'; import { maxConcurrentTasks } from './utils/concurrency'; import { PACKS_DIRECTORY } from './utils/constants'; +import { killPort } from './utils/kill-port'; import { getWorkspaces } from './utils/workspace'; program @@ -154,8 +155,17 @@ const publish = async (packages: { name: string; location: string }[], url: stri ); }; +const VERDACCIO_SERVER_PORT = 6001; +const VERDACCIO_REGISTRY_PORT = 6002; + +const cleanupVerdaccioProcesses = async () => { + await killPort([VERDACCIO_SERVER_PORT, VERDACCIO_REGISTRY_PORT]); +}; + const run = async () => { - const verdaccioUrl = `http://localhost:6001`; + await cleanupVerdaccioProcesses(); + + const verdaccioUrl = `http://localhost:${VERDACCIO_SERVER_PORT}`; logger.log(`📐 reading version of storybook`); logger.log(`🚛 listing storybook packages`); @@ -192,7 +202,7 @@ const run = async () => { '-e', 'test@test.com', '-r', - 'http://localhost:6002', + `http://localhost:${VERDACCIO_REGISTRY_PORT}`, ], { cwd: root, @@ -204,7 +214,7 @@ const run = async () => { ); if (opts.publish) { - await publish(packages, 'http://localhost:6002'); + await publish(packages, `http://localhost:${VERDACCIO_REGISTRY_PORT}`); } await rm(join(root, '.npmrc'), { force: true }); @@ -218,6 +228,8 @@ const run = async () => { run().catch((e) => { logger.error(e); rm(join(root, '.npmrc'), { force: true }).then(() => { - process.exit(1); + cleanupVerdaccioProcesses().then(() => { + process.exit(1); + }); }); }); diff --git a/scripts/utils/kill-port.ts b/scripts/utils/kill-port.ts new file mode 100644 index 000000000000..4c25be565431 --- /dev/null +++ b/scripts/utils/kill-port.ts @@ -0,0 +1,67 @@ +// eslint-disable-next-line depend/ban-dependencies +import { execa } from 'execa'; + +// 99% inspired by https://github.com/tiaanduplessis/kill-port/blob/master/index.js +const killProcessInPort = async (port: number, method: 'tcp' | 'udp' = 'tcp') => { + if (!port || isNaN(port)) { + throw new Error('Invalid argument provided for port'); + } + let args: string[] = []; + let command: string; + + if (process.platform === 'win32') { + try { + const { stdout } = await execa('netstat', ['-nao']); + + if (!stdout) { + return; + } + + const lines = stdout.split('\n'); + const lineWithLocalPortRegEx = new RegExp(`^ *${method.toUpperCase()} *[^ ]*:${port}`, 'gm'); + const linesWithLocalPort = lines.filter((line) => line.match(lineWithLocalPortRegEx)); + + const pids = linesWithLocalPort.reduce((acc, line) => { + const match = line.match(/\d+/gm); + if (match && match[0] && !acc.includes(match[0])) { + acc.push(match[0]); + } + return acc; + }, []); + + if (pids.length > 0) { + args = ['/F', ...pids.flatMap((pid) => ['/PID', pid])]; + command = 'TaskKill'; + } + } catch (error) { + throw new Error(`Failed to detect process on port ${port}: ${(error as Error).message}`); + } + } else { + const protocol = method === 'udp' ? 'udp' : 'tcp'; + args = [ + '-c', + `lsof -i ${protocol}:${port} | grep ${method === 'udp' ? 'UDP' : 'LISTEN'} | awk '{print $2}' | xargs kill -9`, + ]; + command = 'sh'; + } + + try { + if (command) { + await execa(command, args); + } else { + throw new Error('No command to kill process found'); + } + } catch (error: any) { + if (!error.message.includes('No such process')) { + console.error(`Failed to kill process on port ${port}`); + throw error; + } + } +}; + +export const killPort = async (ports: number | number[], method: 'tcp' | 'udp' = 'tcp') => { + const allPorts = Array.isArray(ports) ? ports : [ports]; + + console.log(`🚮 cleaning up process in ports: ${allPorts.join(', ')}`); + await Promise.all(allPorts.map((port) => killProcessInPort(port, method))); +};