diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 000a950..a4dfb43 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -12,7 +12,7 @@ jobs: - name: Install pnpm uses: pnpm/action-setup@v2 with: - version: 8.6.7 + version: 8.7.5 - name: Run Tests run: | diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 4d9eb96..c2e6882 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -1,22 +1,20 @@ version: 0.1 cli: - version: 1.12.0 + version: 1.19.0 plugins: sources: - id: trunk - ref: v0.0.21 + ref: v1.4.2 uri: https://github.com/trunk-io/plugins - id: configs uri: https://github.com/trunk-io/configs - ref: v0.0.3 + ref: v1.0.2 lint: disabled: - eslint enabled: - - git-diff-check - - markdownlint@0.35.0 - - prettier@3.0.0 - - trufflehog@3.43.0 + - prettier@3.2.5 + - trufflehog@3.67.5 ignore: - linters: [ALL] paths: @@ -24,4 +22,3 @@ lint: runtimes: enabled: - node@18.12.1 - - python@3.10.8 diff --git a/action.yaml b/action.yaml index bcd2b66..4f064d0 100644 --- a/action.yaml +++ b/action.yaml @@ -6,7 +6,7 @@ inputs: trunk-token: description: Repo or Organization API token used for authentication. Can be found at app.trunk.io. - required: true + required: false # Required for any workflow not coming from a forked PR. target-branch: description: The branch that the Merge Queue merges PRs into. If unspecified, defaults to the repository's @@ -94,6 +94,8 @@ runs: ACTOR: ${{ github.actor }} API_TOKEN: ${{ inputs.trunk-token }} REPOSITORY: ${{ github.repository }} + RUN_ID: ${{ github.run_id }} + IS_FORK: ${{ github.event.pull_request.head.repo.fork }} TARGET_BRANCH: ${{ steps.prerequisites.outputs.merge_instance_branch }} PR_NUMBER: ${{ github.event.pull_request.number }} PR_SHA: ${{ steps.prerequisites.outputs.pr_branch_head_sha }} diff --git a/package.json b/package.json index bbe7306..209b3d6 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,8 @@ "@types/node": "20.4.2" }, "devDependencies": { + "@jest/globals": "^29.7.0", "@types/express": "4.17.17", - "@types/jest": "29.5.3", "@types/lodash": "4.14.195", "express": "4.18.2", "http-status-codes": "2.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8b116db..cdbdf86 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,12 +10,12 @@ dependencies: version: 20.4.2 devDependencies: + '@jest/globals': + specifier: ^29.7.0 + version: 29.7.0 '@types/express': specifier: 4.17.17 version: 4.17.17 - '@types/jest': - specifier: 29.5.3 - version: 29.5.3 '@types/lodash': specifier: 4.14.195 version: 4.14.195 @@ -459,6 +459,16 @@ packages: jest-mock: 29.6.1 dev: true + /@jest/environment@29.7.0: + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.4.2 + jest-mock: 29.7.0 + dev: true + /@jest/expect-utils@29.6.1: resolution: {integrity: sha512-o319vIf5pEMx0LmzSxxkYYxo4wrRLKHq9dP1yJU7FoPTB0LfAKSz8SWD6D/6U3v/O52t9cF5t+MeJiRsfk7zMw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -466,6 +476,13 @@ packages: jest-get-type: 29.4.3 dev: true + /@jest/expect-utils@29.7.0: + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.6.3 + dev: true + /@jest/expect@29.6.1: resolution: {integrity: sha512-N5xlPrAYaRNyFgVf2s9Uyyvr795jnB6rObuPx4QFvNJz8aAjpZUDfO4bh5G/xuplMID8PrnuF1+SfSyDxhsgYg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -476,6 +493,16 @@ packages: - supports-color dev: true + /@jest/expect@29.7.0: + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + /@jest/fake-timers@29.6.1: resolution: {integrity: sha512-RdgHgbXyosCDMVYmj7lLpUwXA4c69vcNzhrt69dJJdf8azUrpRh3ckFCaTPNjsEeRi27Cig0oKDGxy5j7hOgHg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -488,14 +515,26 @@ packages: jest-util: 29.6.1 dev: true - /@jest/globals@29.6.1: - resolution: {integrity: sha512-2VjpaGy78JY9n9370H8zGRCFbYVWwjY6RdDMhoJHa1sYfwe6XM/azGN0SjY8kk7BOZApIejQ1BFPyH7FPG0w3A==} + /@jest/fake-timers@29.7.0: + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/environment': 29.6.1 - '@jest/expect': 29.6.1 - '@jest/types': 29.6.1 - jest-mock: 29.6.1 + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 20.4.2 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + dev: true + + /@jest/globals@29.7.0: + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 transitivePeerDependencies: - supports-color dev: true @@ -544,6 +583,13 @@ packages: '@sinclair/typebox': 0.27.8 dev: true + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + /@jest/source-map@29.6.0: resolution: {integrity: sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -596,6 +642,29 @@ packages: - supports-color dev: true + /@jest/transform@29.7.0: + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.22.9 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.18 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.5 + pirates: 4.0.6 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + dev: true + /@jest/types@29.6.1: resolution: {integrity: sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -608,6 +677,18 @@ packages: chalk: 4.1.2 dev: true + /@jest/types@29.6.3: + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-reports': 3.0.1 + '@types/node': 20.4.2 + '@types/yargs': 17.0.24 + chalk: 4.1.2 + dev: true + /@jridgewell/gen-mapping@0.3.3: resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} engines: {node: '>=6.0.0'} @@ -744,13 +825,6 @@ packages: '@types/istanbul-lib-report': 3.0.0 dev: true - /@types/jest@29.5.3: - resolution: {integrity: sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA==} - dependencies: - expect: 29.6.1 - pretty-format: 29.6.1 - dev: true - /@types/lodash@4.14.195: resolution: {integrity: sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==} dev: true @@ -1197,6 +1271,11 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: true @@ -1287,6 +1366,17 @@ packages: jest-util: 29.6.1 dev: true + /expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + dev: true + /express@4.18.2: resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} engines: {node: '>= 0.10.0'} @@ -1722,6 +1812,16 @@ packages: pretty-format: 29.6.1 dev: true + /jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + /jest-docblock@29.4.3: resolution: {integrity: sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1757,6 +1857,11 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true + /jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + /jest-haste-map@29.6.1: resolution: {integrity: sha512-0m7f9PZXxOCk1gRACiVgX85knUKPKLPg4oRCjLoqIm9brTHXaorMA0JpmtmVkQiT8nmXyIVoZd/nnH1cfC33ig==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1776,6 +1881,25 @@ packages: fsevents: 2.3.2 dev: true + /jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.6 + '@types/node': 20.4.2 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.5 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.2 + dev: true + /jest-leak-detector@29.6.1: resolution: {integrity: sha512-OrxMNyZirpOEwkF3UHnIkAiZbtkBWiye+hhBweCHkVbCgyEy71Mwbb5zgeTNYWJBi1qgDVfPC1IwO9dVEeTLwQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1794,6 +1918,16 @@ packages: pretty-format: 29.6.1 dev: true + /jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + /jest-message-util@29.6.1: resolution: {integrity: sha512-KoAW2zAmNSd3Gk88uJ56qXUWbFk787QKmjjJVOjtGFmmGSZgDBrlIL4AfQw1xyMYPNVD7dNInfIbur9B2rd/wQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1809,6 +1943,21 @@ packages: stack-utils: 2.0.6 dev: true + /jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/code-frame': 7.22.5 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.1 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.5 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.6 + dev: true + /jest-mock@29.6.1: resolution: {integrity: sha512-brovyV9HBkjXAEdRooaTQK42n8usKoSRR3gihzUpYeV/vwqgSoNfrksO7UfSACnPmxasO/8TmHM3w9Hp3G1dgw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1818,6 +1967,15 @@ packages: jest-util: 29.6.1 dev: true + /jest-mock@29.7.0: + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.4.2 + jest-util: 29.7.0 + dev: true + /jest-pnp-resolver@1.2.3(jest-resolve@29.6.1): resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} engines: {node: '>=6'} @@ -1835,6 +1993,11 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true + /jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + /jest-resolve-dependencies@29.6.1: resolution: {integrity: sha512-BbFvxLXtcldaFOhNMXmHRWx1nXQO5LoXiKSGQcA1LxxirYceZT6ch8KTE1bK3X31TNG/JbkI7OkS/ABexVahiw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1895,7 +2058,7 @@ packages: dependencies: '@jest/environment': 29.6.1 '@jest/fake-timers': 29.6.1 - '@jest/globals': 29.6.1 + '@jest/globals': 29.7.0 '@jest/source-map': 29.6.0 '@jest/test-result': 29.6.1 '@jest/transform': 29.6.1 @@ -1948,6 +2111,34 @@ packages: - supports-color dev: true + /jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.22.9 + '@babel/generator': 7.22.9 + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.9) + '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.22.9) + '@babel/types': 7.22.5 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.9) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + dev: true + /jest-util@29.6.1: resolution: {integrity: sha512-NRFCcjc+/uO3ijUVyNOQJluf8PtGCe/W6cix36+M3cTFgiYqFOOW5MgN4JOOcvbUhcKTYVd1CvHz/LWi8d16Mg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1960,6 +2151,18 @@ packages: picomatch: 2.3.1 dev: true + /jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.4.2 + chalk: 4.1.2 + ci-info: 3.8.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + dev: true + /jest-validate@29.6.1: resolution: {integrity: sha512-r3Ds69/0KCN4vx4sYAbGL1EVpZ7MSS0vLmd3gV78O+NAx3PDQQukRU5hNHPXlyqCgFY8XUk7EuTMLugh0KzahA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1996,6 +2199,16 @@ packages: supports-color: 8.1.1 dev: true + /jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@types/node': 20.4.2 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + dev: true + /jest@29.6.1(@types/node@20.4.2): resolution: {integrity: sha512-Nirw5B4nn69rVUZtemCQhwxOBhm0nsp3hmtF4rzCeWD7BkjAXRIji7xWQfnTNbz9g0aVsBX6aZK3n+23LM6uDw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2317,6 +2530,15 @@ packages: react-is: 18.2.0 dev: true + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + /prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} diff --git a/src/scripts/upload_impacted_targets.sh b/src/scripts/upload_impacted_targets.sh index 9fb75d2..114bd3a 100755 --- a/src/scripts/upload_impacted_targets.sh +++ b/src/scripts/upload_impacted_targets.sh @@ -2,9 +2,15 @@ set -euo pipefail -# API Token -if [[ -z ${API_TOKEN+x} ]]; then - echo "Missing API Token" +IS_FORK_BOOL="${IS_FORK:=false}" + +# API Token is required if PR is not from a fork, or +# RUN ID is required if PR is from a fork +if [[ (-z ${API_TOKEN-}) && (${IS_FORK_BOOL} == 'false') ]]; then + echo "Missing API Token when PR is not from a fork" + exit 2 +elif [[ (-z ${RUN_ID-}) && (${IS_FORK_BOOL} == 'true') ]]; then + echo "Missing workflow run id when PR is from a fork" exit 2 fi @@ -79,7 +85,7 @@ fi HTTP_STATUS_CODE=$( curl -s -o /dev/null -w '%{http_code}' -X POST \ - -H "Content-Type: application/json" -H "x-api-token:${API_TOKEN}" \ + -H "Content-Type: application/json" -H "x-api-token:${API_TOKEN-}" -H "x-forked-workflow-run-id:${RUN_ID-}" \ -d "@${POST_BODY}" \ "${API_URL}" ) diff --git a/tests/upload.test.ts b/tests/upload.test.ts index f8a2de0..81bc869 100644 --- a/tests/upload.test.ts +++ b/tests/upload.test.ts @@ -6,14 +6,30 @@ import exec from "node:child_process"; import fs from "node:fs"; import http from "node:http"; import util from "node:util"; +import { describe, beforeEach, beforeAll, afterAll, it, expect, afterEach } from "@jest/globals"; +import { strict as assert } from "node:assert"; const PORT = 4567; type ImpactedTargets = string[] | "ALL"; +type EnvVar = + | "API_TOKEN" + | "REPOSITORY" + | "TARGET_BRANCH" + | "PR_NUMBER" + | "PR_SHA" + | "IMPACTED_TARGETS_FILE" + | "IMPACTS_ALL_DETECTED" + | "API_URL" + | "RUN_ID" + | "IS_FORK"; + +type EnvVarSet = Record; + const fetchUrl = (path: string) => `http://localhost:${PORT}${path}`; const UPLOAD_IMPACTED_TARGETS_SCRIPT = "src/scripts/upload_impacted_targets.sh"; -const ENV_VARIABLES: Record = { +const DEFAULT_ENV_VARIABLES: EnvVarSet = { API_TOKEN: "test-api-token", REPOSITORY: "test-repo-owner/test-repo-name", TARGET_BRANCH: "test-target-branch", @@ -22,129 +38,162 @@ const ENV_VARIABLES: Record = { IMPACTED_TARGETS_FILE: "/tmp/test-impacted-targets-file", IMPACTS_ALL_DETECTED: "false", API_URL: fetchUrl("/testUploadImpactedTargets"), -}; -const exportEnv = (env: Record) => - Object.entries(env) - .map(([key, value]) => `${key}=${value}`) - .join(" "); - -// assigned in beforeAll -let server: http.Server; - -// assigned in beforeEach -let uploadedImpactedTargetsPayload = [null, null]; - -const runUploadTargets = async ( - impactedTargets: ImpactedTargets, - env: Record = ENV_VARIABLES, -) => { - // The bazel / glob / ... scripts are responsible for populating these files. - // Verify that the upload works as intended. - if (impactedTargets !== "ALL") { - fs.writeFileSync(env.IMPACTED_TARGETS_FILE, impactedTargets.join("\n")); - } - - const runScript = util.promisify(exec.exec)( - `${exportEnv(env)} ${UPLOAD_IMPACTED_TARGETS_SCRIPT}`, - ); - - await runScript; + RUN_ID: "123456", + IS_FORK: "false", }; -const expectImpactedTargetsUpload = (impactedTargets: ImpactedTargets): void => { - const { API_TOKEN, REPOSITORY, TARGET_BRANCH, PR_NUMBER, PR_SHA } = ENV_VARIABLES; - const [actualToken, actualBody] = uploadedImpactedTargetsPayload; - expect(actualToken).toEqual(API_TOKEN); - expect(actualBody).toEqual({ - repo: { - host: "github.com", - owner: REPOSITORY.split("/")[0], - name: REPOSITORY.split("/")[1], - }, - pr: { - number: PR_NUMBER, - sha: PR_SHA, - }, - targetBranch: TARGET_BRANCH, - impactedTargets, - }); -}; +describe("upload_impacted_targets", () => { + let server: http.Server; + let uploadedImpactedTargetsPayload: { + apiTokenHeader: string | null; + forkedWorkflowIdHeader: string | null; + requestBody: typeof express.request | null; + } | null = null; + let exportedEnvVars: EnvVarSet | null = null; + let forceUnauthorized = false; + + const exportEnv = (env: EnvVarSet): string => { + exportedEnvVars = env; + return Object.entries(env) + .map(([key, value]) => `${key}=${value}`) + .join(" "); + }; -beforeAll(function () { - const app = express(); + const runUploadTargets = async ( + impactedTargets: ImpactedTargets, + envOverrides: Partial = {}, + ) => { + const env: EnvVarSet = { ...DEFAULT_ENV_VARIABLES, ...envOverrides }; + // The bazel / glob / ... scripts are responsible for populating these files. + // Verify that the upload works as intended. + if (impactedTargets !== "ALL") { + fs.writeFileSync(env.IMPACTED_TARGETS_FILE, impactedTargets.join("\n")); + } + + const runScript = util.promisify(exec.exec)( + `${exportEnv(env)} ${UPLOAD_IMPACTED_TARGETS_SCRIPT}`, + ); - app.use(express.json({ limit: "10mb" })); + await runScript; + }; - app.post("/testUploadImpactedTargets", (req, res) => { - const actualApiToken = req.headers["x-api-token"]; - uploadedImpactedTargetsPayload = [actualApiToken, req.body]; + const expectImpactedTargetsUpload = (impactedTargets: ImpactedTargets): void => { + assert(exportedEnvVars); + assert(uploadedImpactedTargetsPayload); + + const { API_TOKEN, REPOSITORY, TARGET_BRANCH, PR_NUMBER, PR_SHA, RUN_ID } = exportedEnvVars; + const { apiTokenHeader, forkedWorkflowIdHeader, requestBody } = uploadedImpactedTargetsPayload; + + expect(apiTokenHeader).toEqual(API_TOKEN); + expect(forkedWorkflowIdHeader).toEqual(RUN_ID); + expect(requestBody).toEqual({ + repo: { + host: "github.com", + owner: REPOSITORY.split("/")[0], + name: REPOSITORY.split("/")[1], + }, + pr: { + number: PR_NUMBER, + sha: PR_SHA, + }, + targetBranch: TARGET_BRANCH, + impactedTargets, + }); + }; - res.sendStatus( - actualApiToken === ENV_VARIABLES.API_TOKEN ? StatusCodes.OK : StatusCodes.UNAUTHORIZED, - ); + beforeAll(function () { + const app = express(); + + app.use(express.json({ limit: "10mb" })); + + app.post("/testUploadImpactedTargets", (req, res) => { + const actualApiToken = req.headers["x-api-token"]; + const actualRunId = req.headers["x-forked-workflow-run-id"]; + + uploadedImpactedTargetsPayload = { + apiTokenHeader: (actualApiToken ?? "") as string, + forkedWorkflowIdHeader: (actualRunId ?? "") as string, + requestBody: req.body, + }; + + assert(exportedEnvVars); + res.sendStatus(forceUnauthorized ? StatusCodes.UNAUTHORIZED : StatusCodes.OK); + }); + + server = app.listen(PORT); }); - server = app.listen(PORT); -}); + beforeEach(function () { + uploadedImpactedTargetsPayload = null; + exportedEnvVars = null; + forceUnauthorized = false; + }); -beforeEach(function () { - uploadedImpactedTargetsPayload = [null, null]; -}); + afterEach(function () { + fs.rmSync(DEFAULT_ENV_VARIABLES.IMPACTED_TARGETS_FILE, { force: true }); + }); -afterEach(function () { - fs.rmSync(ENV_VARIABLES.IMPACTED_TARGETS_FILE, { force: true }); -}); + afterAll(function () { + server.close(); + }); -afterAll(function () { - server.close(); -}); + it("rejects if missing required input", async function () { + await expect(() => + util.promisify(exec.exec)(`${UPLOAD_IMPACTED_TARGETS_SCRIPT}`), + ).rejects.toBeTruthy(); + }); -// Tests + it("hits the endpoint", async function () { + const impactedTargets = ["target-1", "target-2", "target-3"]; + await runUploadTargets(impactedTargets); + expectImpactedTargetsUpload(impactedTargets); + }); -test("rejects if missing required input", async function () { - await expect(() => - util.promisify(exec.exec)(`${UPLOAD_IMPACTED_TARGETS_SCRIPT}`), - ).rejects.toBeTruthy(); -}); + it("supports empty targets", async function () { + const impactedTargets: string[] = []; + await runUploadTargets(impactedTargets); + expectImpactedTargetsUpload(impactedTargets); + }); -test("hits the endpoint", async function () { - const impactedTargets = ["target-1", "target-2", "target-3"]; - await runUploadTargets(impactedTargets); - expectImpactedTargetsUpload(impactedTargets); -}); + it("supports 1K targets", async function () { + const impactedTargets = [...new Array(1_000)].map((_, i) => `target-${i}`); + await runUploadTargets(impactedTargets); + expectImpactedTargetsUpload(impactedTargets); + }); -test("supports empty targets", async function () { - const impactedTargets: string[] = []; - await runUploadTargets(impactedTargets); - expectImpactedTargetsUpload(impactedTargets); -}); + it("supports 100K targets", async function () { + const impactedTargets = [...new Array(100_000)].map((_, i) => `target-${i}`); + await runUploadTargets(impactedTargets); + expectImpactedTargetsUpload(impactedTargets); + }); -test("supports 1K targets", async function () { - const impactedTargets = [...new Array(1_000)].map((_, i) => `target-${i}`); - await runUploadTargets(impactedTargets); - expectImpactedTargetsUpload(impactedTargets); -}); + it("supports IMPACTS_ALL", async function () { + await runUploadTargets("ALL", { IMPACTS_ALL_DETECTED: "true" }); + expectImpactedTargetsUpload("ALL"); + }); -test("supports 100K targets", async function () { - const impactedTargets = [...new Array(100_000)].map((_, i) => `target-${i}`); - await runUploadTargets(impactedTargets); - expectImpactedTargetsUpload(impactedTargets); -}); + it("allows missing API token if PR is coming from a fork", async function () { + const impactedTargets = ["target-1", "target-2", "target-3"]; + await runUploadTargets(impactedTargets, { API_TOKEN: "", IS_FORK: "true" }); + expectImpactedTargetsUpload(impactedTargets); + }); -test("supports IMPACTS_ALL", async function () { - const env = { ...ENV_VARIABLES, IMPACTS_ALL_DETECTED: "true" }; - await runUploadTargets("ALL", env); - expectImpactedTargetsUpload("ALL"); -}); + it("rejects when missing API token and is not a fork", async function () { + await expect(runUploadTargets(["a"], { API_TOKEN: "" })).rejects.toBeTruthy(); + }); -test("rejects when missing API token", async function () { - await expect(runUploadTargets([], _.omit(ENV_VARIABLES, "API_TOKEN"))).rejects.toBeTruthy(); -}); + it("rejects when missing API token and fork env vars", async function () { + await expect(runUploadTargets(["a"], { API_TOKEN: "", IS_FORK: "" })).rejects.toBeTruthy(); + }); -test("rejects on http 401", async function () { - const malformedEnv = { - ...ENV_VARIABLES, - API_TOKEN: " ", - }; - await expect(runUploadTargets([], malformedEnv)).rejects.toBeTruthy(); + it("rejects when missing forked workflow ID and is a fork", async function () { + await expect( + runUploadTargets(["a"], { API_TOKEN: "", RUN_ID: "", IS_FORK: "true" }), + ).rejects.toBeTruthy(); + }); + + it("rejects on http 401", async function () { + forceUnauthorized = true; + await expect(runUploadTargets([])).rejects.toBeTruthy(); + }); });