From 0b3dd27188cf2272254e50439da375bb6e79f905 Mon Sep 17 00:00:00 2001 From: Abram Sanderson Date: Wed, 18 Dec 2024 12:47:04 -0800 Subject: [PATCH 1/4] feat(test-tools): Support lerna in assign-test-ports --- tools/test-tools/bin/assign-test-ports | 13 ++ tools/test-tools/package.json | 3 + tools/test-tools/pnpm-lock.yaml | 114 ++++++------------ tools/test-tools/src/assignTestPorts.ts | 15 ++- .../src/test/assignTestPorts.spec.ts | 2 +- 5 files changed, 66 insertions(+), 81 deletions(-) diff --git a/tools/test-tools/bin/assign-test-ports b/tools/test-tools/bin/assign-test-ports index 267d26ed270a..b614b182062e 100644 --- a/tools/test-tools/bin/assign-test-ports +++ b/tools/test-tools/bin/assign-test-ports @@ -1,5 +1,10 @@ #!/usr/bin/env node const mod = require("../dist/assignTestPorts.js"); +const nconf = require("nconf"); + +nconf.argv().env().defaults({ + 'package-manager': 'pnpm' +}); const firstArg = process.argv[2]; @@ -14,6 +19,14 @@ if (firstArg === "--help" || firstArg === "-h" || (firstArg !== undefined && !Nu process.exit(0); } +// Note: we support using lerna to resolve package info as the LTS branch still has some lerna usage at the time of writing. +// Once we no longer need to support any FF branches using lerna, this logic can be removed. +const packageManager = nconf.get('package-manager'); +if (!['lerna', 'pnpm'].includes(packageManager)) { + console.error(`Unsupported package manager: ${packageManager}. Please select one of 'lerna' or 'pnpm'.`); + process.exit(1); +} + // We used to hardcode port 8081 as the initial one // but as of 2024-11-25 port 8084 is used by something in the build agent image. // If a package that has jest tests ends up assigned that port, diff --git a/tools/test-tools/package.json b/tools/test-tools/package.json index 828d26df6c56..61fc71756fa1 100644 --- a/tools/test-tools/package.json +++ b/tools/test-tools/package.json @@ -30,6 +30,9 @@ "test": "mocha", "tsc": "tsc" }, + "dependencies": { + "nconf": "^0.12.1" + }, "devDependencies": { "@fluidframework/build-common": "^2.0.3", "@fluidframework/build-tools": "^0.51.0", diff --git a/tools/test-tools/pnpm-lock.yaml b/tools/test-tools/pnpm-lock.yaml index b068a5e874da..ba8f0e78ec90 100644 --- a/tools/test-tools/pnpm-lock.yaml +++ b/tools/test-tools/pnpm-lock.yaml @@ -7,6 +7,10 @@ settings: importers: .: + dependencies: + nconf: + specifier: ^0.12.1 + version: 0.12.1 devDependencies: '@fluidframework/build-common': specifier: ^2.0.3 @@ -815,7 +819,7 @@ packages: dependencies: '@typescript-eslint/typescript-estree': 6.7.5(typescript@5.4.5) '@typescript-eslint/utils': 6.7.5(eslint@8.55.0)(typescript@5.4.5) - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.7(supports-color@8.1.1) eslint: 8.55.0 ts-api-utils: 1.2.1(typescript@5.4.5) typescript: 5.4.5 @@ -849,10 +853,10 @@ packages: dependencies: '@typescript-eslint/types': 5.59.11 '@typescript-eslint/visitor-keys': 5.59.11 - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.7(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 - semver: 7.6.0 + semver: 7.6.3 tsutils: 3.21.0(typescript@5.4.5) typescript: 5.4.5 transitivePeerDependencies: @@ -870,11 +874,11 @@ packages: dependencies: '@typescript-eslint/types': 6.21.0 '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.7(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 - semver: 7.6.0 + semver: 7.6.3 ts-api-utils: 1.2.1(typescript@5.4.5) typescript: 5.4.5 transitivePeerDependencies: @@ -892,10 +896,10 @@ packages: dependencies: '@typescript-eslint/types': 6.7.5 '@typescript-eslint/visitor-keys': 6.7.5 - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.7(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 - semver: 7.6.0 + semver: 7.6.3 ts-api-utils: 1.2.1(typescript@5.4.5) typescript: 5.4.5 transitivePeerDependencies: @@ -916,7 +920,7 @@ packages: '@typescript-eslint/typescript-estree': 5.59.11(typescript@5.4.5) eslint: 8.55.0 eslint-scope: 5.1.1 - semver: 7.6.0 + semver: 7.6.3 transitivePeerDependencies: - supports-color - typescript @@ -935,7 +939,7 @@ packages: '@typescript-eslint/types': 6.7.5 '@typescript-eslint/typescript-estree': 6.7.5(typescript@5.4.5) eslint: 8.55.0 - semver: 7.6.0 + semver: 7.6.3 transitivePeerDependencies: - supports-color - typescript @@ -1023,7 +1027,6 @@ packages: /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - dev: true /ansi-regex@6.0.1: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} @@ -1042,7 +1045,6 @@ packages: engines: {node: '>=8'} dependencies: color-convert: 2.0.1 - dev: true /ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} @@ -1146,7 +1148,6 @@ packages: /async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - dev: true /asynciterator.prototype@1.0.0: resolution: {integrity: sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==} @@ -1325,7 +1326,6 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - dev: true /code-block-writer@13.0.1: resolution: {integrity: sha512-c5or4P6erEA69TxaxTNcHUNcIn+oyxSRTOWV+pSYF+z4epXqNvwvJ70XPGjPNgue83oAFAPBRQYwpAJ/Hpe/Sg==} @@ -1349,7 +1349,6 @@ packages: engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 - dev: true /color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} @@ -1357,7 +1356,6 @@ packages: /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - dev: true /comment-parser@1.4.0: resolution: {integrity: sha512-QLyTNiZ2KDOibvFPlZ6ZngVsZ/0gYnE6uTXi5aoDg8ed3AkJAz4sEje3Y8a29hQ1s6A99MZXe47fLAXQ1rTqaw==} @@ -1539,7 +1537,6 @@ packages: /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: true /emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} @@ -1675,7 +1672,6 @@ packages: /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} - dev: true /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} @@ -1732,7 +1728,7 @@ packages: debug: 4.3.6(supports-color@8.1.1) enhanced-resolve: 5.15.0 eslint: 8.55.0 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-typescript@3.6.3)(eslint@8.55.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.55.0) eslint-plugin-import: /eslint-plugin-i@2.29.1(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-typescript@3.6.3)(eslint@8.55.0) fast-glob: 3.3.2 get-tsconfig: 4.8.1 @@ -1745,38 +1741,9 @@ packages: - supports-color dev: true - /eslint-module-utils@2.12.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-typescript@3.6.3)(eslint@8.55.0): + /eslint-module-utils@2.12.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.55.0): resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==} engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true - dependencies: - '@typescript-eslint/parser': 6.7.5(eslint@8.55.0)(typescript@5.4.5) - debug: 3.2.7 - eslint: 8.55.0 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@6.7.5)(eslint-plugin-i@2.29.1)(eslint@8.55.0) - transitivePeerDependencies: - - supports-color - dev: true - - /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.55.0): - resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} - engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' eslint: '*' @@ -1821,15 +1788,15 @@ packages: peerDependencies: eslint: ^7.2.0 || ^8 dependencies: - debug: 4.3.6(supports-color@8.1.1) + debug: 4.3.7(supports-color@8.1.1) doctrine: 3.0.0 eslint: 8.55.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.55.0) - get-tsconfig: 4.7.2 + eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.55.0) + get-tsconfig: 4.8.1 is-glob: 4.0.3 minimatch: 3.1.2 - semver: 7.6.0 + semver: 7.6.3 transitivePeerDependencies: - '@typescript-eslint/parser' - eslint-import-resolver-typescript @@ -2224,7 +2191,6 @@ packages: /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - dev: true /get-east-asian-width@1.3.0: resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} @@ -2256,12 +2222,6 @@ packages: get-intrinsic: 1.2.4 dev: true - /get-tsconfig@4.7.2: - resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} - dependencies: - resolve-pkg-maps: 1.0.0 - dev: true - /get-tsconfig@4.8.1: resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} dependencies: @@ -2293,7 +2253,7 @@ packages: dependencies: foreground-child: 3.1.1 jackspeak: 2.3.6 - minimatch: 9.0.4 + minimatch: 9.0.5 minipass: 7.0.4 path-scurry: 1.10.2 dev: true @@ -2491,6 +2451,11 @@ packages: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} dev: true + /ini@2.0.0: + resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} + engines: {node: '>=10'} + dev: false + /ink@5.1.0(@types/react@18.3.12)(react@18.3.1): resolution: {integrity: sha512-3vIO+CU4uSg167/dZrg4wHy75llUINYXxN4OsdaCkE40q4zyOTPwNc2VEpLnnWsIvIQeo6x6lilAhuaSt+rIsA==} engines: {node: '>=18'} @@ -2635,7 +2600,6 @@ packages: /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - dev: true /is-fullwidth-code-point@4.0.0: resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} @@ -3006,13 +2970,6 @@ packages: brace-expansion: 2.0.1 dev: true - /minimatch@9.0.4: - resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} - engines: {node: '>=16 || 14 >=14.17'} - dependencies: - brace-expansion: 2.0.1 - dev: true - /minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -3109,6 +3066,16 @@ packages: engines: {node: '>=18'} dev: true + /nconf@0.12.1: + resolution: {integrity: sha512-p2cfF+B3XXacQdswUYWZ0w6Vld0832A/tuqjLBu3H1sfUcby4N2oVbGhyuCkZv+t3iY3aiFEj7gZGqax9Q2c1w==} + engines: {node: '>= 0.4.0'} + dependencies: + async: 3.2.6 + ini: 2.0.0 + secure-keys: 1.0.0 + yargs: 16.2.0 + dev: false + /normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: @@ -3474,7 +3441,6 @@ packages: /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - dev: true /require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} @@ -3591,6 +3557,10 @@ packages: loose-envify: 1.4.0 dev: true + /secure-keys@1.0.0: + resolution: {integrity: sha512-nZi59hW3Sl5P3+wOO89eHBAAGwmCPd2aE1+dLZV5MO+ItQctIvAqihzaAXIQhvtH4KJPxM080HsnqltR2y8cWg==} + dev: false + /semver@5.7.1: resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} hasBin: true @@ -3754,7 +3724,6 @@ packages: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - dev: true /string-width@5.1.2: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} @@ -3817,7 +3786,6 @@ packages: engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 - dev: true /strip-ansi@7.1.0: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} @@ -4146,7 +4114,6 @@ packages: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true /wrap-ansi@8.1.0: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} @@ -4186,7 +4153,6 @@ packages: /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - dev: true /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} @@ -4201,7 +4167,6 @@ packages: /yargs-parser@20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} - dev: true /yargs-unparser@2.0.0: resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} @@ -4224,7 +4189,6 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 20.2.9 - dev: true /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} diff --git a/tools/test-tools/src/assignTestPorts.ts b/tools/test-tools/src/assignTestPorts.ts index 56f0576dce11..47a4de54d1d8 100644 --- a/tools/test-tools/src/assignTestPorts.ts +++ b/tools/test-tools/src/assignTestPorts.ts @@ -12,21 +12,26 @@ export interface PackageInfo { name: string; version: string; private: string; - path: string; + // If useful, both lerna and pnpm report the path to the package. + // Pnpm uses the key "path", while lerna uses "location" } /** * Gets and parses a PackageInfo for packages in the workspace. */ -export function getPackageInfo(): PackageInfo[] { +export function getPackageInfo(packageManager: 'pnpm' | 'lerna'): PackageInfo[] { try { - const child = spawnSync("pnpm", ["recursive", "list", "--json", "--depth=-1"], { + const command = packageManager; + const args = packageManager === 'pnpm' ? ["recursive", "list", "--json", "--depth=-1"] : ["list", "--json", "--all"]; + const child = spawnSync(command, args, { encoding: "utf8", // shell:true is required for Windows without a resolved path to pnpm. shell: true, }); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const info: PackageInfo[] = JSON.parse(child.stdout); + if (!Array.isArray(info)) { // eslint-disable-next-line unicorn/prefer-type-error throw new Error( @@ -40,8 +45,8 @@ export function getPackageInfo(): PackageInfo[] { } } -export function writePortMapFile(initialPort: number): void { - const info: PackageInfo[] = getPackageInfo(); +export function writePortMapFile(initialPort: number, packageManager: 'pnpm' | 'lerna'): void { + const info: PackageInfo[] = getPackageInfo(packageManager); // Assign a unique port to each package const portMap: { [pkgName: string]: number } = {}; diff --git a/tools/test-tools/src/test/assignTestPorts.spec.ts b/tools/test-tools/src/test/assignTestPorts.spec.ts index 289070caca47..ead8892aee44 100644 --- a/tools/test-tools/src/test/assignTestPorts.spec.ts +++ b/tools/test-tools/src/test/assignTestPorts.spec.ts @@ -9,7 +9,7 @@ import { getPackageInfo } from "../assignTestPorts"; describe("assignTestPorts", () => { it("getPackageInfo", () => { - const info = getPackageInfo(); + const info = getPackageInfo('pnpm'); assert.equal(info.length, 1); assert.equal(info[0].name, "@fluidframework/test-tools"); }); From 6e3befa615862e8947b2c93d7587b12e9ee64f7c Mon Sep 17 00:00:00 2001 From: Abram Sanderson Date: Thu, 19 Dec 2024 07:43:46 -0800 Subject: [PATCH 2/4] Pass argument in script --- tools/test-tools/bin/assign-test-ports | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/test-tools/bin/assign-test-ports b/tools/test-tools/bin/assign-test-ports index b614b182062e..6fed6060fefe 100644 --- a/tools/test-tools/bin/assign-test-ports +++ b/tools/test-tools/bin/assign-test-ports @@ -35,4 +35,4 @@ if (!['lerna', 'pnpm'].includes(packageManager)) { const initialPortString = firstArg ?? "9000"; const initialPort = Number.parseInt(initialPortString, 10); -mod.writePortMapFile(initialPort); +mod.writePortMapFile(initialPort, packageManager); From 71462ea1aea6e5c6ed9dee68d9d3d37f90577c38 Mon Sep 17 00:00:00 2001 From: Abram Sanderson Date: Thu, 19 Dec 2024 09:26:16 -0800 Subject: [PATCH 3/4] Clean up parsing to more heavily leverage nconf/yargs --- tools/test-tools/bin/assign-test-ports | 49 ++++++++++++-------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/tools/test-tools/bin/assign-test-ports b/tools/test-tools/bin/assign-test-ports index 6fed6060fefe..49f841a713e9 100644 --- a/tools/test-tools/bin/assign-test-ports +++ b/tools/test-tools/bin/assign-test-ports @@ -2,37 +2,34 @@ const mod = require("../dist/assignTestPorts.js"); const nconf = require("nconf"); -nconf.argv().env().defaults({ - 'package-manager': 'pnpm' -}); - -const firstArg = process.argv[2]; - -if (firstArg === "--help" || firstArg === "-h" || (firstArg !== undefined && !Number.isInteger(firstArg)) ) { - console.log( - "Usage: assign-test-ports [initialPort]\n\n" + - "Assigns a port to each package in the current pnpm workspace so they can run jest/puppeteer\n" + - "tests concurrently without port conflicts, and writes the port mapping to a file.\n\n" + - "The initial port is optional and defaults to 9000.\n\n" + - "The port mapping file is written to the OS' temporary storage folder (e.g. /tmp/testportmap.json)." - ); - process.exit(0); -} - -// Note: we support using lerna to resolve package info as the LTS branch still has some lerna usage at the time of writing. -// Once we no longer need to support any FF branches using lerna, this logic can be removed. -const packageManager = nconf.get('package-manager'); -if (!['lerna', 'pnpm'].includes(packageManager)) { - console.error(`Unsupported package manager: ${packageManager}. Please select one of 'lerna' or 'pnpm'.`); - process.exit(1); -} +nconf + .argv( + { + "package-manager": { + type: "string", + describe: "Package manager used for the monorepo.", + default: "pnpm", + choices: ["pnpm", "lerna"], + }, + }, + "Usage: assign-test-ports [initialPort] [--package-manager ]\n\n" + + "Assigns a port to each package in the current monorepo so they can run jest/puppeteer\n" + + "tests concurrently without port conflicts, and writes the port mapping to a file.\n\n" + + "The initial port is optional and defaults to 9000.\n\n" + + "The port mapping file is written to the OS' temporary storage folder (e.g. /tmp/testportmap.json).", + ) + .env(); // We used to hardcode port 8081 as the initial one // but as of 2024-11-25 port 8084 is used by something in the build agent image. // If a package that has jest tests ends up assigned that port, // jest will fail to start. // So try to use a port range where nothing will be listening. -const initialPortString = firstArg ?? "9000"; +// Note '_' is used for positional arguments in nconf. +const initialPort = nconf.get("_")[0] ?? 9000; + +// Note: we support using lerna to resolve package info as the LTS branch still has some lerna usage at the time of writing. +// Once we no longer need to support any FF branches using lerna, this logic can be removed. +const packageManager = nconf.get("package-manager"); -const initialPort = Number.parseInt(initialPortString, 10); mod.writePortMapFile(initialPort, packageManager); From a91d8739caab152e166a6ffa3af1ad554fcd0185 Mon Sep 17 00:00:00 2001 From: Abram Sanderson Date: Thu, 2 Jan 2025 14:29:54 -0800 Subject: [PATCH 4/4] PR feedback --- tools/test-tools/bin/assign-test-ports | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/test-tools/bin/assign-test-ports b/tools/test-tools/bin/assign-test-ports index 49f841a713e9..12498826f561 100644 --- a/tools/test-tools/bin/assign-test-ports +++ b/tools/test-tools/bin/assign-test-ports @@ -20,13 +20,22 @@ nconf ) .env(); +// Slightly stricter variant of Number.parseInt +function parsePort(value) { + const asInt = Number.parseInt(value); + if (Number.isNaN(asInt) || `${asInt}` !== value) { + throw new Error(`Invalid port number: ${value}`); + } + return asInt; +} + // We used to hardcode port 8081 as the initial one // but as of 2024-11-25 port 8084 is used by something in the build agent image. // If a package that has jest tests ends up assigned that port, // jest will fail to start. // So try to use a port range where nothing will be listening. // Note '_' is used for positional arguments in nconf. -const initialPort = nconf.get("_")[0] ?? 9000; +const initialPort = parsePort(nconf.get("_")[0] ?? "9000"); // Note: we support using lerna to resolve package info as the LTS branch still has some lerna usage at the time of writing. // Once we no longer need to support any FF branches using lerna, this logic can be removed.