From eaec6a61595d8680aa7600abc6115760c436ba4b Mon Sep 17 00:00:00 2001 From: James Austen Date: Thu, 30 Jul 2026 08:51:32 +0100 Subject: [PATCH 1/3] ci: fix broken install, bump to Node 22/24, publish via OIDC pnpm-workspace.yaml (added in 9ad1735) has no packages field, which pnpm 8 rejects outright - every CI run died at install, never reaching tests. - pin pnpm via packageManager (11.5.1); lockfile is v9, needs pnpm >=9 - test matrix 20 -> 22/24; checkout v7, setup-node v7, action-setup v6 - publish with npm trusted publishing (OIDC), drop NPM_TOKEN + .npmrc.ci - add typecheck script; scope tsconfig include to src to fix TS6059 - point types/exports at dist (types/ is never emitted) Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/cicd-next.yml | 2 +- .github/workflows/cicd.yml | 2 +- .github/workflows/release-drafter.yml | 2 +- .github/workflows/service_publish.yml | 46 ++++++------------ .github/workflows/service_tests.yml | 24 +++++----- .github/workflows/tests.yml | 5 +- .gitignore | 1 + .npmrc.ci | 5 -- package.json | 16 ++++--- pnpm-lock.yaml | 69 ++++++++++++++++++++------- tsconfig.json | 5 +- 11 files changed, 101 insertions(+), 76 deletions(-) delete mode 100644 .npmrc.ci diff --git a/.github/workflows/cicd-next.yml b/.github/workflows/cicd-next.yml index 93a06d3..8e3a04d 100644 --- a/.github/workflows/cicd-next.yml +++ b/.github/workflows/cicd-next.yml @@ -10,7 +10,7 @@ jobs: tests: strategy: matrix: - node: [20] + node: [22, 24] uses: ./.github/workflows/service_tests.yml with: node-version: ${{ matrix.node }} diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index e72802f..9426501 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -10,7 +10,7 @@ jobs: tests: strategy: matrix: - node: [20] + node: [22, 24] uses: ./.github/workflows/service_tests.yml with: node-version: ${{ matrix.node }} diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 541fe27..8d228ea 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -27,7 +27,7 @@ jobs: runs-on: ubuntu-latest steps: # Drafts your next Release notes as Pull Requests are merged into "main" - - uses: release-drafter/release-drafter@v5 + - uses: release-drafter/release-drafter@v6 # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml # with: # config-name: my-config.yml diff --git a/.github/workflows/service_publish.yml b/.github/workflows/service_publish.yml index 921af1b..cacf018 100644 --- a/.github/workflows/service_publish.yml +++ b/.github/workflows/service_publish.yml @@ -1,4 +1,4 @@ -name: CI/CD (@next) +name: Publish on: workflow_call: @@ -13,55 +13,39 @@ jobs: name: Publish to NPM runs-on: ubuntu-latest + permissions: + # Required to mint the OIDC token npm exchanges for publish credentials. + # The package is configured as a Trusted Publisher on npmjs.org, pinned to + # ge-tracker/ge-tracker-api + this workflow file, so no NPM_TOKEN is needed. + id-token: write + contents: read + steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - - uses: pnpm/action-setup@v2 - with: - version: 8 + # Version comes from the "packageManager" field in package.json. + # Trusted publishing requires pnpm >= 11. + - uses: pnpm/action-setup@v6 - name: Setup Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v7 with: - node-version: 20 + node-version: 24 cache: 'pnpm' # We may have to hoist dependencies to fix TS2742 # https://github.com/microsoft/TypeScript/issues/47663#issuecomment-1519138189 # https://github.com/microsoft/TypeScript/issues/47663 - name: Install dependencies - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} run: | - mv .npmrc.ci .npmrc - echo 'node-linker=hoisted' >> .npmrc + echo 'node-linker=hoisted' > .npmrc pnpm install --frozen-lockfile - name: Publish to NPM (@${{ inputs.tag }}) if: inputs.tag == 'latest' - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} run: pnpm publish --no-git-checks - name: Publish tagged to NPM (@${{ inputs.tag }}) if: inputs.tag != 'latest' - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} run: pnpm publish --tag ${{ inputs.tag }} --no-git-checks -# - name: Publish to NPM (@latest) -# # Stop v5 releases being published to @latest -# if: ${{ !startsWith(github.ref_name, 'v5.') }} -# env: -# NPM_TOKEN: ${{ secrets.NPM_TOKEN }} -# run: | -# mv .npmrc.ci .npmrc -# npm publish -# -# - name: Publish to NPM (@v5) -# if: startsWith(github.ref_name, 'v5.') -# env: -# NPM_TOKEN: ${{ secrets.NPM_TOKEN }} -# run: | -# mv .npmrc.ci .npmrc -# npm publish --tag v5 diff --git a/.github/workflows/service_tests.yml b/.github/workflows/service_tests.yml index 394ad56..f69ef97 100644 --- a/.github/workflows/service_tests.yml +++ b/.github/workflows/service_tests.yml @@ -7,7 +7,10 @@ on: required: false type: number description: 'Node.js version to use for the test run' - default: 20 + default: 24 + +permissions: + contents: read jobs: tests: @@ -18,27 +21,24 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - - uses: pnpm/action-setup@v2 - with: - version: 8 + # Version comes from the "packageManager" field in package.json + - uses: pnpm/action-setup@v6 - name: Setup Node.js ${{ inputs.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v7 with: node-version: ${{ inputs.node-version }} cache: 'pnpm' - name: Install dependencies run: | - mv .npmrc.ci .npmrc - echo 'node-linker=hoisted' >> .npmrc + echo 'node-linker=hoisted' > .npmrc pnpm install --frozen-lockfile -# - name: Run ESLint -# run: pnpm run lint + - name: Typecheck + run: pnpm run typecheck - name: Run Tests (Node v${{ inputs.node-version }}) - run: | - pnpm run test --ci + run: pnpm run test --ci diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f9d2118..e33e3c8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,11 +19,14 @@ on: - '**.tsx' - '**.json' +permissions: + contents: read + jobs: tests: strategy: matrix: - node: [20] + node: [22, 24] uses: ./.github/workflows/service_tests.yml with: node-version: ${{ matrix.node }} diff --git a/.gitignore b/.gitignore index c4fc9e5..3de54c6 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ yarn-error.log* .env .npm test-export.ts +.npmrc diff --git a/.npmrc.ci b/.npmrc.ci deleted file mode 100644 index c819fdb..0000000 --- a/.npmrc.ci +++ /dev/null @@ -1,5 +0,0 @@ -registry=https://registry.yarnpkg.com/ -@getracker:registry=https://registry.yarnpkg.com/ -//registry.npmjs.org/:_authToken=${NPM_TOKEN} -//registry.yarnpkg.com/:_authToken=${NPM_TOKEN} -always-auth=true diff --git a/package.json b/package.json index f787fd5..03ae2cf 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,9 @@ }, "scripts": { "test": "jest --passWithNoTests", + "typecheck": "tsc --noEmit", "build": "pnpm run clean && tsup", - "clean": "rm -rf dist types", + "clean": "rm -rf dist", "dev": "tsup src/index.ts --watch --dts", "prettier": "prettier --write .", "prepublishOnly": "pnpm run build", @@ -20,10 +21,13 @@ }, "files": [ "dist", - "types", "jest" ], "type": "commonjs", + "packageManager": "pnpm@11.5.1", + "engines": { + "node": ">=22" + }, "sideEffects": false, "source": "./src/index.ts", "exports": { @@ -34,12 +38,12 @@ "import": "./dist/index.mjs", "require": "./dist/index.js" }, - "./types": "./types/index.d.ts", + "./types": "./dist/index.d.ts", "./jest/jest-mock": "./jest/jest-mock.ts" }, "main": "./dist/index.js", "module": "./dist/index.mjs", - "types": "./types/index.d.ts", + "types": "./dist/index.d.ts", "dependencies": { "axios": "^1.6.2", "dayjs": "^1.11.10", @@ -50,9 +54,9 @@ "@babel/core": "^7.23.3", "@babel/preset-env": "^7.23.3", "@babel/preset-typescript": "^7.23.3", - "@tsconfig/node20": "^20.1.2", + "@tsconfig/node22": "^22.0.5", "@types/jest": "^29.5.10", - "@types/node": "^20.10.0", + "@types/node": "^22.20.1", "husky": "^8.0.3", "jest": "^29.7.0", "lint-staged": "^13.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3a402d2..af893d7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,21 +30,21 @@ importers: '@babel/preset-typescript': specifier: ^7.23.3 version: 7.29.7(@babel/core@7.29.7) - '@tsconfig/node20': - specifier: ^20.1.2 - version: 20.1.9 + '@tsconfig/node22': + specifier: ^22.0.5 + version: 22.0.5 '@types/jest': specifier: ^29.5.10 version: 29.5.14 '@types/node': - specifier: ^20.10.0 - version: 20.19.43 + specifier: ^22.20.1 + version: 22.20.1 husky: specifier: ^8.0.3 version: 8.0.3 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.19.43) + version: 29.7.0(@types/node@22.20.1) lint-staged: specifier: ^13.3.0 version: 13.3.0 @@ -1057,8 +1057,8 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - '@tsconfig/node20@20.1.9': - resolution: {integrity: sha512-IjlTv1RsvnPtUcjTqtVsZExKVq+KQx4g5pCP5tI7rAs6Xesl2qFwSz/tPDBC4JajkL/MlezBu3gPUwqRHl+RIg==} + '@tsconfig/node22@22.0.5': + resolution: {integrity: sha512-hLf2ld+sYN/BtOJjHUWOk568dvjFQkHnLNa6zce25GIH+vxKfvTgm3qpaH6ToF5tu/NN0IH66s+Bb5wElHrLcw==} '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -1093,6 +1093,9 @@ packages: '@types/node@20.19.43': resolution: {integrity: sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==} + '@types/node@22.20.1': + resolution: {integrity: sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==} + '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} @@ -3582,7 +3585,7 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 - '@tsconfig/node20@20.1.9': {} + '@tsconfig/node22@22.0.5': {} '@types/babel__core@7.20.5': dependencies: @@ -3630,6 +3633,10 @@ snapshots: dependencies: undici-types: 6.21.0 + '@types/node@22.20.1': + dependencies: + undici-types: 6.21.0 + '@types/stack-utils@2.0.3': {} '@types/yargs-parser@21.0.3': {} @@ -3887,13 +3894,13 @@ snapshots: dependencies: browserslist: 4.28.7 - create-jest@29.7.0(@types/node@20.19.43): + create-jest@29.7.0(@types/node@22.20.1): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.19.43) + jest-config: 29.7.0(@types/node@22.20.1) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -4285,16 +4292,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@20.19.43): + jest-cli@29.7.0(@types/node@22.20.1): dependencies: '@jest/core': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.19.43) + create-jest: 29.7.0(@types/node@22.20.1) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@20.19.43) + jest-config: 29.7.0(@types/node@22.20.1) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.3 @@ -4334,6 +4341,36 @@ snapshots: - babel-plugin-macros - supports-color + jest-config@29.7.0(@types/node@22.20.1): + dependencies: + '@babel/core': 7.29.7 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.29.7) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 22.20.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + jest-diff@29.7.0: dependencies: chalk: 4.1.2 @@ -4549,12 +4586,12 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@20.19.43): + jest@29.7.0(@types/node@22.20.1): dependencies: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@20.19.43) + jest-cli: 29.7.0(@types/node@22.20.1) transitivePeerDependencies: - '@types/node' - babel-plugin-macros diff --git a/tsconfig.json b/tsconfig.json index 811cba1..96df9f4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "@tsconfig/node20/tsconfig.json", + "extends": "@tsconfig/node22/tsconfig.json", "compilerOptions": { "module": "es2022", "moduleResolution": "bundler", @@ -11,5 +11,6 @@ "sourceMap": true, "typeRoots": ["./node_modules/@types"], "types": ["node", "jest"] - } + }, + "include": ["src"] } From 5bd6a7dcd15012494c1e2e89a46d5652421a99bc Mon Sep 17 00:00:00 2001 From: James Austen Date: Thu, 30 Jul 2026 08:53:46 +0100 Subject: [PATCH 2/3] ci: grant id-token on caller jobs, drop dead secrets: inherit Reusable workflows cannot elevate permissions, and id-token is never granted by default - without this the OIDC publish would fail on the first release. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/cicd-next.yml | 7 +++++-- .github/workflows/cicd.yml | 7 +++++-- .github/workflows/tests.yml | 1 - 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cicd-next.yml b/.github/workflows/cicd-next.yml index 8e3a04d..bd304c2 100644 --- a/.github/workflows/cicd-next.yml +++ b/.github/workflows/cicd-next.yml @@ -14,12 +14,15 @@ jobs: uses: ./.github/workflows/service_tests.yml with: node-version: ${{ matrix.node }} - secrets: inherit publish: needs: [tests] name: Publish @next + # Reusable workflows can only maintain or reduce the caller's permissions, + # never elevate them - so id-token must be granted here for OIDC to work. + permissions: + id-token: write + contents: read uses: ./.github/workflows/service_publish.yml with: tag: next - secrets: inherit diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 9426501..09df2f9 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -14,12 +14,15 @@ jobs: uses: ./.github/workflows/service_tests.yml with: node-version: ${{ matrix.node }} - secrets: inherit publish: needs: [tests] name: Publish @latest + # Reusable workflows can only maintain or reduce the caller's permissions, + # never elevate them - so id-token must be granted here for OIDC to work. + permissions: + id-token: write + contents: read uses: ./.github/workflows/service_publish.yml with: tag: latest - secrets: inherit diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e33e3c8..3e4b48b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,4 +30,3 @@ jobs: uses: ./.github/workflows/service_tests.yml with: node-version: ${{ matrix.node }} - secrets: inherit From 2ce058eb7b4b00833d3a5e52b8c5d3715ed5a8fa Mon Sep 17 00:00:00 2001 From: James Austen Date: Thu, 30 Jul 2026 08:54:58 +0100 Subject: [PATCH 3/3] ci: run tests on workflow and lockfile changes Path filters excluded .github and pnpm-*.yaml, so the workspace-config break landed on main without CI ever running. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/tests.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3e4b48b..26525c5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,12 +12,19 @@ on: - '**.ts' - '**.tsx' - '**.json' + # Changes to CI or install config must validate themselves + - '.github/workflows/**' + - 'pnpm-lock.yaml' + - 'pnpm-workspace.yaml' pull_request: paths: - '**.js' - '**.ts' - '**.tsx' - '**.json' + - '.github/workflows/**' + - 'pnpm-lock.yaml' + - 'pnpm-workspace.yaml' permissions: contents: read