From 6c6faabe43b46ef5fbb0253316a401a466e500c3 Mon Sep 17 00:00:00 2001 From: le-cong Date: Wed, 5 Jun 2024 13:24:06 -0400 Subject: [PATCH] verify npm package (#77) --- .github/workflows/ci.yml | 8 +- .github/workflows/coverage.yml | 2 + package-lock.json | 725 ++++++++++-------- package.json | 19 +- src/check-imports/check-imports.ts | 38 + src/check-imports/index.ts | 48 +- .../package-lock-file-util.spec.ts | 22 +- src/check-label/check-label.spec.ts | 151 ++-- src/check-label/check-label.ts | 8 +- src/check-label/index.ts | 16 +- src/check-pr-reviews/check-pr-reviews.ts | 50 ++ src/check-pr-reviews/index.ts | 60 +- src/check-published/check-published.ts | 65 ++ src/check-published/index.ts | 74 +- src/check-published/slack.ts | 2 +- .../comment-npm-publish.ts | 22 + src/comment-npm-publish/index.ts | 32 +- src/coverage-reporter/coverage-reporter.ts | 109 +++ src/coverage-reporter/index.ts | 103 +-- src/github-api/index-context.spec.ts | 25 +- src/github-api/index-publish-comment.spec.ts | 34 +- src/github-api/index-reviews.spec.ts | 37 +- src/github-api/index.ts | 2 +- src/nocks/github.test.ts | 72 +- src/perform-bundle/deployer.ts | 2 +- src/perform-bundle/index.ts | 51 +- src/perform-bundle/perform-bundle.ts | 42 + src/prepare-beta/index.ts | 26 +- src/prepare-beta/package.spec.ts | 37 +- src/prepare-beta/package.ts | 2 +- src/prepare-beta/prepare-beta.ts | 17 + src/publish-beta/compile.ts | 2 +- src/publish-beta/files.spec.ts | 61 +- src/publish-beta/index.ts | 37 +- src/publish-beta/package.spec.ts | 46 +- src/publish-beta/package.ts | 2 +- src/publish-beta/publish-beta.ts | 28 + src/publish-beta/publish.spec.ts | 28 +- src/publish-beta/publish.ts | 4 +- src/validate-npm-package/index.ts | 5 + .../validate-npm-package.spec.ts | 37 + .../validate-npm-package.ts | 107 +++ validate-npm-package/action.yml | 10 + 43 files changed, 1162 insertions(+), 1106 deletions(-) create mode 100644 src/check-imports/check-imports.ts create mode 100644 src/check-pr-reviews/check-pr-reviews.ts create mode 100644 src/check-published/check-published.ts create mode 100644 src/comment-npm-publish/comment-npm-publish.ts create mode 100644 src/coverage-reporter/coverage-reporter.ts create mode 100644 src/perform-bundle/perform-bundle.ts create mode 100644 src/prepare-beta/prepare-beta.ts create mode 100644 src/publish-beta/publish-beta.ts create mode 100644 src/validate-npm-package/index.ts create mode 100644 src/validate-npm-package/validate-npm-package.spec.ts create mode 100644 src/validate-npm-package/validate-npm-package.ts create mode 100644 validate-npm-package/action.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79262f2..603b465 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: ['20.x', '21.x'] + node-version: ['20.x', '22.x'] steps: - name: Checkout Code uses: actions/checkout@v4 @@ -30,13 +30,15 @@ jobs: run: npm run ci:lint - name: Run Tests run: npm run ci:test + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN_PUBLISH }} branchBuild: name: Branch Build runs-on: ubuntu-latest strategy: matrix: - node-version: ['20.x', '21.x'] + node-version: ['20.x', '22.x'] steps: - name: Checkout Code uses: actions/checkout@v4 @@ -57,3 +59,5 @@ jobs: run: npm run ci:lint - name: Run Tests run: npm run ci:test + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN_PUBLISH }} diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 5fc461d..5085f3f 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -23,6 +23,8 @@ jobs: run: npm ci --ignore-scripts - name: Calculate Code Coverage run: npm run ci:coverage + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN_PUBLISH }} - name: Create Coverage Report for base branch run: | mv coverage/lcov.info coverage/lcov_head.info diff --git a/package-lock.json b/package-lock.json index 77b39e2..aa29bcb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,51 +1,37 @@ { "name": "@checkdigit/github-actions", - "version": "2.0.0", + "version": "2.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@checkdigit/github-actions", - "version": "2.0.0", + "version": "2.1.0", "license": "MIT", "dependencies": { "@actions/core": "^1.10.1", "@actions/github": "^6.0.0", - "@octokit/rest": "^20.1.0", - "debug": "^4.3.4", - "semver": "^7.6.0", - "undici": "^6.11.1" - }, - "bin": { - "action": "bin/action" + "@octokit/rest": "^20.1.1", + "debug": "^4.3.5", + "semver": "^7.6.2", + "undici": "^6.18.2", + "uuid": "^9.0.1" }, "devDependencies": { "@checkdigit/eslint-config": "^9.2.0", - "@checkdigit/jest-config": "^6.0.0", - "@checkdigit/prettier-config": "^5.3.0", + "@checkdigit/jest-config": "^6.0.2", + "@checkdigit/prettier-config": "^5.4.0", "@checkdigit/typescript-config": "^7.0.1", "@types/debug": "^4.1.12", "@types/semver": "^7.5.8", "@types/uuid": "^9.0.8", - "esbuild": "^0.20.2", "nock": "^14.0.0-beta.5", - "rimraf": "^5.0.5", - "uuid": "^9.0.1" + "rimraf": "^5.0.7" }, "engines": { "node": ">=20.11" } }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@actions/core": { "version": "1.10.1", "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.1.tgz", @@ -109,13 +95,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", - "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.6.tgz", + "integrity": "sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==", "dev": true, "peer": true, "dependencies": { - "@babel/highlight": "^7.24.2", + "@babel/highlight": "^7.24.6", "picocolors": "^1.0.0" }, "engines": { @@ -123,9 +109,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", - "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.6.tgz", + "integrity": "sha512-aC2DGhBq5eEdyXWqrDInSqQjO0k8xtPRf5YylULqx8MCd6jBtzqfta/3ETMRpuKIc5hyswfO80ObyA1MvkCcUQ==", "dev": true, "peer": true, "engines": { @@ -133,22 +119,22 @@ } }, "node_modules/@babel/core": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz", - "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.6.tgz", + "integrity": "sha512-qAHSfAdVyFmIvl0VHELib8xar7ONuSHrE2hLnsaWkYNTI68dmi1x8GYDhJjMI/e7XWal9QBlZkwbOnkcw7Z8gQ==", "dev": true, "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.4", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.24.4", - "@babel/parser": "^7.24.4", - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.1", - "@babel/types": "^7.24.0", + "@babel/code-frame": "^7.24.6", + "@babel/generator": "^7.24.6", + "@babel/helper-compilation-targets": "^7.24.6", + "@babel/helper-module-transforms": "^7.24.6", + "@babel/helpers": "^7.24.6", + "@babel/parser": "^7.24.6", + "@babel/template": "^7.24.6", + "@babel/traverse": "^7.24.6", + "@babel/types": "^7.24.6", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -174,13 +160,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz", - "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.6.tgz", + "integrity": "sha512-S7m4eNa6YAPJRHmKsLHIDJhNAGNKoWNiWefz1MBbpnt8g9lvMDl1hir4P9bo/57bQEmuwEhnRU/AMWsD0G/Fbg==", "dev": true, "peer": true, "dependencies": { - "@babel/types": "^7.24.0", + "@babel/types": "^7.24.6", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -203,14 +189,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.6.tgz", + "integrity": "sha512-VZQ57UsDGlX/5fFA7GkVPplZhHsVc+vuErWgdOiysI9Ksnw0Pbbd6pnPiR/mmJyKHgyIW0c7KT32gmhiF+cirg==", "dev": true, "peer": true, "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", + "@babel/compat-data": "^7.24.6", + "@babel/helper-validator-option": "^7.24.6", "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -230,9 +216,9 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.6.tgz", + "integrity": "sha512-Y50Cg3k0LKLMjxdPjIl40SdJgMB85iXn27Vk/qbHZCFx/o5XO3PSnpi675h1KEmmDb6OFArfd5SCQEQ5Q4H88g==", "dev": true, "peer": true, "engines": { @@ -240,57 +226,57 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.6.tgz", + "integrity": "sha512-xpeLqeeRkbxhnYimfr2PC+iA0Q7ljX/d1eZ9/inYbmfG2jpl8Lu3DyXvpOAnrS5kxkfOWJjioIMQsaMBXFI05w==", "dev": true, "peer": true, "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" + "@babel/template": "^7.24.6", + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.6.tgz", + "integrity": "sha512-SF/EMrC3OD7dSta1bLJIlrsVxwtd0UpjRJqLno6125epQMJ/kyFmpTT4pbvPbdQHzCHg+biQ7Syo8lnDtbR+uA==", "dev": true, "peer": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", - "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.6.tgz", + "integrity": "sha512-a26dmxFJBF62rRO9mmpgrfTLsAuyHk4e1hKTUkD/fcMfynt8gvEKwQPQDVxWhca8dHoDck+55DFt42zV0QMw5g==", "dev": true, "peer": true, "dependencies": { - "@babel/types": "^7.24.0" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.6.tgz", + "integrity": "sha512-Y/YMPm83mV2HJTbX1Qh2sjgjqcacvOlhbzdCCsSlblOKjSYmQqEbO6rUniWQyRo9ncyfjT8hnUjlG06RXDEmcA==", "dev": true, "peer": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-module-imports": "^7.24.6", + "@babel/helper-simple-access": "^7.24.6", + "@babel/helper-split-export-declaration": "^7.24.6", + "@babel/helper-validator-identifier": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -300,9 +286,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", - "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.6.tgz", + "integrity": "sha512-MZG/JcWfxybKwsA9N9PmtF2lOSFSEMVCpIRrbxccZFLJPrJciJdG/UhSh5W96GEteJI2ARqm5UAHxISwRDLSNg==", "dev": true, "peer": true, "engines": { @@ -310,35 +296,35 @@ } }, "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.6.tgz", + "integrity": "sha512-nZzcMMD4ZhmB35MOOzQuiGO5RzL6tJbsT37Zx8M5L/i9KSrukGXWTjLe1knIbb/RmxoJE9GON9soq0c0VEMM5g==", "dev": true, "peer": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.6.tgz", + "integrity": "sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw==", "dev": true, "peer": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", - "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.6.tgz", + "integrity": "sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q==", "dev": true, "peer": true, "engines": { @@ -346,9 +332,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz", + "integrity": "sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==", "dev": true, "peer": true, "engines": { @@ -356,9 +342,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.6.tgz", + "integrity": "sha512-Jktc8KkF3zIkePb48QO+IapbXlSapOW9S+ogZZkcO6bABgYAxtZcjZ/O005111YLf+j4M84uEgwYoidDkXbCkQ==", "dev": true, "peer": true, "engines": { @@ -366,28 +352,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz", - "integrity": "sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.6.tgz", + "integrity": "sha512-V2PI+NqnyFu1i0GyTd/O/cTpxzQCYioSkUIRmgo7gFEHKKCg5w46+r/A6WeUR1+P3TeQ49dspGPNd/E3n9AnnA==", "dev": true, "peer": true, "dependencies": { - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.1", - "@babel/types": "^7.24.0" + "@babel/template": "^7.24.6", + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", - "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.6.tgz", + "integrity": "sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==", "dev": true, "peer": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.24.6", "chalk": "^2.4.2", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" @@ -475,9 +460,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", - "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.6.tgz", + "integrity": "sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==", "dev": true, "peer": true, "bin": { @@ -553,13 +538,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz", - "integrity": "sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.6.tgz", + "integrity": "sha512-lWfvAIFNWMlCsU0DRUun2GpFwZdGTukLaHJqRh1JRb80NdAP5Sb1HDHB5X9P9OtgZHQl089UzQkpYlBq2VTPRw==", "dev": true, "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -663,13 +648,13 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz", - "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.6.tgz", + "integrity": "sha512-TzCtxGgVTEJWWwcYwQhCIQ6WaKlo80/B+Onsk4RRCcYqpYGFcG9etPW94VToGte5AAcxRrhjPUFvUS3Y2qKi4A==", "dev": true, "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -679,35 +664,35 @@ } }, "node_modules/@babel/template": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", - "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.6.tgz", + "integrity": "sha512-3vgazJlLwNXi9jhrR1ef8qiB65L1RK90+lEQwv4OxveHnqC3BfmnHdgySwRLzf6akhlOYenT+b7AfWq+a//AHw==", "dev": true, "peer": true, "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0" + "@babel/code-frame": "^7.24.6", + "@babel/parser": "^7.24.6", + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", - "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.6.tgz", + "integrity": "sha512-OsNjaJwT9Zn8ozxcfoBc+RaHdj3gFmCmYoQLUII1o6ZrUwku0BMg80FoOTPx+Gi6XhcQxAYE4xyjPTo4SxEQqw==", "dev": true, "peer": true, "dependencies": { - "@babel/code-frame": "^7.24.1", - "@babel/generator": "^7.24.1", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.24.1", - "@babel/types": "^7.24.0", + "@babel/code-frame": "^7.24.6", + "@babel/generator": "^7.24.6", + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-function-name": "^7.24.6", + "@babel/helper-hoist-variables": "^7.24.6", + "@babel/helper-split-export-declaration": "^7.24.6", + "@babel/parser": "^7.24.6", + "@babel/types": "^7.24.6", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -726,14 +711,14 @@ } }, "node_modules/@babel/types": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", - "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.6.tgz", + "integrity": "sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ==", "dev": true, "peer": true, "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-string-parser": "^7.24.6", + "@babel/helper-validator-identifier": "^7.24.6", "to-fast-properties": "^2.0.0" }, "engines": { @@ -786,30 +771,29 @@ } }, "node_modules/@checkdigit/jest-config": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@checkdigit/jest-config/-/jest-config-6.0.0.tgz", - "integrity": "sha512-VZd3mzzZatAvRoz5fKuYJCS7L8BatJllyiOYxpHsOAAe2WwjBRx0W9KoJcq0e8DQDA8l8DsNn1XURy1kIBwKNQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@checkdigit/jest-config/-/jest-config-6.0.2.tgz", + "integrity": "sha512-2KaVBG0RFGv6NREQodAuCmmiVI2JGIJ0gvyN6+4UAuOwDM5nEGJpLVU59fz8cqYe0iPgk8WxcauqHYcqCxJ1Aw==", "dev": true, "engines": { - "node": ">=20.11" + "node": ">=20.12" }, "peerDependencies": { "@jest/globals": "29.7.0", - "dotenv": ">=16", "jest": "29.7.0", "ts-jest": "29.1.2" } }, "node_modules/@checkdigit/prettier-config": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@checkdigit/prettier-config/-/prettier-config-5.3.0.tgz", - "integrity": "sha512-S2Q/v8aLiH3ROxvgAawhhZkt3t0a2S7fmF+E+GCWOiYXkSXm02+8QvNZVIMyx7xgwK2MpRQv0bdJuyWjsLe7MQ==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@checkdigit/prettier-config/-/prettier-config-5.4.0.tgz", + "integrity": "sha512-uq6IcLc76rv+BwVXKpp8nXFS2DMRHRYwiQxWedAHumkc1fCmkXZhENVmQAYx4UbjYtfAnIejvKabZjvRyN1EkA==", "dev": true, "engines": { - "node": ">=20.11" + "node": ">=20.12" }, "peerDependencies": { - "prettier": "3.2.5" + "prettier": "3.3.0" } }, "node_modules/@checkdigit/typescript-config": { @@ -841,6 +825,7 @@ "os": [ "aix" ], + "peer": true, "engines": { "node": ">=12" } @@ -857,6 +842,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=12" } @@ -873,6 +859,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=12" } @@ -889,6 +876,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=12" } @@ -905,6 +893,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=12" } @@ -921,6 +910,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=12" } @@ -937,6 +927,7 @@ "os": [ "freebsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -953,6 +944,7 @@ "os": [ "freebsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -969,6 +961,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -985,6 +978,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -1001,6 +995,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -1017,6 +1012,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -1033,6 +1029,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -1049,6 +1046,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -1065,6 +1063,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -1081,6 +1080,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -1097,6 +1097,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -1113,6 +1114,7 @@ "os": [ "netbsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -1129,6 +1131,7 @@ "os": [ "openbsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -1145,6 +1148,7 @@ "os": [ "sunos" ], + "peer": true, "engines": { "node": ">=12" } @@ -1161,6 +1165,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=12" } @@ -1177,6 +1182,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=12" } @@ -1193,6 +1199,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=12" } @@ -1214,9 +1221,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz", + "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", "dev": true, "peer": true, "engines": { @@ -1970,15 +1977,15 @@ } }, "node_modules/@octokit/core": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.1.0.tgz", - "integrity": "sha512-BDa2VAMLSh3otEiaMJ/3Y36GU4qf6GI+VivQ/P41NC6GHcdxpKlqV0ikSZ5gdQsmS3ojXeRx5vasgNTinF0Q4g==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.0.tgz", + "integrity": "sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==", "dependencies": { "@octokit/auth-token": "^4.0.0", - "@octokit/graphql": "^7.0.0", - "@octokit/request": "^8.0.2", - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^12.0.0", + "@octokit/graphql": "^7.1.0", + "@octokit/request": "^8.3.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.0.0", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" }, @@ -1987,11 +1994,11 @@ } }, "node_modules/@octokit/endpoint": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.4.tgz", - "integrity": "sha512-DWPLtr1Kz3tv8L0UvXTDP1fNwM0S+z6EJpRcvH66orY6Eld4XBMCSYsaWp4xIm61jTWxK68BrR7ibO+vSDnZqw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.5.tgz", + "integrity": "sha512-ekqR4/+PCLkEBF6qgj8WqJfvDq65RH85OAgrtnVp1mSxaXF03u2xW/hUdweGS5654IlC0wkNYC18Z50tSYTAFw==", "dependencies": { - "@octokit/types": "^12.0.0", + "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" }, "engines": { @@ -1999,12 +2006,12 @@ } }, "node_modules/@octokit/graphql": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz", - "integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.0.tgz", + "integrity": "sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ==", "dependencies": { - "@octokit/request": "^8.0.1", - "@octokit/types": "^12.0.0", + "@octokit/request": "^8.3.0", + "@octokit/types": "^13.0.0", "universal-user-agent": "^6.0.0" }, "engines": { @@ -2012,9 +2019,9 @@ } }, "node_modules/@octokit/openapi-types": { - "version": "20.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", - "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==" + "version": "22.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", + "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==" }, "node_modules/@octokit/plugin-paginate-rest": { "version": "9.2.1", @@ -2030,6 +2037,19 @@ "@octokit/core": "5" } }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==" + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" + } + }, "node_modules/@octokit/plugin-request-log": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.1.tgz", @@ -2055,14 +2075,27 @@ "@octokit/core": "5" } }, + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==" + }, + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" + } + }, "node_modules/@octokit/request": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.2.0.tgz", - "integrity": "sha512-exPif6x5uwLqv1N1irkLG1zZNJkOtj8bZxuVHd71U5Ftuxf2wGNvAJyNBcPbPC+EBzwYEbBDdSFb8EPcjpYxPQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.0.tgz", + "integrity": "sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw==", "dependencies": { - "@octokit/endpoint": "^9.0.0", - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^12.0.0", + "@octokit/endpoint": "^9.0.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" }, "engines": { @@ -2070,11 +2103,11 @@ } }, "node_modules/@octokit/request-error": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz", - "integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.0.tgz", + "integrity": "sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q==", "dependencies": { - "@octokit/types": "^12.0.0", + "@octokit/types": "^13.1.0", "deprecation": "^2.0.0", "once": "^1.4.0" }, @@ -2083,25 +2116,53 @@ } }, "node_modules/@octokit/rest": { - "version": "20.1.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.1.0.tgz", - "integrity": "sha512-STVO3itHQLrp80lvcYB2UIKoeil5Ctsgd2s1AM+du3HqZIR35ZH7WE9HLwUOLXH0myA0y3AGNPo8gZtcgIbw0g==", + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.1.1.tgz", + "integrity": "sha512-MB4AYDsM5jhIHro/dq4ix1iWTLGToIGk6cWF5L6vanFaMble5jTX/UBQyiv05HsWnwUtY8JrfHy2LWfKwihqMw==", "dependencies": { "@octokit/core": "^5.0.2", - "@octokit/plugin-paginate-rest": "^9.1.5", + "@octokit/plugin-paginate-rest": "11.3.1", "@octokit/plugin-request-log": "^4.0.0", - "@octokit/plugin-rest-endpoint-methods": "^10.2.0" + "@octokit/plugin-rest-endpoint-methods": "13.2.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest/node_modules/@octokit/plugin-paginate-rest": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.3.1.tgz", + "integrity": "sha512-ryqobs26cLtM1kQxqeZui4v8FeznirUsksiA+RYemMPJ7Micju0WSkv50dBksTuZks9O5cg4wp+t8fZ/cLY56g==", + "dependencies": { + "@octokit/types": "^13.5.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/rest/node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.2.2.tgz", + "integrity": "sha512-EI7kXWidkt3Xlok5uN43suK99VWqc8OaIMktY9d9+RNKl69juoTyxmLoWPIZgJYzi41qj/9zU7G/ljnNOJ5AFA==", + "dependencies": { + "@octokit/types": "^13.5.0" }, "engines": { "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^5" } }, "node_modules/@octokit/types": { - "version": "12.6.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", - "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz", + "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==", "dependencies": { - "@octokit/openapi-types": "^20.0.0" + "@octokit/openapi-types": "^22.2.0" } }, "node_modules/@pkgjs/parseargs": { @@ -2177,9 +2238,9 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", - "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", "dev": true, "peer": true, "dependencies": { @@ -2253,9 +2314,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.12.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.4.tgz", - "integrity": "sha512-E+Fa9z3wSQpzgYQdYmme5X3OTuejnnTx88A6p6vkkJosR3KBz+HpE3kqNm98VE6cfLFcISx7zW7MsJkH6KwbTw==", + "version": "20.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.1.tgz", + "integrity": "sha512-T2MzSGEu+ysB/FkWfqmhV3PLyQlowdptmmgD20C6QxsS8Fmv5SjpZ1ayXaEC0S21/h5UJ9iA6W/5vSNU5l00OA==", "dev": true, "peer": true, "dependencies": { @@ -2914,13 +2975,13 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "peer": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -3003,9 +3064,9 @@ } }, "node_modules/builtins": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", - "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", + "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", "dev": true, "peer": true, "dependencies": { @@ -3053,9 +3114,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001605", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz", - "integrity": "sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==", + "version": "1.0.30001627", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001627.tgz", + "integrity": "sha512-4zgNiB8nTyV/tHhwZrFs88ryjls/lHiqFhrxCW4qSTeuRByBVnPYpDInchOIySWknznucaf31Z4KYqjfbrecVw==", "dev": true, "funding": [ { @@ -3117,9 +3178,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", + "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", "dev": true, "peer": true }, @@ -3212,9 +3273,9 @@ "peer": true }, "node_modules/core-js-compat": { - "version": "3.36.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.1.tgz", - "integrity": "sha512-Dk997v9ZCt3X/npqzyGdTlq6t7lDBhZwGvV94PKzDArjp7BTRm7WlDAXYd/OWdeFHO8OChQYRJNJvUCqCbrtKA==", + "version": "3.37.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz", + "integrity": "sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==", "dev": true, "peer": true, "dependencies": { @@ -3316,9 +3377,9 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dependencies": { "ms": "2.1.2" }, @@ -3332,9 +3393,9 @@ } }, "node_modules/dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", "dev": true, "peer": true, "peerDependencies": { @@ -3450,19 +3511,6 @@ "node": ">=6.0.0" } }, - "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -3470,9 +3518,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.726", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.726.tgz", - "integrity": "sha512-xtjfBXn53RORwkbyKvDfTajtnTp0OJoPOIBzXvkNbb7+YYvCHJflba3L7Txyx/6Fov3ov2bGPr/n5MTixmPhdQ==", + "version": "1.4.789", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.789.tgz", + "integrity": "sha512-0VbyiaXoT++Fi2vHGo2ThOeS6X3vgRCWrjPeO2FeIAWL6ItiSJ9BqlH8LfCXe3X1IdcG+S0iLoNaxQWhfZoGzQ==", "dev": true, "peer": true }, @@ -3496,9 +3544,9 @@ "dev": true }, "node_modules/enhanced-resolve": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", - "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz", + "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==", "dev": true, "peer": true, "dependencies": { @@ -3665,6 +3713,7 @@ "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", "dev": true, "hasInstallScript": true, + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -3777,9 +3826,9 @@ } }, "node_modules/eslint-compat-utils": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.0.tgz", - "integrity": "sha512-dc6Y8tzEcSYZMHa+CMPLi/hyo1FzNeonbhJL7Ol0ccuKQkwopJcJBA9YL/xmMTLU1eKigXo9vj9nALElWYSowg==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", + "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", "dev": true, "peer": true, "dependencies": { @@ -4568,9 +4617,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "peer": true, "dependencies": { @@ -4616,6 +4665,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "peer": true, "dependencies": { @@ -4816,9 +4866,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz", - "integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==", + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.5.tgz", + "integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==", "dev": true, "peer": true, "dependencies": { @@ -4832,6 +4882,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "peer": true, "dependencies": { @@ -4903,13 +4954,14 @@ } }, "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "peer": true, "dependencies": { - "define-properties": "^1.1.3" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -5149,6 +5201,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "peer": true, "dependencies": { @@ -5589,9 +5642,9 @@ } }, "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.2.3.tgz", + "integrity": "sha512-htOzIMPbpLid/Gq9/zaz9SfExABxqRe1sSCdxntlO/aMD6u0issZQiY25n2GKQUtJ02j7z5sfptlAOMpWWOmvw==", "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -6443,13 +6496,13 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", "dev": true, "peer": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -6481,6 +6534,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, + "peer": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -6502,9 +6556,9 @@ } }, "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "engines": { "node": ">=16 || 14 >=14.17" @@ -6523,9 +6577,9 @@ "peer": true }, "node_modules/nock": { - "version": "14.0.0-beta.5", - "resolved": "https://registry.npmjs.org/nock/-/nock-14.0.0-beta.5.tgz", - "integrity": "sha512-u255tf4DYvyErTlPZA9uTfXghiZZy+NflUOFONPVKZ5tP0yaHwKig28zyFOLhu8y5YcCRC+V5vDk4HHileh2iw==", + "version": "14.0.0-beta.7", + "resolved": "https://registry.npmjs.org/nock/-/nock-14.0.0-beta.7.tgz", + "integrity": "sha512-+EQMm5W9K8YnBE2Ceg4hnJynaCZmvK8ZlFXQ2fxGwtkOkBUq8GpQLTks2m1jpvse9XDxMDDOHgOWpiznFuh0bA==", "dev": true, "dependencies": { "json-stringify-safe": "^5.0.1", @@ -6711,18 +6765,18 @@ } }, "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "peer": true, "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -6839,25 +6893,25 @@ "peer": true }, "node_modules/path-scurry": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", - "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", "dev": true, "engines": { "node": "14 || >=16.14" @@ -6874,9 +6928,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", "dev": true, "peer": true }, @@ -7003,9 +7057,9 @@ } }, "node_modules/prettier": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", - "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.0.tgz", + "integrity": "sha512-J9odKxERhCQ10OC2yb93583f6UnYutOeiV5i0zEDS7UGTdUt0u+y8erxl3lBKvwo/JHyyoEdXjwp4dke9oyZ/g==", "dev": true, "peer": true, "bin": { @@ -7118,9 +7172,9 @@ "peer": true }, "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, "peer": true }, @@ -7379,9 +7433,9 @@ } }, "node_modules/rimraf": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", - "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.7.tgz", + "integrity": "sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==", "dev": true, "dependencies": { "glob": "^10.3.7" @@ -7390,27 +7444,42 @@ "rimraf": "dist/esm/bin.mjs" }, "engines": { - "node": ">=14" + "node": ">=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/rimraf/node_modules/glob": { - "version": "10.3.12", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", - "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", + "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", - "minimatch": "^9.0.1", - "minipass": "^7.0.4", - "path-scurry": "^1.10.2" + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { "node": ">=16 || 14 >=14.17" }, @@ -7480,12 +7549,9 @@ } }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "bin": { "semver": "bin/semver.js" }, @@ -7493,22 +7559,6 @@ "node": ">=10" } }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -7658,9 +7708,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", - "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", + "version": "3.0.18", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", + "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==", "dev": true, "peer": true }, @@ -8210,9 +8260,9 @@ } }, "node_modules/typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "peer": true, "bin": { @@ -8240,11 +8290,11 @@ } }, "node_modules/undici": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.11.1.tgz", - "integrity": "sha512-KyhzaLJnV1qa3BSHdj4AZ2ndqI0QWPxYzaIOio0WzcEJB9gvuysprJSLtpvc2D9mhR9jPDUk7xlJlZbH2KR5iw==", + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.18.2.tgz", + "integrity": "sha512-o/MQLTwRm9IVhOqhZ0NQ9oXax1ygPjw6Vs+Vq/4QRjbOAC3B1GCHy7TYxxbExKlb7bzDRzt9vBWU6BDz0RFfYg==", "engines": { - "node": ">=18.0" + "node": ">=18.17" } }, "node_modules/undici-types": { @@ -8260,9 +8310,9 @@ "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==" }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", + "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", "dev": true, "funding": [ { @@ -8280,8 +8330,8 @@ ], "peer": true, "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.1.2", + "picocolors": "^1.0.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -8304,7 +8354,6 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "dev": true, "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -8401,6 +8450,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index b1ccbd3..4d6e469 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@checkdigit/github-actions", - "version": "2.0.0", + "version": "2.1.0", "description": " Provides supporting operations for github action builds.", "author": "Check Digit, LLC", "license": "MIT", @@ -20,23 +20,22 @@ "dependencies": { "@actions/core": "^1.10.1", "@actions/github": "^6.0.0", - "@octokit/rest": "^20.1.0", - "debug": "^4.3.4", - "semver": "^7.6.0", - "undici": "^6.11.1" + "@octokit/rest": "^20.1.1", + "debug": "^4.3.5", + "semver": "^7.6.2", + "undici": "^6.18.2", + "uuid": "^9.0.1" }, "devDependencies": { "@checkdigit/eslint-config": "^9.2.0", - "@checkdigit/jest-config": "^6.0.0", - "@checkdigit/prettier-config": "^5.3.0", + "@checkdigit/jest-config": "^6.0.2", + "@checkdigit/prettier-config": "^5.4.0", "@checkdigit/typescript-config": "^7.0.1", "@types/debug": "^4.1.12", "@types/semver": "^7.5.8", "@types/uuid": "^9.0.8", - "esbuild": "^0.20.2", "nock": "^14.0.0-beta.5", - "rimraf": "^5.0.5", - "uuid": "^9.0.1" + "rimraf": "^5.0.7" }, "scripts": { "build:dist-mjs": "rimraf dist-mjs && npx builder --type=module --sourceMap --outDir=dist-mjs", diff --git a/src/check-imports/check-imports.ts b/src/check-imports/check-imports.ts new file mode 100644 index 0000000..42ceda7 --- /dev/null +++ b/src/check-imports/check-imports.ts @@ -0,0 +1,38 @@ +// check-imports/check-imports.ts + +import { strict as assert } from 'node:assert'; + +import debug from 'debug'; + +import { extractPackageName, getPackageLock, satisfiesNameAndRange } from './package-lock-file-util'; +import notAllowed from './packages-not-allowed'; + +const log = debug('github-actions:check-imports'); + +export default async function main(): Promise { + log('Action starting'); + + const { packages } = await getPackageLock(process.cwd()); + + log('Reviewing package-lock'); + for (const key in packages) { + if (Object.hasOwn(packages, key)) { + const descriptor = packages[key]; + assert.ok(descriptor !== undefined, 'Package version is missing'); + const packageVersion = descriptor.version; + const packageName = extractPackageName(key); + + for (const [name, range, reason] of notAllowed) { + if (satisfiesNameAndRange(packageName, packageVersion, [name, range])) { + throw new Error( + `Package ${packageName}@${packageVersion} is not allowed to be imported because it is included in ${JSON.stringify( + [name, range], + )}. Package ${name}@${range} is not allowed for the following reason: ${reason}`, + ); + } + } + } + } + + log('Action end'); +} diff --git a/src/check-imports/index.ts b/src/check-imports/index.ts index 0df361c..4070dbc 100644 --- a/src/check-imports/index.ts +++ b/src/check-imports/index.ts @@ -1,49 +1,5 @@ // check-imports/index.ts -import { strict as assert } from 'node:assert'; +import main from './check-imports'; -import debug from 'debug'; - -import { extractPackageName, getPackageLock, satisfiesNameAndRange } from './package-lock-file-util'; -import notAllowed from './packages-not-allowed'; - -const log = debug('check-imports'); -export async function main(): Promise { - log('Action starting'); - - const { packages } = await getPackageLock(process.cwd()); - - log('Reviewing package-lock'); - for (const key in packages) { - if (Object.hasOwn(packages, key)) { - const descriptor = packages[key]; - assert.ok(descriptor !== undefined, 'Package version is missing'); - const packageVersion = descriptor.version; - const packageName = extractPackageName(key); - - for (const [name, range, reason] of notAllowed) { - if (satisfiesNameAndRange(packageName, packageVersion, [name, range])) { - throw new Error( - `Package ${packageName}@${packageVersion} is not allowed to be imported because it is included in ${JSON.stringify( - [name, range], - )}. Package ${name}@${range} is not allowed for the following reason: ${reason}`, - ); - } - } - } - } -} - -main() - .then(() => { - process.stdin.destroy(); - // eslint-disable-next-line unicorn/no-process-exit - process.exit(0); - }) - // eslint-disable-next-line unicorn/prefer-top-level-await - .catch((error) => { - // eslint-disable-next-line no-console - console.log('Action Error - exit 1 - error:', error); - // eslint-disable-next-line unicorn/no-process-exit - process.exit(1); - }); +await main(); diff --git a/src/check-imports/package-lock-file-util.spec.ts b/src/check-imports/package-lock-file-util.spec.ts index 27014a1..921aab3 100644 --- a/src/check-imports/package-lock-file-util.spec.ts +++ b/src/check-imports/package-lock-file-util.spec.ts @@ -1,27 +1,23 @@ // check-imports/package-lock-file-util.spec.ts import { strict as assert } from 'node:assert'; -import { mkdir, rm, writeFile } from 'node:fs/promises'; +import { promises as fs } from 'node:fs'; import path from 'node:path'; -import { tmpdir } from 'node:os'; +import os from 'node:os'; -import { afterAll, beforeAll, describe, it } from '@jest/globals'; +import { describe, it } from '@jest/globals'; +import { v4 as uuid } from 'uuid'; import examplePackageLock from './example-package-lock.json'; import { extractPackageName, getPackageLock, satisfiesNameAndRange } from './package-lock-file-util'; describe('package lock file utilities', () => { - beforeAll(async () => { - await mkdir(path.join(tmpdir(), 'temporaryDirectory'), { recursive: true }); - }); - - afterAll(async () => { - await rm(path.join(tmpdir(), 'temporaryDirectory'), { recursive: true }); - }); - it('can get a package-lock file', async () => { - await writeFile(path.join(tmpdir(), 'temporaryDirectory/package-lock.json'), JSON.stringify(examplePackageLock)); - const packageLock = await getPackageLock(path.join(tmpdir(), 'temporaryDirectory')); + const workFolder = path.join(os.tmpdir(), uuid()); + await fs.mkdir(workFolder); + + await fs.writeFile(path.join(workFolder, 'package-lock.json'), JSON.stringify(examplePackageLock)); + const packageLock = await getPackageLock(workFolder); assert.ok(packageLock.name === '@checkdigit/github-actions'); }); diff --git a/src/check-label/check-label.spec.ts b/src/check-label/check-label.spec.ts index cb68abf..b16a514 100644 --- a/src/check-label/check-label.spec.ts +++ b/src/check-label/check-label.spec.ts @@ -2,118 +2,71 @@ import { strict as assert } from 'node:assert'; import path from 'node:path'; -import { tmpdir } from 'node:os'; -import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'; +import os from 'node:os'; +import { promises as fs } from 'node:fs'; -import { afterAll, beforeAll, describe, it } from '@jest/globals'; +import { describe, it } from '@jest/globals'; import { v4 as uuid } from 'uuid'; -import gitHubNock from '../nocks/github.test'; +import gitHubNock, { + createGithubEventFile, + PR_NUMBER_MAJOR, + PR_NUMBER_MINOR, + PR_NUMBER_PATCH, +} from '../nocks/github.test'; import checkLabel from './check-label'; async function createContext(prNumber: number) { process.env['GITHUB_REPOSITORY'] = 'checkdigit/testlabel'; - const filePath = path.join(tmpdir(), 'actioncontexttestlabel', uuid()); - await writeFile( - filePath, - JSON.stringify({ - // eslint-disable-next-line camelcase - pull_request: { - number: prNumber, - }, - }), - ); - process.env['GITHUB_EVENT_PATH'] = filePath; -} - -function semverSubtract(version: string, versionLabel: 'patch' | 'major' | 'minor'): string { - const versionParts = version.split('.'); - if (versionLabel === 'major' && Number(versionParts[0]) !== 0) { - versionParts[0] = (Number(versionParts[0]) - 1).toString(); - } - - if (versionLabel === 'minor' && Number(versionParts[1]) !== 0) { - versionParts[1] = (Number(versionParts[1]) - 1).toString(); - } - - if (versionLabel === 'patch' && Number(versionParts[2]) !== 0) { - versionParts[2] = (Number(versionParts[2]) - 1).toString(); - } - - return versionParts.join('.'); + process.env['GITHUB_EVENT_PATH'] = await createGithubEventFile(prNumber); } describe('check label', () => { - beforeAll(async () => mkdir(path.join(tmpdir(), 'actioncontexttestlabel'))); - afterAll(async () => rm(path.join(tmpdir(), 'actioncontexttestlabel'), { recursive: true })); - it('Test with no labels throws correctly', async () => { // assert that the call to checkLabel rejects a promise await assert.rejects(checkLabel()); }); - /* -------------------- enable if the current PR label is patch -------------------- */ - - // eslint-disable-next-line jest/no-disabled-tests - it.skip('label matches - patch', async () => { - process.env['GITHUB_TOKEN'] = 'token 0000000000000000000000000000000000000001'; - - const packageJsonRaw = await readFile(path.join(process.cwd(), 'package.json'), 'utf8'); - const packageJson = JSON.parse(packageJsonRaw); - - const targetVersion = semverSubtract(packageJson.version, 'patch'); - gitHubNock({ labelPackageVersionMain: targetVersion }); - - await createContext(10); - - await assert.doesNotReject(checkLabel()); - }); - - // eslint-disable-next-line jest/no-disabled-tests - it.skip('label does not match - should be major but is patch', async () => { - process.env['GITHUB_TOKEN'] = 'token 0000000000000000000000000000000000000001'; - - const packageJsonRaw = await readFile(path.join(process.cwd(), 'package.json'), 'utf8'); - const packageJson = JSON.parse(packageJsonRaw); - - const targetVersion = semverSubtract(packageJson.version, 'patch'); - gitHubNock({ labelPackageVersionMain: targetVersion }); - - await createContext(11); - - await assert.rejects(checkLabel(), { - message: 'Version is incorrect based on Pull Request label', - }); - }); - - /* -------------------- enable if the current PR label is major -------------------- */ - it('label matches - major', async () => { - process.env['GITHUB_TOKEN'] = 'token 0000000000000000000000000000000000000001'; - - const packageJsonRaw = await readFile(path.join(process.cwd(), 'package.json'), 'utf8'); - const packageJson = JSON.parse(packageJsonRaw); - - const targetVersion = semverSubtract(packageJson.version, 'major'); - gitHubNock({ labelPackageVersionMain: targetVersion }); - - await createContext(10); - - await assert.rejects(checkLabel(), { - message: 'Version is incorrect based on Pull Request label', - }); - }); - - it('label does not match - should be major but is major', async () => { - process.env['GITHUB_TOKEN'] = 'token 0000000000000000000000000000000000000001'; - - const packageJsonRaw = await readFile(path.join(process.cwd(), 'package.json'), 'utf8'); - const packageJson = JSON.parse(packageJsonRaw); - - const targetVersion = semverSubtract(packageJson.version, 'major'); - gitHubNock({ labelPackageVersionMain: targetVersion }); - - await createContext(11); - - await assert.doesNotReject(checkLabel()); - }); + it.each([ + { mainVersion: '1.0.0', currentVersion: '1.0.0', prNumber: PR_NUMBER_PATCH, success: false }, + { mainVersion: '1.0.0', currentVersion: '1.0.1', prNumber: PR_NUMBER_PATCH, success: true }, + { mainVersion: '1.0.0', currentVersion: '1.1.0', prNumber: PR_NUMBER_PATCH, success: false }, + { mainVersion: '1.0.0', currentVersion: '2.0.0', prNumber: PR_NUMBER_PATCH, success: false }, + { mainVersion: '1.1.0', currentVersion: '1.1.0', prNumber: PR_NUMBER_MINOR, success: false }, + { mainVersion: '1.1.0', currentVersion: '1.1.1', prNumber: PR_NUMBER_MINOR, success: false }, + { mainVersion: '1.1.1', currentVersion: '1.2.0', prNumber: PR_NUMBER_MINOR, success: true }, + { mainVersion: '1.1.1', currentVersion: '1.2.1', prNumber: PR_NUMBER_MINOR, success: false }, + { mainVersion: '1.1.1', currentVersion: '2.0.0', prNumber: PR_NUMBER_MINOR, success: false }, + { mainVersion: '2.2.2', currentVersion: '2.2.2', prNumber: PR_NUMBER_MAJOR, success: false }, + { mainVersion: '2.2.2', currentVersion: '2.2.3', prNumber: PR_NUMBER_MAJOR, success: false }, + { mainVersion: '2.2.2', currentVersion: '2.3.0', prNumber: PR_NUMBER_MAJOR, success: false }, + { mainVersion: '2.2.2', currentVersion: '3.0.0', prNumber: PR_NUMBER_MAJOR, success: true }, + { mainVersion: '2.2.2', currentVersion: '3.0.2', prNumber: PR_NUMBER_MAJOR, success: false }, + { mainVersion: '2.2.2', currentVersion: '3.2.2', prNumber: PR_NUMBER_MAJOR, success: false }, + ])( + 'pull request: $prNumber; version in main branch: $mainVersion; version in PR branch: $currentVersion; success: $success', + async ({ mainVersion, currentVersion, prNumber, success }) => { + process.env['GITHUB_TOKEN'] = 'token 0000000000000000000000000000000000000001'; + + gitHubNock({ labelPackageVersionMain: mainVersion }); + + const workFolder = path.join(os.tmpdir(), uuid()); + await fs.mkdir(workFolder); + await fs.writeFile(path.join(workFolder, 'package.json'), JSON.stringify({ version: currentVersion })); + await fs.writeFile(path.join(workFolder, 'package-lock.json'), JSON.stringify({ version: currentVersion })); + + const originalCwd = process.cwd(); + try { + process.chdir(workFolder); + await createContext(prNumber); + if (success) { + await assert.doesNotReject(checkLabel()); + } else { + await assert.rejects(checkLabel()); + } + } finally { + process.chdir(originalCwd); + } + }, + ); }); diff --git a/src/check-label/check-label.ts b/src/check-label/check-label.ts index c093ff9..69bc977 100644 --- a/src/check-label/check-label.ts +++ b/src/check-label/check-label.ts @@ -9,7 +9,7 @@ import semver from 'semver'; import { getFileFromMain, getLabelsOnPR } from '../github-api'; -const log = debug('check-label'); +const log = debug('github-actions:check-label'); interface PackageJSON { name: string; @@ -51,7 +51,7 @@ export function validateVersion( } const expectedVersion = mainVersionSplit.join('.'); - assert.equal(expectedVersion, branchPackageJsonVersion, 'Version is incorrect based on Pull Request label'); + assert.equal(branchPackageJsonVersion, expectedVersion, 'Version is incorrect based on Pull Request label'); return true; } @@ -76,5 +76,7 @@ export default async function (): Promise { validateVersion(branchPackageJsonVersion, mainPackageJsonVersion.version, label); const branchLockFile = await getLocalPackageJsonVersion('package-lock.json'); - assert.equal(branchLockFile, branchPackageJsonVersion, 'package.json and package-lock.json versions do not match'); + assert.equal(branchPackageJsonVersion, branchLockFile, 'package.json and package-lock.json versions do not match'); + + log('Action end'); } diff --git a/src/check-label/index.ts b/src/check-label/index.ts index 0d43d0e..8909579 100644 --- a/src/check-label/index.ts +++ b/src/check-label/index.ts @@ -1,17 +1,5 @@ // check-label/index.ts -import checkLabel from './check-label'; +import main from './check-label'; -checkLabel() - .then(() => { - process.stdin.destroy(); - // eslint-disable-next-line unicorn/no-process-exit - process.exit(0); - }) - // eslint-disable-next-line unicorn/prefer-top-level-await - .catch((error) => { - // eslint-disable-next-line no-console - console.log('Action Error - exit 1 - error:', error); - // eslint-disable-next-line unicorn/no-process-exit - process.exit(1); - }); +await main(); diff --git a/src/check-pr-reviews/check-pr-reviews.ts b/src/check-pr-reviews/check-pr-reviews.ts new file mode 100644 index 0000000..5a19581 --- /dev/null +++ b/src/check-pr-reviews/check-pr-reviews.ts @@ -0,0 +1,50 @@ +// check-pr-reviews/check-pr-reviews.ts + +import debug from 'debug'; +import { setFailed } from '@actions/core'; + +import { approvedReviews, haveAllReviewersReviewed, publishCommentAndRemovePrevious } from '../github-api'; + +const PULL_REQUEST_MESSAGE_KEYWORD = 'PR review status '; +const log = debug('github-actions:check-pr-reviews'); + +export default async function (): Promise { + log('Action start'); + + const yetToReview = await haveAllReviewersReviewed(); + if (yetToReview > 0) { + const reviewOutstandingMessage = + yetToReview === 1 ? `has ${yetToReview} reviewer outstanding` : `has ${yetToReview} reviewers outstanding`; + await publishCommentAndRemovePrevious( + `:x: PR review status - ${reviewOutstandingMessage}`, + PULL_REQUEST_MESSAGE_KEYWORD, + ); + setFailed(`PR has not been reviewed correctly - ${reviewOutstandingMessage}`); + throw new Error(`PR has not been reviewed correctly - ${reviewOutstandingMessage}`); + } + + const reviews = await approvedReviews(); + + if (reviews.approvedReviews < reviews.totalReviewers) { + const approvedReviewsOutstandingMessage = + reviews.totalReviewers === 1 + ? `reviewer has not approved` + : `not all reviewers have approved - ${reviews.approvedReviews} approved - ${ + reviews.totalReviewers - reviews.approvedReviews + } outstanding`; + + await publishCommentAndRemovePrevious( + `:x: PR review status - ${approvedReviewsOutstandingMessage}`, + PULL_REQUEST_MESSAGE_KEYWORD, + ); + setFailed(`PR has not been reviewed correctly - ${approvedReviewsOutstandingMessage}`); + throw new Error('PR has not been reviewed correctly - not all reviewers have approved'); + } + + await publishCommentAndRemovePrevious( + ':white_check_mark: PR review status - All reviews completed and approved!', + PULL_REQUEST_MESSAGE_KEYWORD, + ); + + log('Action end'); +} diff --git a/src/check-pr-reviews/index.ts b/src/check-pr-reviews/index.ts index aac5ab2..10fb5ad 100644 --- a/src/check-pr-reviews/index.ts +++ b/src/check-pr-reviews/index.ts @@ -1,61 +1,5 @@ // check-pr-reviews/index.ts -import debug from 'debug'; -import { setFailed } from '@actions/core'; +import main from './check-pr-reviews'; -import { approvedReviews, haveAllReviewersReviewed, publishCommentAndRemovePrevious } from '../github-api'; -const PULL_REQUEST_MESSAGE_KEYWORD = 'PR review status '; - -const log = debug('check-pr-reviews'); -export async function main(): Promise { - log('Action start'); - - const yetToReview = await haveAllReviewersReviewed(); - if (yetToReview > 0) { - const reviewOutstandingMessage = - yetToReview === 1 ? `has ${yetToReview} reviewer outstanding` : `has ${yetToReview} reviewers outstanding`; - await publishCommentAndRemovePrevious( - `:x: PR review status - ${reviewOutstandingMessage}`, - PULL_REQUEST_MESSAGE_KEYWORD, - ); - setFailed(`PR has not been reviewed correctly - ${reviewOutstandingMessage}`); - throw new Error(`PR has not been reviewed correctly - ${reviewOutstandingMessage}`); - } - - const reviews = await approvedReviews(); - - if (reviews.approvedReviews < reviews.totalReviewers) { - const approvedReviewsOutstandingMessage = - reviews.totalReviewers === 1 - ? `reviewer has not approved` - : `not all reviewers have approved - ${reviews.approvedReviews} approved - ${ - reviews.totalReviewers - reviews.approvedReviews - } outstanding`; - - await publishCommentAndRemovePrevious( - `:x: PR review status - ${approvedReviewsOutstandingMessage}`, - PULL_REQUEST_MESSAGE_KEYWORD, - ); - setFailed(`PR has not been reviewed correctly - ${approvedReviewsOutstandingMessage}`); - throw new Error('PR has not been reviewed correctly - not all reviewers have approved'); - } - - await publishCommentAndRemovePrevious( - ':white_check_mark: PR review status - All reviews completed and approved!', - PULL_REQUEST_MESSAGE_KEYWORD, - ); -} - -main() - .then(() => { - process.stdin.destroy(); - // eslint-disable-next-line unicorn/no-process-exit - process.exit(0); - }) - // eslint-disable-next-line unicorn/prefer-top-level-await - .catch((error) => { - // eslint-disable-next-line no-console - console.log('Action Error - exit 1 - error:', error); - // eslint-disable-next-line unicorn/no-process-exit - process.exit(1); - }); +await main(); diff --git a/src/check-published/check-published.ts b/src/check-published/check-published.ts new file mode 100644 index 0000000..ed71f03 --- /dev/null +++ b/src/check-published/check-published.ts @@ -0,0 +1,65 @@ +// check-published/check-published.ts + +import { readFile } from 'node:fs/promises'; +import path from 'node:path'; +import { promisify } from 'node:util'; +import { exec } from 'node:child_process'; + +import debug from 'debug'; + +import slackPost, { postErrorToSlack } from './slack'; + +const execAsync = promisify(exec); +const log = debug('github-actions:check-published'); + +interface PackageJSON { + name: string; + version: string; +} + +async function getLocalPackageJson(fileName: string): Promise { + const packageJSONPath = path.join(process.cwd(), fileName); + const readPackageJson = await readFile(packageJSONPath, 'utf8'); + return JSON.parse(readPackageJson) as PackageJSON; +} + +async function getVersionFromNPM(packageName: string): Promise { + const { stdout: latestVersion } = await execAsync(`npm show ${packageName} version`); + return latestVersion.replaceAll(/[\n\r]/gu, '').trim(); // rm any new lines +} + +export default async function (): Promise { + log('Action starting'); + + let mainPackageJson: PackageJSON; + try { + mainPackageJson = await getLocalPackageJson('package.json'); + } catch { + const errorMessage = 'Action failed - could not read package.json'; + await postErrorToSlack(errorMessage); + log(errorMessage); + throw new Error(errorMessage); + } + + log(`Main package.json - name ${mainPackageJson.name} - version ${mainPackageJson.version}`); + + // run npm show to get the latest version published + let latestVersion: string; + try { + latestVersion = await getVersionFromNPM(mainPackageJson.name); + } catch { + const errorMessage = `Action failed - could not get latest version from npm - name: ${mainPackageJson.name}`; + await postErrorToSlack(errorMessage); + throw new Error(errorMessage); + } + + log(`Latest version published - ${latestVersion}`); + + if (mainPackageJson.version !== latestVersion) { + log('Action failed - version published does not match'); + await slackPost(mainPackageJson.name, mainPackageJson.version, latestVersion); + throw new Error(`Version published does not match - ${mainPackageJson.version} !== ${latestVersion}`); + } + + log('Action end'); +} diff --git a/src/check-published/index.ts b/src/check-published/index.ts index 2fc9891..393c355 100644 --- a/src/check-published/index.ts +++ b/src/check-published/index.ts @@ -1,75 +1,5 @@ // check-published/index.ts -import { readFile } from 'node:fs/promises'; -import path from 'node:path'; -import { promisify } from 'node:util'; -import { exec } from 'node:child_process'; +import main from './check-published'; -import debug from 'debug'; - -import slackPost, { postErrorToSlack } from './slack'; - -const execAsync = promisify(exec); - -const log = debug('check-published'); -interface PackageJSON { - name: string; - version: string; -} -async function getLocalPackageJson(fileName: string): Promise { - const packageJSONPath = path.join(process.cwd(), fileName); - const readPackageJson = await readFile(packageJSONPath, 'utf8'); - return JSON.parse(readPackageJson) as PackageJSON; -} - -async function getVersionFromNPM(packageName: string): Promise { - const { stdout: latestVersion } = await execAsync(`npm show ${packageName} version`); - return latestVersion.replaceAll(/[\n\r]/gu, '').trim(); // rm any new lines -} - -export async function main(): Promise { - log('Action starting'); - let mainPackageJson: PackageJSON; - try { - mainPackageJson = await getLocalPackageJson('package.json'); - } catch { - const errorMessage = 'Action failed - could not read package.json'; - await postErrorToSlack(errorMessage); - log(errorMessage); - throw new Error(errorMessage); - } - - log(`Main package.json - name ${mainPackageJson.name} - version ${mainPackageJson.version}`); - - // run npm show to get the latest version published - let latestVersion: string; - try { - latestVersion = await getVersionFromNPM(mainPackageJson.name); - } catch { - const errorMessage = `Action failed - could not get latest version from npm - name: ${mainPackageJson.name}`; - await postErrorToSlack(errorMessage); - throw new Error(errorMessage); - } - - log(`Latest version published - ${latestVersion}`); - - if (mainPackageJson.version !== latestVersion) { - log('Action failed - version published does not match'); - await slackPost(mainPackageJson.name, mainPackageJson.version, latestVersion); - throw new Error(`Version published does not match - ${mainPackageJson.version} !== ${latestVersion}`); - } -} - -main() - .then(() => { - process.stdin.destroy(); - // eslint-disable-next-line unicorn/no-process-exit - process.exit(0); - }) - // eslint-disable-next-line unicorn/prefer-top-level-await - .catch((error) => { - // eslint-disable-next-line no-console - console.log('Action Error - exit 1 - error:', error); - // eslint-disable-next-line unicorn/no-process-exit - process.exit(1); - }); +await main(); diff --git a/src/check-published/slack.ts b/src/check-published/slack.ts index be224f1..1237403 100644 --- a/src/check-published/slack.ts +++ b/src/check-published/slack.ts @@ -4,7 +4,7 @@ import { strict as assert } from 'node:assert'; import debug from 'debug'; import { fetch } from 'undici'; -const log = debug('check-published:slack'); +const log = debug('github-actions:check-published:slack'); export interface SlackMessage { text: string; diff --git a/src/comment-npm-publish/comment-npm-publish.ts b/src/comment-npm-publish/comment-npm-publish.ts new file mode 100644 index 0000000..c570faa --- /dev/null +++ b/src/comment-npm-publish/comment-npm-publish.ts @@ -0,0 +1,22 @@ +// comment-npm-publish/comment-npm-publish.ts + +import debug from 'debug'; +import { getInput } from '@actions/core'; + +import { publishCommentAndRemovePrevious } from '../github-api'; + +const log = debug('github-actions:comment-npm-publish'); + +export default async function (): Promise { + log('Action start'); + + const packageNameAndBetaVersion = getInput('betaPackage'); + log('Package Name and Version obtained: ', packageNameAndBetaVersion); + + await publishCommentAndRemovePrevious( + `Beta Published - Install Command: \`npm install ${packageNameAndBetaVersion}\` `.replaceAll('"', ''), + 'Beta Published - Install Command: ', + ); + + log('Action end'); +} diff --git a/src/comment-npm-publish/index.ts b/src/comment-npm-publish/index.ts index 93b469f..b1434ea 100644 --- a/src/comment-npm-publish/index.ts +++ b/src/comment-npm-publish/index.ts @@ -1,33 +1,5 @@ // comment-npm-publish/index.ts -import debug from 'debug'; -import { getInput } from '@actions/core'; +import main from './comment-npm-publish'; -import { publishCommentAndRemovePrevious } from '../github-api'; - -const log = debug('comment-npm-publish'); -export async function main(): Promise { - log('Action start'); - - const packageNameAndBetaVersion = getInput('betaPackage'); - log('Package Name and Version obtained: ', packageNameAndBetaVersion); - await publishCommentAndRemovePrevious( - `Beta Published - Install Command: \`npm install ${packageNameAndBetaVersion}\` `.replaceAll('"', ''), - 'Beta Published - Install Command: ', - ); - log('Action end'); -} - -main() - .then(() => { - process.stdin.destroy(); - // eslint-disable-next-line unicorn/no-process-exit - process.exit(0); - }) - // eslint-disable-next-line unicorn/prefer-top-level-await - .catch((error) => { - // eslint-disable-next-line no-console - console.log('Action Error - exit 1 - error:', error); - // eslint-disable-next-line unicorn/no-process-exit - process.exit(1); - }); +await main(); diff --git a/src/coverage-reporter/coverage-reporter.ts b/src/coverage-reporter/coverage-reporter.ts new file mode 100644 index 0000000..a7054e4 --- /dev/null +++ b/src/coverage-reporter/coverage-reporter.ts @@ -0,0 +1,109 @@ +// coverage-reporter/coverage-reporter.ts + +/** + * Based on code from lcov-report-action, see LICENSE.lcov-reporter-action.txt. + * https://github.com/romeovs/lcov-reporter-action + */ + +import { promises as fs } from 'node:fs'; +import path from 'node:path'; + +import { getInput, setFailed } from '@actions/core'; +import { context, getOctokit } from '@actions/github'; +import debug from 'debug'; + +import { parse } from './lcov'; +import { diff } from './comment'; +import { getChangedFiles } from './get-changes'; +import { deleteOldComments } from './delete-old-comments'; +import type { Options } from './options'; +import { normalizePath } from './util'; + +const MAX_COMMENT_CHARS = 65_536; +const log = debug('github-actions:coverage-reporter'); + +export default async function (): Promise { + try { + log('Action start'); + + const token = getInput('github-token'); + const githubClient = getOctokit(token); + const workingDirectory = getInput('working-directory') || './'; + const lcovFile = path.join(workingDirectory, getInput('lcov-file') || './coverage/lcov.info'); + const baseFile = getInput('lcov-base'); + const shouldFilterChangedFiles = getInput('filter-changed-files').toLowerCase() === 'true'; + const shouldDeleteOldComments = getInput('delete-old-comments').toLowerCase() === 'true'; + const title = getInput('title'); + + const raw = await fs.readFile(lcovFile, 'utf8').catch(() => null); + if (raw === null || raw === '') { + // eslint-disable-next-line no-console + console.log(`No coverage report found at '${lcovFile}', exiting...`); + return; + } + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const baseRaw = baseFile && (await fs.readFile(baseFile, 'utf8').catch(() => null))!; + if (baseFile && !baseRaw) { + // eslint-disable-next-line no-console + console.log(`No coverage report found at '${baseFile}', ignoring...`); + } + + const options = { + repository: context.payload.repository?.full_name, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + prefix: normalizePath(`${process.env['GITHUB_WORKSPACE']!}/`), + workingDir: workingDirectory, + } as Options; + + if (context.eventName === 'pull_request') { + options.commit = (context.payload.pull_request?.['head'] as { sha: string }).sha; + options.baseCommit = (context.payload.pull_request?.['base'] as { sha: string }).sha; + options.head = (context.payload.pull_request?.['head'] as { ref: string }).ref; + options.base = (context.payload.pull_request?.['base'] as { ref: string }).ref; + } else if (context.eventName === 'push') { + options.commit = context.payload['after'] as string; + options.baseCommit = context.payload['before'] as string; + options.head = context.ref; + } + + options.shouldFilterChangedFiles = shouldFilterChangedFiles; + options.title = title; + + if (shouldFilterChangedFiles) { + options.changedFiles = await getChangedFiles(githubClient, options, context); + } + + const lcov = parse(raw); + const baseLcov = parse(baseRaw); + const body = diff(lcov, baseLcov, options).slice(0, Math.max(0, MAX_COMMENT_CHARS)); + + if (shouldDeleteOldComments) { + await deleteOldComments(githubClient, options, context); + } + + if (context.eventName === 'pull_request') { + await githubClient.rest.issues.createComment({ + repo: context.repo.repo, + owner: context.repo.owner, + // eslint-disable-next-line camelcase, @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-non-null-asserted-optional-chain + issue_number: context.payload.pull_request?.number!, + body, + }); + } else if (context.eventName === 'push') { + await githubClient.rest.repos.createCommitComment({ + repo: context.repo.repo, + owner: context.repo.owner, + // eslint-disable-next-line camelcase + commit_sha: options.commit, + body, + }); + } + + log('Action end'); + } catch (error) { + // eslint-disable-next-line no-console + console.log(error); + setFailed((error as { message: string }).message); + } +} diff --git a/src/coverage-reporter/index.ts b/src/coverage-reporter/index.ts index 207aec6..440e970 100644 --- a/src/coverage-reporter/index.ts +++ b/src/coverage-reporter/index.ts @@ -1,104 +1,5 @@ // coverage-reporter/index.ts -/** - * Based on code from lcov-report-action, see LICENSE.lcov-reporter-action.txt. - * https://github.com/romeovs/lcov-reporter-action - */ +import main from './coverage-reporter'; -import { promises as fs } from 'node:fs'; -import path from 'node:path'; - -import { getInput, setFailed } from '@actions/core'; -import { context, getOctokit } from '@actions/github'; - -import { parse } from './lcov'; -import { diff } from './comment'; -import { getChangedFiles } from './get-changes'; -import { deleteOldComments } from './delete-old-comments'; -import type { Options } from './options'; -import { normalizePath } from './util'; - -const MAX_COMMENT_CHARS = 65_536; - -async function main() { - const token = getInput('github-token'); - const githubClient = getOctokit(token); - const workingDirectory = getInput('working-directory') || './'; - const lcovFile = path.join(workingDirectory, getInput('lcov-file') || './coverage/lcov.info'); - const baseFile = getInput('lcov-base'); - const shouldFilterChangedFiles = getInput('filter-changed-files').toLowerCase() === 'true'; - const shouldDeleteOldComments = getInput('delete-old-comments').toLowerCase() === 'true'; - const title = getInput('title'); - - const raw = await fs.readFile(lcovFile, 'utf8').catch(() => null); - if (raw === null || raw === '') { - // eslint-disable-next-line no-console - console.log(`No coverage report found at '${lcovFile}', exiting...`); - return; - } - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const baseRaw = baseFile && (await fs.readFile(baseFile, 'utf8').catch(() => null))!; - if (baseFile && !baseRaw) { - // eslint-disable-next-line no-console - console.log(`No coverage report found at '${baseFile}', ignoring...`); - } - - const options = { - repository: context.payload.repository?.full_name, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - prefix: normalizePath(`${process.env['GITHUB_WORKSPACE']!}/`), - workingDir: workingDirectory, - } as Options; - - if (context.eventName === 'pull_request') { - options.commit = (context.payload.pull_request?.['head'] as { sha: string }).sha; - options.baseCommit = (context.payload.pull_request?.['base'] as { sha: string }).sha; - options.head = (context.payload.pull_request?.['head'] as { ref: string }).ref; - options.base = (context.payload.pull_request?.['base'] as { ref: string }).ref; - } else if (context.eventName === 'push') { - options.commit = context.payload['after'] as string; - options.baseCommit = context.payload['before'] as string; - options.head = context.ref; - } - - options.shouldFilterChangedFiles = shouldFilterChangedFiles; - options.title = title; - - if (shouldFilterChangedFiles) { - options.changedFiles = await getChangedFiles(githubClient, options, context); - } - - const lcov = parse(raw); - const baseLcov = parse(baseRaw); - const body = diff(lcov, baseLcov, options).slice(0, Math.max(0, MAX_COMMENT_CHARS)); - - if (shouldDeleteOldComments) { - await deleteOldComments(githubClient, options, context); - } - - if (context.eventName === 'pull_request') { - await githubClient.rest.issues.createComment({ - repo: context.repo.repo, - owner: context.repo.owner, - // eslint-disable-next-line camelcase, @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-non-null-asserted-optional-chain - issue_number: context.payload.pull_request?.number!, - body, - }); - } else if (context.eventName === 'push') { - await githubClient.rest.repos.createCommitComment({ - repo: context.repo.repo, - owner: context.repo.owner, - // eslint-disable-next-line camelcase - commit_sha: options.commit, - body, - }); - } -} - -// eslint-disable-next-line unicorn/prefer-top-level-await -main().catch((error) => { - // eslint-disable-next-line no-console - console.log(error); - setFailed((error as { message: string }).message); -}); +await main(); diff --git a/src/github-api/index-context.spec.ts b/src/github-api/index-context.spec.ts index 7319dd1..2baa127 100644 --- a/src/github-api/index-context.spec.ts +++ b/src/github-api/index-context.spec.ts @@ -1,19 +1,16 @@ // github-api/index-context.spec.ts import { strict as assert } from 'node:assert'; -import { tmpdir } from 'node:os'; +import os from 'node:os'; import path from 'node:path'; -import { mkdir, rm, writeFile } from 'node:fs/promises'; -import { afterAll, beforeAll, describe, it } from '@jest/globals'; +import { describe, it } from '@jest/globals'; import { v4 as uuid } from 'uuid'; +import { createGithubEventFile, PR_NUMBER_DEFAULT } from '../nocks/github.test'; import { getPullRequestContext } from './index'; describe('github context', () => { - beforeAll(async () => mkdir(path.join(tmpdir(), 'actioncontexttest'))); - afterAll(async () => rm(path.join(tmpdir(), 'actioncontexttest'), { recursive: true })); - it('no environment variable', async () => { process.env['GITHUB_EVENT_PATH'] = ''; const result = await getPullRequestContext(); @@ -21,23 +18,13 @@ describe('github context', () => { }); it('unable to open file - file does not exist', async () => { - process.env['GITHUB_EVENT_PATH'] = path.join(tmpdir(), uuid()); + process.env['GITHUB_EVENT_PATH'] = path.join(os.tmpdir(), uuid()); assert.equal(await getPullRequestContext(), undefined); }); it('standard happy path', async () => { process.env['GITHUB_REPOSITORY'] = 'checkdigit/test'; - const filePath = path.join(tmpdir(), 'actioncontexttest', uuid()); - await writeFile( - filePath, - JSON.stringify({ - // eslint-disable-next-line camelcase - pull_request: { - number: 10, - }, - }), - ); - process.env['GITHUB_EVENT_PATH'] = filePath; - assert.deepEqual(await getPullRequestContext(), { owner: 'checkdigit', number: 10, repo: 'test' }); + process.env['GITHUB_EVENT_PATH'] = await createGithubEventFile(PR_NUMBER_DEFAULT); + assert.deepEqual(await getPullRequestContext(), { owner: 'checkdigit', number: PR_NUMBER_DEFAULT, repo: 'test' }); }); }); diff --git a/src/github-api/index-publish-comment.spec.ts b/src/github-api/index-publish-comment.spec.ts index 8e479a3..f686445 100644 --- a/src/github-api/index-publish-comment.spec.ts +++ b/src/github-api/index-publish-comment.spec.ts @@ -1,20 +1,14 @@ // github-api/index-publish-comment.spec.ts import { strict as assert } from 'node:assert'; -import { tmpdir } from 'node:os'; -import path from 'node:path'; -import { mkdir, rm, writeFile } from 'node:fs/promises'; -import { afterAll, beforeAll, describe, it } from '@jest/globals'; +import { describe, it } from '@jest/globals'; import { v4 as uuid } from 'uuid'; -import gitHubNock from '../nocks/github.test'; +import gitHubNock, { createGithubEventFile } from '../nocks/github.test'; import { publishCommentAndRemovePrevious } from './index'; describe('github publish', () => { - beforeAll(async () => mkdir(path.join(tmpdir(), 'actionpublishcommenttest'))); - afterAll(async () => rm(path.join(tmpdir(), 'actionpublishcommenttest'), { recursive: true })); - it('no token', async () => { await assert.rejects(publishCommentAndRemovePrevious(uuid(), uuid())); }); @@ -29,17 +23,7 @@ describe('github publish', () => { gitHubNock(); process.env['GITHUB_REPOSITORY'] = 'checkdigit/nocomments'; process.env['GITHUB_TOKEN'] = 'token 0000000000000000000000000000000000000001'; - const filePath = path.join(tmpdir(), 'actionpublishcommenttest', uuid()); - await writeFile( - filePath, - JSON.stringify({ - // eslint-disable-next-line camelcase - pull_request: { - number: 10, - }, - }), - ); - process.env['GITHUB_EVENT_PATH'] = filePath; + process.env['GITHUB_EVENT_PATH'] = await createGithubEventFile(); await publishCommentAndRemovePrevious(uuid(), uuid()); assert(true); }); @@ -49,17 +33,7 @@ describe('github publish', () => { gitHubNock(); process.env['GITHUB_REPOSITORY'] = 'checkdigit/comments'; process.env['GITHUB_TOKEN'] = 'token 0000000000000000000000000000000000000001'; - const filePath = path.join(tmpdir(), 'actionpublishcommenttest', uuid()); - await writeFile( - filePath, - JSON.stringify({ - // eslint-disable-next-line camelcase - pull_request: { - number: 10, - }, - }), - ); - process.env['GITHUB_EVENT_PATH'] = filePath; + process.env['GITHUB_EVENT_PATH'] = await createGithubEventFile(); await publishCommentAndRemovePrevious(uuid(), uuid()); assert(true); }); diff --git a/src/github-api/index-reviews.spec.ts b/src/github-api/index-reviews.spec.ts index 3fb0d89..3656ffb 100644 --- a/src/github-api/index-reviews.spec.ts +++ b/src/github-api/index-reviews.spec.ts @@ -1,38 +1,19 @@ // github-api/index-reviews.spec.ts import { strict as assert } from 'node:assert'; -import { tmpdir } from 'node:os'; -import path from 'node:path'; -import { mkdir, rm, writeFile } from 'node:fs/promises'; -import { afterAll, beforeAll, describe, it } from '@jest/globals'; -import { v4 as uuid } from 'uuid'; +import { describe, it } from '@jest/globals'; -import gitHubNock from '../nocks/github.test'; +import gitHubNock, { createGithubEventFile } from '../nocks/github.test'; import { approvedReviews, haveAllReviewersReviewed } from './index'; -const actionFolderName = 'actionreviewtest'; - describe('github review', () => { - beforeAll(async () => mkdir(path.join(tmpdir(), actionFolderName))); - afterAll(async () => rm(path.join(tmpdir(), actionFolderName), { recursive: true })); - it('review two outstanding reviewers', async () => { // setGlobalDispatcher(gitHubNock); gitHubNock(); process.env['GITHUB_REPOSITORY'] = 'checkdigit/previewOutstanding'; process.env['GITHUB_TOKEN'] = 'token 0000000000000000000000000000000000000001'; - const filePath = path.join(tmpdir(), actionFolderName, uuid()); - await writeFile( - filePath, - JSON.stringify({ - // eslint-disable-next-line camelcase - pull_request: { - number: 10, - }, - }), - ); - process.env['GITHUB_EVENT_PATH'] = filePath; + process.env['GITHUB_EVENT_PATH'] = await createGithubEventFile(); const result = await haveAllReviewersReviewed(); assert.equal(result, 2); }); @@ -42,17 +23,7 @@ describe('github review', () => { gitHubNock(); process.env['GITHUB_REPOSITORY'] = 'checkdigit/preview'; process.env['GITHUB_TOKEN'] = 'token 0000000000000000000000000000000000000001'; - const filePath = path.join(tmpdir(), actionFolderName, uuid()); - await writeFile( - filePath, - JSON.stringify({ - // eslint-disable-next-line camelcase - pull_request: { - number: 10, - }, - }), - ); - process.env['GITHUB_EVENT_PATH'] = filePath; + process.env['GITHUB_EVENT_PATH'] = await createGithubEventFile(); const result = await approvedReviews(); assert.deepEqual(result, { approvedReviews: 2, totalReviewers: 2 }); }); diff --git a/src/github-api/index.ts b/src/github-api/index.ts index dbf7f70..caf2c7c 100644 --- a/src/github-api/index.ts +++ b/src/github-api/index.ts @@ -32,7 +32,7 @@ export interface PullRequestState { }; } -const log = debug('publish-beta:github'); +const log = debug('github-actions:publish-beta:github'); export function getPRNumber(): string { const prNumberSearch = process.env['GITHUB_REF']?.match(/[0-9]+/gu); // matches the PR number diff --git a/src/nocks/github.test.ts b/src/nocks/github.test.ts index de30667..c6f3477 100644 --- a/src/nocks/github.test.ts +++ b/src/nocks/github.test.ts @@ -1,16 +1,47 @@ // nocks/github.test.ts +import os from 'node:os'; +import { promises as fs } from 'node:fs'; +import path from 'node:path'; + import nock from 'nock'; +import { v4 as uuid } from 'uuid'; + +export const PR_NUMBER_PATCH = 1; +export const PR_NUMBER_MINOR = 20; +export const PR_NUMBER_MAJOR = 300; +export const PR_NUMBER_DEFAULT = PR_NUMBER_PATCH; export interface GithubNock { labelPackageVersionMain?: string; } + +export async function createGithubEventFile(prNumber = PR_NUMBER_DEFAULT): Promise { + const filePath = path.join(os.tmpdir(), uuid()); + await fs.writeFile( + filePath, + JSON.stringify({ + // eslint-disable-next-line camelcase + pull_request: { + number: prNumber, + }, + }), + ); + return filePath; +} + export default function (options?: GithubNock): void { - nock('https://api.github.com/').persist().get('/repos/checkdigit/nocomments/issues/10/comments').reply(200); - nock('https://api.github.com/').persist().post('/repos/checkdigit/nocomments/issues/10/comments').reply(200); + nock('https://api.github.com/') + .persist() + .get(`/repos/checkdigit/nocomments/issues/${PR_NUMBER_DEFAULT}/comments`) + .reply(200); + nock('https://api.github.com/') + .persist() + .post(`/repos/checkdigit/nocomments/issues/${PR_NUMBER_DEFAULT}/comments`) + .reply(200); nock('https://api.github.com/') .persist() - .get('/repos/checkdigit/comments/issues/10/comments') + .get(`/repos/checkdigit/comments/issues/${PR_NUMBER_DEFAULT}/comments`) .reply(200, () => [ { id: 1, @@ -28,14 +59,14 @@ export default function (options?: GithubNock): void { nock('https://api.github.com/') .persist() - .get('/repos/checkdigit/preview/pulls/10/requested_reviewers') + .get(`/repos/checkdigit/preview/pulls/${PR_NUMBER_DEFAULT}/requested_reviewers`) .reply(200, () => ({ users: [], })); nock('https://api.github.com/') .persist() - .get('/repos/checkdigit/previewOutstanding/pulls/10/requested_reviewers') + .get(`/repos/checkdigit/previewOutstanding/pulls/${PR_NUMBER_DEFAULT}/requested_reviewers`) .reply(200, () => ({ users: [ { @@ -49,7 +80,7 @@ export default function (options?: GithubNock): void { nock('https://api.github.com/') .persist() - .get('/repos/checkdigit/preview/pulls/10/reviews') + .get(`/repos/checkdigit/preview/pulls/${PR_NUMBER_DEFAULT}/reviews`) .reply(200, () => [ { id: '1234prReviewPull', @@ -95,7 +126,7 @@ export default function (options?: GithubNock): void { nock('https://api.github.com/') .persist() - .get('/repos/checkdigit/preview/pulls/10') + .get(`/repos/checkdigit/preview/pulls/${PR_NUMBER_DEFAULT}`) .reply(200, () => ({ head: { sha: '1234', @@ -108,37 +139,40 @@ export default function (options?: GithubNock): void { // return label nock('https://api.github.com/') .persist() - .get('/repos/checkdigit/testlabel/pulls/10') + .get(`/repos/checkdigit/testlabel/pulls/${PR_NUMBER_DEFAULT}`) .reply(200, () => ({ labels: [{ name: 'patch' }], })); nock('https://api.github.com/') .persist() - .get('/repos/checkdigit/testlabel/pulls/11') + .get(`/repos/checkdigit/testlabel/pulls/${PR_NUMBER_MINOR}`) + .reply(200, () => ({ + labels: [{ name: 'minor' }], + })); + + nock('https://api.github.com/') + .persist() + .get(`/repos/checkdigit/testlabel/pulls/${PR_NUMBER_MAJOR}`) .reply(200, () => ({ labels: [{ name: 'major' }], })); // return a raw package json file nock('https://api.github.com/') - .persist() .get('/repos/checkdigit/testlabel/contents/package.json?ref=main') - .reply(200, () => { - if (options?.labelPackageVersionMain) { - return `{"version": "${options.labelPackageVersionMain}"}`; - } - return '{"version": "1.0.0"}'; - }); + .reply(200, () => JSON.stringify({ version: options?.labelPackageVersionMain ?? '1.0.0' })); nock('https://api.github.com/') - .persist() .get('/repos/checkdigit/testlabel/contents/package-lock.json?ref=main') - .reply(200, () => '{"version": "1.0.0"}'); + .reply(200, () => JSON.stringify({ version: options?.labelPackageVersionMain ?? '1.0.0' })); // allow delete operations to the two comments that should be deleted nock('https://api.github.com/').persist().delete('/repos/checkdigit/comments/issues/comments/1').reply(200); nock('https://api.github.com/').persist().delete('/repos/checkdigit/comments/issues/comments/3').reply(200); - nock('https://api.github.com/').persist().post('/repos/checkdigit/comments/issues/10/comments').reply(200); + nock('https://api.github.com/') + .persist() + .post(`/repos/checkdigit/comments/issues/${PR_NUMBER_DEFAULT}/comments`) + .reply(200); } diff --git a/src/perform-bundle/deployer.ts b/src/perform-bundle/deployer.ts index 94bf6cb..3c2ac0a 100644 --- a/src/perform-bundle/deployer.ts +++ b/src/perform-bundle/deployer.ts @@ -6,7 +6,7 @@ import debug from 'debug'; const execAsync = promisify(exec); -const log = debug('perform-bundle:deployer'); +const log = debug('github-actions:perform-bundle:deployer'); export default async function (): Promise { log('Install deployer'); diff --git a/src/perform-bundle/index.ts b/src/perform-bundle/index.ts index a9f72e1..2157a4f 100644 --- a/src/perform-bundle/index.ts +++ b/src/perform-bundle/index.ts @@ -1,52 +1,5 @@ // perform-bundle/index.ts -import { readFile } from 'node:fs/promises'; -import debug from 'debug'; -import type { Metafile } from 'esbuild'; +import main from './perform-bundle'; -import { publishCommentAndRemovePrevious } from '../github-api'; -import runDeployer from './deployer'; -import analyze from './analyze'; - -const log = debug('perform-bundle'); - -function bytesToKB(bytes: number) { - return Math.round(bytes / 1024); -} - -async function readMetaDataFile(): Promise { - try { - const rawFile = await readFile('esbuild-lambda/metafile.json', 'utf8'); - return JSON.parse(rawFile) as Metafile; - } catch (error) { - log('Exception thrown attempting to read meta data file: ', JSON.stringify(error)); - throw error; - } -} - -export async function main(): Promise { - log('Action start'); - await runDeployer(); - const metaDataFile = await readMetaDataFile(); - const results = analyze(metaDataFile); - await publishCommentAndRemovePrevious( - `Bundle created - Total size ${bytesToKB(results.totalBytes)} KB - source ${bytesToKB( - results.sourceBytes, - )} KB - modules ${bytesToKB(results.moduleBytes)} KB`, - 'Bundle created ', - ); -} - -main() - .then(() => { - process.stdin.destroy(); - // eslint-disable-next-line unicorn/no-process-exit - process.exit(0); - }) - // eslint-disable-next-line unicorn/prefer-top-level-await - .catch((error) => { - // eslint-disable-next-line no-console - console.log('Action Error - exit 1 - error:', error); - // eslint-disable-next-line unicorn/no-process-exit - process.exit(1); - }); +await main(); diff --git a/src/perform-bundle/perform-bundle.ts b/src/perform-bundle/perform-bundle.ts new file mode 100644 index 0000000..2917ee9 --- /dev/null +++ b/src/perform-bundle/perform-bundle.ts @@ -0,0 +1,42 @@ +// perform-bundle/perform-bundle.ts + +import { readFile } from 'node:fs/promises'; + +import debug from 'debug'; +import type { Metafile } from 'esbuild'; + +import { publishCommentAndRemovePrevious } from '../github-api'; +import runDeployer from './deployer'; +import analyze from './analyze'; + +const log = debug('github-actions:perform-bundle'); + +function bytesToKB(bytes: number) { + return Math.round(bytes / 1024); +} + +async function readMetaDataFile(): Promise { + try { + const rawFile = await readFile('esbuild-lambda/metafile.json', 'utf8'); + return JSON.parse(rawFile) as Metafile; + } catch (error) { + log('Exception thrown attempting to read meta data file: ', JSON.stringify(error)); + throw error; + } +} + +export default async function main(): Promise { + log('Action start'); + + await runDeployer(); + const metaDataFile = await readMetaDataFile(); + const results = analyze(metaDataFile); + await publishCommentAndRemovePrevious( + `Bundle created - Total size ${bytesToKB(results.totalBytes)} KB - source ${bytesToKB( + results.sourceBytes, + )} KB - modules ${bytesToKB(results.moduleBytes)} KB`, + 'Bundle created ', + ); + + log('Action end'); +} diff --git a/src/prepare-beta/index.ts b/src/prepare-beta/index.ts index beead2e..ea90088 100644 --- a/src/prepare-beta/index.ts +++ b/src/prepare-beta/index.ts @@ -1,27 +1,5 @@ // prepare-beta/index.ts -import debug from 'debug'; -import { setOutput } from '@actions/core'; +import main from './prepare-beta'; -import { packageJSONUpdate } from './package'; - -const log = debug('prepare-beta'); -export async function main(): Promise { - log('Action start'); - const packageResult = await packageJSONUpdate(process.cwd()); - setOutput('betaPackage', packageResult); -} - -main() - .then(() => { - process.stdin.destroy(); - // eslint-disable-next-line unicorn/no-process-exit - process.exit(0); - }) - // eslint-disable-next-line unicorn/prefer-top-level-await - .catch((error) => { - // eslint-disable-next-line no-console - console.log('Action Error - exit 1 - error:', error); - // eslint-disable-next-line unicorn/no-process-exit - process.exit(1); - }); +await main(); diff --git a/src/prepare-beta/package.spec.ts b/src/prepare-beta/package.spec.ts index bf486cb..8949d93 100644 --- a/src/prepare-beta/package.spec.ts +++ b/src/prepare-beta/package.spec.ts @@ -1,29 +1,16 @@ // prepare-beta/package.spec.ts import { strict as assert } from 'node:assert'; -import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'; +import { promises as fs } from 'node:fs'; import path from 'node:path'; -import { tmpdir } from 'node:os'; +import os from 'node:os'; -import { afterAll, beforeAll, describe, it } from '@jest/globals'; +import { describe, it } from '@jest/globals'; +import { v4 as uuid } from 'uuid'; import { generatePackageBetaTag, packageJSONUpdate } from './package'; describe('package', () => { - beforeAll(async () => { - await mkdir(path.join(tmpdir(), 'packageUpdate'), { recursive: true }); - await mkdir(path.join(tmpdir(), 'packageUpdate2'), { recursive: true }); - await mkdir(path.join(tmpdir(), 'hasAPI', 'src/api'), { recursive: true }); - await mkdir(path.join(tmpdir(), 'noAPI', 'src/'), { recursive: true }); - }); - - afterAll(async () => { - await rm(path.join(tmpdir(), 'packageUpdate'), { recursive: true }); - await rm(path.join(tmpdir(), 'packageUpdate2'), { recursive: true }); - await rm(path.join(tmpdir(), 'hasAPI'), { recursive: true }); - await rm(path.join(tmpdir(), 'noAPI'), { recursive: true }); - }); - it('generatePackageBetaTag', async () => { process.env['GITHUB_REF'] = '/ref/2406/branch'; process.env['GITHUB_SHA'] = '1234'; @@ -32,17 +19,17 @@ describe('package', () => { }); it('test packageJSON Update and add /src/ to files', async () => { - const filePath = path.join(tmpdir(), 'packageUpdate/package.json'); + const workFolder = path.join(os.tmpdir(), uuid()); + await fs.mkdir(workFolder); + + const filePath = path.join(workFolder, 'package.json'); process.env['GITHUB_REF'] = '/ref/87/branch'; process.env['GITHUB_SHA'] = '12345678ad90'; - await writeFile( - path.join(tmpdir(), 'packageUpdate/package.json'), - JSON.stringify({ name: 'testpackage', version: '1.2.10', files: ['/dist/'] }), - ); - await mkdir(path.join(tmpdir(), 'packageUpdate/src'), { recursive: true }); + await fs.writeFile(filePath, JSON.stringify({ name: 'testpackage', version: '1.2.10', files: ['/dist/'] })); + await fs.mkdir(path.join(workFolder, 'src')); - await packageJSONUpdate(path.join(tmpdir(), 'packageUpdate')); - const rawUpdatedFile = await readFile(filePath, 'utf8'); + await packageJSONUpdate(workFolder); + const rawUpdatedFile = await fs.readFile(filePath, 'utf8'); assert.ok(JSON.parse(rawUpdatedFile).version === '1.2.10-PR.87-ad90'); assert.deepEqual(JSON.parse(rawUpdatedFile).files, ['/dist/']); }); diff --git a/src/prepare-beta/package.ts b/src/prepare-beta/package.ts index 107d696..ffe30ab 100644 --- a/src/prepare-beta/package.ts +++ b/src/prepare-beta/package.ts @@ -7,7 +7,7 @@ import debug from 'debug'; import { getPRNumber } from '../github-api'; -const log = debug('publish-beta:package'); +const log = debug('github-actions:publish-beta:package'); interface PackageJSON { name: string; diff --git a/src/prepare-beta/prepare-beta.ts b/src/prepare-beta/prepare-beta.ts new file mode 100644 index 0000000..d147ead --- /dev/null +++ b/src/prepare-beta/prepare-beta.ts @@ -0,0 +1,17 @@ +// prepare-beta/prepare-beta.ts + +import debug from 'debug'; +import { setOutput } from '@actions/core'; + +import { packageJSONUpdate } from './package'; + +const log = debug('github-actions:prepare-beta'); + +export default async function (): Promise { + log('Action start'); + + const packageResult = await packageJSONUpdate(process.cwd()); + setOutput('betaPackage', packageResult); + + log('Action end'); +} diff --git a/src/publish-beta/compile.ts b/src/publish-beta/compile.ts index f9a86dc..ee668d5 100644 --- a/src/publish-beta/compile.ts +++ b/src/publish-beta/compile.ts @@ -4,7 +4,7 @@ import childProcess from 'node:child_process'; import util from 'node:util'; import debug from 'debug'; -const log = debug('publish-beta:compile'); +const log = debug('github-actions:publish-beta:compile'); const exec = util.promisify(childProcess.exec); export default async function (directory: string): Promise { diff --git a/src/publish-beta/files.spec.ts b/src/publish-beta/files.spec.ts index eb4bc44..2f0e72f 100644 --- a/src/publish-beta/files.spec.ts +++ b/src/publish-beta/files.spec.ts @@ -1,70 +1,61 @@ // publish-beta/files.spec.ts import { strict as assert } from 'node:assert'; -import { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises'; +import { promises as fs } from 'node:fs'; import path from 'node:path'; -import { tmpdir } from 'node:os'; +import os from 'node:os'; -import { afterAll, beforeAll, describe, it } from '@jest/globals'; +import { describe, it } from '@jest/globals'; import { v4 as uuid } from 'uuid'; import copyNonTSFiles, { removeTestFilesFromSource } from './files'; describe('copy', () => { - beforeAll(async () => { - await mkdir(path.join(tmpdir(), 'testcopy'), { recursive: true }); - await mkdir(path.join(tmpdir(), 'testsrc', 'src'), { recursive: true }); - }); - - afterAll(async () => { - await rm(path.join(tmpdir(), 'testcopy'), { recursive: true }); - await rm(path.join(tmpdir(), 'testsrc'), { recursive: true }); - }); - it('test recursive filtering', async () => { - const rootDirectory = path.join(tmpdir(), 'testcopy', uuid()); - await mkdir(rootDirectory); + const rootDirectory = path.join(os.tmpdir(), uuid()); + await fs.mkdir(rootDirectory); const sourceDirectory = path.join(rootDirectory, 'src'); const destinationDirectory = path.join(rootDirectory, 'dist'); - await mkdir(destinationDirectory); + await fs.mkdir(destinationDirectory); const sourceV1 = path.join(sourceDirectory, 'api', 'v1'); const sourceV2 = path.join(sourceDirectory, 'api', 'v2'); - await mkdir(sourceV1, { recursive: true }); - await mkdir(sourceV2, { recursive: true }); + await fs.mkdir(sourceV1, { recursive: true }); + await fs.mkdir(sourceV2, { recursive: true }); - await writeFile(path.join(sourceV1, 'test.ts'), 'test'); - await writeFile(path.join(sourceV2, 'test2.ts'), 'test'); - await writeFile(path.join(sourceV1, 'actiontestv1.yml'), 'actiontestv1.yml'); - await writeFile(path.join(sourceV2, 'actiontestv2.yml'), 'actiontestv2.yml'); - await writeFile(path.join(sourceDirectory, 'testfile.json'), 'testfile.json'); + await fs.writeFile(path.join(sourceV1, 'test.ts'), 'test'); + await fs.writeFile(path.join(sourceV2, 'test2.ts'), 'test'); + await fs.writeFile(path.join(sourceV1, 'actiontestv1.yml'), 'actiontestv1.yml'); + await fs.writeFile(path.join(sourceV2, 'actiontestv2.yml'), 'actiontestv2.yml'); + await fs.writeFile(path.join(sourceDirectory, 'testfile.json'), 'testfile.json'); await copyNonTSFiles(sourceDirectory, destinationDirectory); - const file1 = await readFile(path.join(destinationDirectory, 'testfile.json'), 'utf8'); + const file1 = await fs.readFile(path.join(destinationDirectory, 'testfile.json'), 'utf8'); assert.equal(file1, 'testfile.json'); - const file2 = await readFile(path.join(destinationDirectory, 'api/v1/actiontestv1.yml'), 'utf8'); + const file2 = await fs.readFile(path.join(destinationDirectory, 'api/v1/actiontestv1.yml'), 'utf8'); assert.equal(file2, 'actiontestv1.yml'); - const file3 = await readFile(path.join(destinationDirectory, 'api/v2/actiontestv2.yml'), 'utf8'); + const file3 = await fs.readFile(path.join(destinationDirectory, 'api/v2/actiontestv2.yml'), 'utf8'); assert.equal(file3, 'actiontestv2.yml'); - await assert.rejects(readFile(path.join(destinationDirectory, 'api/v1/test.ts'), 'utf8')); - await assert.rejects(readFile(path.join(destinationDirectory, 'api/v2/test2.ts'), 'utf8')); + await assert.rejects(fs.readFile(path.join(destinationDirectory, 'api/v1/test.ts'), 'utf8')); + await assert.rejects(fs.readFile(path.join(destinationDirectory, 'api/v2/test2.ts'), 'utf8')); }); it('test removal of all files except .ts (excluding .spec.ts and .test.ts)', async () => { - const sourceDirectory = path.join(tmpdir(), 'testsrc', 'src'); + const sourceDirectory = path.join(os.tmpdir(), uuid(), 'src'); + await fs.mkdir(sourceDirectory, { recursive: true }); await Promise.all([ - writeFile(path.join(sourceDirectory, 'test.ts'), 'test'), - writeFile(path.join(sourceDirectory, 'test.spec.ts'), 'test'), - writeFile(path.join(sourceDirectory, 'test.spec.mts'), 'testmjs'), - writeFile(path.join(sourceDirectory, 'favorite.test.mts'), 'testmjs'), - writeFile(path.join(sourceDirectory, 'swagger.yml'), 'test'), + fs.writeFile(path.join(sourceDirectory, 'test.ts'), 'test'), + fs.writeFile(path.join(sourceDirectory, 'test.spec.ts'), 'test'), + fs.writeFile(path.join(sourceDirectory, 'test.spec.mts'), 'testmjs'), + fs.writeFile(path.join(sourceDirectory, 'favorite.test.mts'), 'testmjs'), + fs.writeFile(path.join(sourceDirectory, 'swagger.yml'), 'test'), ]); await removeTestFilesFromSource(sourceDirectory); - const files = await readdir(sourceDirectory, { withFileTypes: true }); + const files = await fs.readdir(sourceDirectory, { withFileTypes: true }); assert.equal(files.length, 2); assert.ok(files.some((item) => item.name === 'test.ts')); assert.ok(files.some((item) => item.name === 'swagger.yml')); diff --git a/src/publish-beta/index.ts b/src/publish-beta/index.ts index 9ae92d5..93bd600 100644 --- a/src/publish-beta/index.ts +++ b/src/publish-beta/index.ts @@ -1,38 +1,5 @@ // publish-beta/index.ts -import path from 'node:path'; -import debug from 'debug'; +import main from './publish-beta'; -import { publishCommentAndRemovePrevious } from '../github-api'; -import { packageJSONUpdate } from './package'; -import copyNonTSFiles from './files'; -import compile from './compile'; -import publish from './publish'; - -const log = debug('publish-beta'); -export async function main(): Promise { - log('Action start'); - - await compile(process.cwd()); - const packageNameAndBetaVersion = await packageJSONUpdate(process.cwd()); - await copyNonTSFiles(path.join(process.cwd(), 'src'), path.join(process.cwd(), 'dist')); - await publish(process.cwd()); - await publishCommentAndRemovePrevious( - `Beta Published - Install Command: \`npm install ${packageNameAndBetaVersion}\` `.replaceAll('"', ''), - 'Beta Published - Install Command: ', - ); -} - -main() - .then(() => { - process.stdin.destroy(); - // eslint-disable-next-line unicorn/no-process-exit - process.exit(0); - }) - // eslint-disable-next-line unicorn/prefer-top-level-await - .catch((error) => { - // eslint-disable-next-line no-console - console.log('Action Error - exit 1 - error:', error); - // eslint-disable-next-line unicorn/no-process-exit - process.exit(1); - }); +await main(); diff --git a/src/publish-beta/package.spec.ts b/src/publish-beta/package.spec.ts index 2fc619e..2ae1172 100644 --- a/src/publish-beta/package.spec.ts +++ b/src/publish-beta/package.spec.ts @@ -1,29 +1,16 @@ // publish-beta/package.spec.ts import { strict as assert } from 'node:assert'; -import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'; +import { mkdir, readFile, writeFile } from 'node:fs/promises'; import path from 'node:path'; import { tmpdir } from 'node:os'; -import { afterAll, beforeAll, describe, it } from '@jest/globals'; +import { describe, it } from '@jest/globals'; +import { v4 as uuid } from 'uuid'; import { generatePackageBetaTag, packageJSONUpdate } from './package'; describe('package', () => { - beforeAll(async () => { - await mkdir(path.join(tmpdir(), 'packageUpdate'), { recursive: true }); - await mkdir(path.join(tmpdir(), 'packageUpdate2'), { recursive: true }); - await mkdir(path.join(tmpdir(), 'hasAPI', 'src/api'), { recursive: true }); - await mkdir(path.join(tmpdir(), 'noAPI', 'src/'), { recursive: true }); - }); - - afterAll(async () => { - await rm(path.join(tmpdir(), 'packageUpdate'), { recursive: true }); - await rm(path.join(tmpdir(), 'packageUpdate2'), { recursive: true }); - await rm(path.join(tmpdir(), 'hasAPI'), { recursive: true }); - await rm(path.join(tmpdir(), 'noAPI'), { recursive: true }); - }); - it('generatePackageBetaTag', async () => { process.env['GITHUB_REF'] = '/ref/2406/branch'; process.env['GITHUB_SHA'] = '1234'; @@ -32,30 +19,27 @@ describe('package', () => { }); it('test packageJSON Update and add /src/ to files', async () => { - const filePath = path.join(tmpdir(), 'packageUpdate/package.json'); + const workFolder = path.join(tmpdir(), uuid()); + await mkdir(workFolder); + + const filePath = path.join(workFolder, 'package.json'); process.env['GITHUB_REF'] = '/ref/87/branch'; process.env['GITHUB_SHA'] = '12345678ad90'; - await writeFile( - path.join(tmpdir(), 'packageUpdate/package.json'), - JSON.stringify({ name: 'testpackage', version: '1.2.10', files: ['/dist/'] }), - ); - await mkdir(path.join(tmpdir(), 'packageUpdate/src'), { recursive: true }); + await writeFile(filePath, JSON.stringify({ name: 'testpackage', version: '1.2.10', files: ['/dist/'] })); + await mkdir(path.join(workFolder, 'src')); - await packageJSONUpdate(path.join(tmpdir(), 'packageUpdate')); + await packageJSONUpdate(workFolder); const rawUpdatedFile = await readFile(filePath, 'utf8'); assert.ok(JSON.parse(rawUpdatedFile).version === '1.2.10-PR.87-ad90'); assert.deepEqual(JSON.parse(rawUpdatedFile).files.sort(), ['/dist/'].sort()); }); it('Test with files property missing', async () => { + const workFolder = path.join(tmpdir(), uuid()); + await mkdir(workFolder); + process.env['GITHUB_REF'] = '/ref/87/branch'; - await writeFile( - path.join(tmpdir(), 'packageUpdate2/package.json'), - JSON.stringify({ name: 'testpackage', version: '1.2.10' }), - ); - await assert.rejects( - packageJSONUpdate(path.join(tmpdir(), 'packageUpdate2')), - '[Error: package.json does not have a files: [] property]', - ); + await writeFile(path.join(workFolder, 'package.json'), JSON.stringify({ name: 'testpackage', version: '1.2.10' })); + await assert.rejects(packageJSONUpdate(workFolder), '[Error: package.json does not have a files: [] property]'); }); }); diff --git a/src/publish-beta/package.ts b/src/publish-beta/package.ts index d79c446..b1162d8 100644 --- a/src/publish-beta/package.ts +++ b/src/publish-beta/package.ts @@ -7,7 +7,7 @@ import debug from 'debug'; import { getPRNumber } from '../github-api'; import { removeTestFilesFromSource } from './files'; -const log = debug('publish-beta:package'); +const log = debug('github-actions:publish-beta:package'); interface PackageJSON { name: string; diff --git a/src/publish-beta/publish-beta.ts b/src/publish-beta/publish-beta.ts new file mode 100644 index 0000000..5179c26 --- /dev/null +++ b/src/publish-beta/publish-beta.ts @@ -0,0 +1,28 @@ +// publish-beta/publish-beta.ts + +import path from 'node:path'; + +import debug from 'debug'; + +import { publishCommentAndRemovePrevious } from '../github-api'; +import { packageJSONUpdate } from './package'; +import copyNonTSFiles from './files'; +import compile from './compile'; +import publish from './publish'; + +const log = debug('github-actions:publish-beta'); + +export default async function (): Promise { + log('Action start'); + + await compile(process.cwd()); + const packageNameAndBetaVersion = await packageJSONUpdate(process.cwd()); + await copyNonTSFiles(path.join(process.cwd(), 'src'), path.join(process.cwd(), 'dist')); + await publish(process.cwd()); + await publishCommentAndRemovePrevious( + `Beta Published - Install Command: \`npm install ${packageNameAndBetaVersion}\` `.replaceAll('"', ''), + 'Beta Published - Install Command: ', + ); + + log('Action end'); +} diff --git a/src/publish-beta/publish.spec.ts b/src/publish-beta/publish.spec.ts index 6f92de9..899e9a2 100644 --- a/src/publish-beta/publish.spec.ts +++ b/src/publish-beta/publish.spec.ts @@ -1,41 +1,33 @@ // publish-beta/publish.spec.ts import { strict as assert } from 'node:assert'; -import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'; +import { promises as fs } from 'node:fs'; import path from 'node:path'; -import { tmpdir } from 'node:os'; +import os from 'node:os'; -import { afterAll, beforeAll, describe, it } from '@jest/globals'; +import { describe, it } from '@jest/globals'; import { v4 as uuid } from 'uuid'; import { addNPMRCFile } from './publish'; describe('package', () => { - beforeAll(async () => { - await mkdir(path.join(tmpdir(), 'publishtest'), { recursive: true }); - }); - - afterAll(async () => { - await rm(path.join(tmpdir(), 'publishtest'), { recursive: true }); - }); - it('Test generation of .npmrc file in empty directory', async () => { - const rootdirectory = path.join(tmpdir(), 'publishtest', uuid()); - await mkdir(rootdirectory); + const rootdirectory = path.join(os.tmpdir(), uuid()); + await fs.mkdir(rootdirectory); await addNPMRCFile(rootdirectory); - const npmrcFile = await readFile(path.join(rootdirectory, '.npmrc'), 'utf8'); + const npmrcFile = await fs.readFile(path.join(rootdirectory, '.npmrc'), 'utf8'); // eslint-disable-next-line no-template-curly-in-string assert.equal(npmrcFile, '//registry.npmjs.org/:_authToken=${NPM_TOKEN}'); }); it('Test existing .npmrc file is left in place if it exists', async () => { - const rootdirectory = path.join(tmpdir(), 'publishtest', uuid()); - await mkdir(rootdirectory); - await writeFile(path.join(rootdirectory, '.npmrc'), 'test', 'utf8'); + const rootdirectory = path.join(os.tmpdir(), uuid()); + await fs.mkdir(rootdirectory); + await fs.writeFile(path.join(rootdirectory, '.npmrc'), 'test', 'utf8'); await addNPMRCFile(rootdirectory); - const npmrcFile = await readFile(path.join(rootdirectory, '.npmrc'), 'utf8'); + const npmrcFile = await fs.readFile(path.join(rootdirectory, '.npmrc'), 'utf8'); assert.equal(npmrcFile, 'test'); }); }); diff --git a/src/publish-beta/publish.ts b/src/publish-beta/publish.ts index 7292076..5825c9d 100644 --- a/src/publish-beta/publish.ts +++ b/src/publish-beta/publish.ts @@ -5,7 +5,7 @@ import util from 'node:util'; import fs from 'node:fs/promises'; import debug from 'debug'; -const log = debug('publish-beta:publish'); +const log = debug('github-actions:publish-beta:publish'); const exec = util.promisify(childProcess.exec); @@ -13,9 +13,11 @@ export async function addNPMRCFile(rootProjectDirectory: string): Promise const npmrcPath = `${rootProjectDirectory}/.npmrc`; try { await fs.access(npmrcPath); + log('.npmrc file already exists', npmrcPath); } catch { // eslint-disable-next-line no-template-curly-in-string await fs.writeFile(npmrcPath, '//registry.npmjs.org/:_authToken=${NPM_TOKEN}', 'utf8'); + log('.npmrc file created', npmrcPath); } } diff --git a/src/validate-npm-package/index.ts b/src/validate-npm-package/index.ts new file mode 100644 index 0000000..e6ac12b --- /dev/null +++ b/src/validate-npm-package/index.ts @@ -0,0 +1,5 @@ +// validate-npm-package/index.ts + +import main from './validate-npm-package'; + +await main(); diff --git a/src/validate-npm-package/validate-npm-package.spec.ts b/src/validate-npm-package/validate-npm-package.spec.ts new file mode 100644 index 0000000..4ba94ae --- /dev/null +++ b/src/validate-npm-package/validate-npm-package.spec.ts @@ -0,0 +1,37 @@ +// validate-npm-package/validate-npm-package.spec.ts + +import { strict as assert } from 'node:assert'; + +import { afterEach, describe, it, jest } from '@jest/globals'; +import core from '@actions/core'; + +import verifyNpmPackage from './validate-npm-package'; + +describe('validate-npm-package', () => { + const actionsCoreSpy = jest.spyOn(core, 'getInput'); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('successfully verify good npm package', async () => { + actionsCoreSpy.mockImplementationOnce((name) => { + if (name === 'betaPackage') { + return '@checkdigit/approval@2.0.3'; + } + return ''; + }); + + await verifyNpmPackage(); + }, 300_000); + + it('bad npm package results in error', async () => { + actionsCoreSpy.mockImplementationOnce((name) => { + if (name === 'betaPackage') { + return '@checkdigit/approval@2.0.0-PR.196-b041'; + } + return ''; + }); + await assert.rejects(() => verifyNpmPackage()); + }, 300_000); +}); diff --git a/src/validate-npm-package/validate-npm-package.ts b/src/validate-npm-package/validate-npm-package.ts new file mode 100644 index 0000000..e66502a --- /dev/null +++ b/src/validate-npm-package/validate-npm-package.ts @@ -0,0 +1,107 @@ +// validate-npm-package/validate-npm-package.ts + +import os from 'node:os'; +import path from 'node:path'; +import { promises as fs } from 'node:fs'; +import childProcess from 'node:child_process'; +import util from 'node:util'; + +import core from '@actions/core'; +import debug from 'debug'; +import { v4 as uuid } from 'uuid'; + +import { addNPMRCFile } from '../publish-beta/publish'; + +interface PackageJson { + name: string; + version: string; + devDependencies?: Record; +} + +const exec = util.promisify(childProcess.exec); +const log = debug('github-actions:validate-npm-package'); + +async function retrievePackageJson(workFolder: string, packageNameAndBetaVersion: string): Promise { + const execResult = await exec(`npm view ${packageNameAndBetaVersion} --json`, { cwd: workFolder }); + log('retrievePackageJson - execResult', execResult); + + const packageJson = JSON.parse(execResult.stdout) as PackageJson; + log('retrievePackageJson - name', packageJson.name); + log('retrievePackageJson - version', packageJson.version); + return packageJson; +} + +// create a minimal project with the package as a dependency +async function generateProject(workFolder: string, packageJson: PackageJson): Promise { + // create index.ts to import the dependency + await fs.mkdir(`${workFolder}/src`); + await fs.writeFile(path.join(workFolder, 'src', 'index.ts'), `import '${packageJson.name}';\n`); + + // create package.json with the dependency + const projectPackageJson = { + name: 'test', + version: '0.0.1', + description: 'test project for validating a target library or service npm package', + engines: { + node: '>=20.11', + }, + type: 'module', + dependencies: { + [packageJson.name]: packageJson.version, + }, + devDependencies: { + // including the devDependencies from the target package is necessary for resolving the typing references including but not limited to the types from its service dependencies + ...packageJson.devDependencies, + '@checkdigit/typescript-config': '^7.0.1', + }, + scripts: { + compile: 'tsc --noEmit', + }, + }; + await fs.writeFile(`${workFolder}/package.json`, JSON.stringify(projectPackageJson, null, 2)); + + // create tsconfig.json + const tsconfigJson = { + extends: '@checkdigit/typescript-config', + }; + await fs.writeFile(`${workFolder}/tsconfig.json`, JSON.stringify(tsconfigJson, null, 2)); +} + +async function installDependencies(workFolder: string): Promise { + const fullCommandLine = `npm i --ignore-scripts`; + log('installNpmDependencies - fullCommandLine', fullCommandLine); + + const execResult = await exec(fullCommandLine, { cwd: workFolder }); + log('installNpmDependencies - execResult', execResult); +} + +async function verifyDefaultImport(workFolder: string): Promise { + const fullCommandLine = `npm run compile`; + log('verifyImportEntryPoints - fullCommandLine', fullCommandLine); + + const execResult = await exec(fullCommandLine, { cwd: workFolder }); + log('verifyImportEntryPoints - execResult', execResult); +} + +export default async function (): Promise { + log('Action start'); + + const packageNameAndBetaVersion = core.getInput('betaPackage'); + log('packageNameAndBetaVersion', packageNameAndBetaVersion); + + const workFolder = path.join(os.tmpdir(), uuid()); + await fs.mkdir(workFolder); + log('temporary work folder created', workFolder); + + await addNPMRCFile(workFolder); + + const packageJson = await retrievePackageJson(workFolder, packageNameAndBetaVersion); + + await generateProject(workFolder, packageJson); + + await installDependencies(workFolder); + + await verifyDefaultImport(workFolder); + + log('Action end'); +} diff --git a/validate-npm-package/action.yml b/validate-npm-package/action.yml new file mode 100644 index 0000000..5c84bc1 --- /dev/null +++ b/validate-npm-package/action.yml @@ -0,0 +1,10 @@ +name: Validate NPM Package +description: Make sure the NPM package is valid by importing its exports +inputs: + betaPackage: + description: published beta package + required: true +runs: + using: node20 + pre: '../setup.mjs' + main: ../dist-mjs/validate-npm-package/index.mjs