From abe83e6b46fe0889be3f07f0b85b34afc044e639 Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Thu, 31 Oct 2024 21:28:26 +0100 Subject: [PATCH 1/2] Split workflow processes. --- .github/workflows/check_ota_pr.yml | 45 +++ .github/workflows/ci.yml | 6 + .github/workflows/concat_cacerts.yml | 6 + .github/workflows/overwrite_cache.yml | 6 + .github/workflows/report_ota_pr.yml | 44 +++ .github/workflows/reprocess_all_images.yml | 8 + .github/workflows/run_autodl.yml | 7 + ...update_ota_pr.yml => update_manifests.yml} | 31 +- .gitignore | 1 + package.json | 8 +- pnpm-lock.yaml | 314 +++++++++--------- src/common.ts | 7 + src/ghw_check_ota_pr.ts | 68 ++++ src/ghw_create_autodl_release.ts | 3 +- src/ghw_get_changed_ota_files.ts | 34 ++ ...ate_ota_pr.ts => ghw_process_ota_files.ts} | 136 ++------ src/ghw_report_ota_pr.ts | 82 +++++ src/ghw_update_manifests.ts | 26 ++ src/index.ts | 6 +- tests/data.test.ts | 4 +- ...ta_pr.test.ts => ghw_check_ota_pr.test.ts} | 167 +++++----- tests/ghw_report_ota_pr.test.ts | 29 ++ tests/ghw_reprocess_all_images.test.ts | 32 +- tests/jest.config.ts | 8 +- 24 files changed, 686 insertions(+), 392 deletions(-) create mode 100644 .github/workflows/check_ota_pr.yml create mode 100644 .github/workflows/report_ota_pr.yml rename .github/workflows/{update_ota_pr.yml => update_manifests.yml} (50%) create mode 100644 src/ghw_check_ota_pr.ts create mode 100644 src/ghw_get_changed_ota_files.ts rename src/{ghw_update_ota_pr.ts => ghw_process_ota_files.ts} (58%) create mode 100644 src/ghw_report_ota_pr.ts create mode 100644 src/ghw_update_manifests.ts rename tests/{ghw_update_ota_pr.test.ts => ghw_check_ota_pr.test.ts} (77%) create mode 100644 tests/ghw_report_ota_pr.test.ts diff --git a/.github/workflows/check_ota_pr.yml b/.github/workflows/check_ota_pr.yml new file mode 100644 index 00000000..900ffb0d --- /dev/null +++ b/.github/workflows/check_ota_pr.yml @@ -0,0 +1,45 @@ +name: Check OTA PR +on: + pull_request: + types: [opened, synchronize, reopened, edited] + branches: [master] + paths: ['images/**'] + +jobs: + check-ota-pr: + runs-on: ubuntu-latest + # don't run if PR was closed without merge, or explicitly disabled + if: | + !contains(github.event.pull_request.labels.*.name, 'ignore-ota-workflow') + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 9 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + registry-url: https://registry.npmjs.org/ + cache: pnpm + + - name: Install dependencies + run: pnpm i --frozen-lockfile + + - name: Build + run: pnpm run build + + - name: Check OTA PR + uses: actions/github-script@v7 + with: + script: | + const {checkOtaPR} = await import("${{ github.workspace }}/dist/ghw_check_ota_pr.js") + + await checkOtaPR(github, core, context) + + - uses: actions/upload-artifact@v4 + if: always() + with: + name: pr + path: pr/ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index adf93002..5417ee38 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,22 +11,28 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 with: version: 9 + - uses: actions/setup-node@v4 with: node-version: 20 registry-url: https://registry.npmjs.org/ cache: pnpm + - name: Install dependencies run: pnpm i --frozen-lockfile + - name: Build run: pnpm run build + - name: Lint run: | pnpm run format:check pnpm run eslint + - name: Test # NOTE: see jest.config.ts `collectCoverageFrom` run: pnpm run coverage diff --git a/.github/workflows/concat_cacerts.yml b/.github/workflows/concat_cacerts.yml index 2618a33e..5604d7c3 100644 --- a/.github/workflows/concat_cacerts.yml +++ b/.github/workflows/concat_cacerts.yml @@ -13,18 +13,23 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 with: version: 9 + - uses: actions/setup-node@v4 with: node-version: 20 registry-url: https://registry.npmjs.org/ cache: pnpm + - name: Install dependencies run: pnpm i --frozen-lockfile + - name: Build run: pnpm run build + - name: Concat CACerts uses: actions/github-script@v7 with: @@ -32,6 +37,7 @@ jobs: const {concatCaCerts} = await import("${{ github.workspace }}/dist/ghw_concat_cacerts.js") await concatCaCerts(github, core, context) + - name: Commit changes run: | git config --global user.name 'github-actions[bot]' diff --git a/.github/workflows/overwrite_cache.yml b/.github/workflows/overwrite_cache.yml index 12fa7603..5424f6cb 100644 --- a/.github/workflows/overwrite_cache.yml +++ b/.github/workflows/overwrite_cache.yml @@ -16,18 +16,23 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 with: version: 9 + - uses: actions/setup-node@v4 with: node-version: 20 registry-url: https://registry.npmjs.org/ cache: pnpm + - name: Install dependencies run: pnpm i --frozen-lockfile + - name: Build run: pnpm run build + - name: Overwrite cache uses: actions/github-script@v7 with: @@ -35,6 +40,7 @@ jobs: const {overwriteCache} = await import("${{ github.workspace }}/dist/gwh_overwrite_cache.js") await overwriteCache(github, core, context, "${{ inputs.manufacturers || '' }}") + - name: Commit changes run: | git config --global user.name 'github-actions[bot]' diff --git a/.github/workflows/report_ota_pr.yml b/.github/workflows/report_ota_pr.yml new file mode 100644 index 00000000..71554d2b --- /dev/null +++ b/.github/workflows/report_ota_pr.yml @@ -0,0 +1,44 @@ +name: Report OTA PR +on: + workflow_run: + workflows: ['Check OTA PR'] + types: + - completed + +permissions: + contents: read + pull-requests: write + actions: read + +jobs: + report-ota-pr: + runs-on: ubuntu-latest + # should never be anything else... + if: | + github.event.workflow_run.event == 'pull_request' + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 9 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + registry-url: https://registry.npmjs.org/ + cache: pnpm + + - name: Install dependencies + run: pnpm i --frozen-lockfile + + - name: Build + run: pnpm run build + + - name: Check OTA PR + uses: actions/github-script@v7 + with: + script: | + const {reportOtaPR} = await import("${{ github.workspace }}/dist/ghw_report_ota_pr.js") + + await reportOtaPR(github, core, context) diff --git a/.github/workflows/reprocess_all_images.yml b/.github/workflows/reprocess_all_images.yml index 124f0165..34d6f551 100644 --- a/.github/workflows/reprocess_all_images.yml +++ b/.github/workflows/reprocess_all_images.yml @@ -22,18 +22,23 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 with: version: 9 + - uses: actions/setup-node@v4 with: node-version: 20 registry-url: https://registry.npmjs.org/ cache: pnpm + - name: Install dependencies run: pnpm i --frozen-lockfile + - name: Build run: pnpm run build + - name: Create and checkout new branch id: create_branch run: | @@ -42,6 +47,7 @@ jobs: branch_name="reprocess-$(date +'%Y-%m-%d-%H-%M-%S')" echo "branch_name=$branch_name" >> $GITHUB_OUTPUT git checkout -b $branch_name + - name: Reprocess uses: actions/github-script@v7 env: @@ -51,11 +57,13 @@ jobs: const {reProcessAllImages} = await import("${{ github.workspace }}/dist/ghw_reprocess_all_images.js") await reProcessAllImages(github, core, context, ${{ fromJSON(inputs.remove_not_in_manifest) }}, true) + - name: Commit changes in new branch run: | git add . git commit -m "Re-Processed all images" || echo 'Nothing to commit' git push -u origin HEAD + - name: Create pull request uses: actions/github-script@v7 with: diff --git a/.github/workflows/run_autodl.yml b/.github/workflows/run_autodl.yml index 216557aa..97e23aa4 100644 --- a/.github/workflows/run_autodl.yml +++ b/.github/workflows/run_autodl.yml @@ -28,18 +28,23 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 with: version: 9 + - uses: actions/setup-node@v4 with: node-version: 20 registry-url: https://registry.npmjs.org/ cache: pnpm + - name: Install dependencies run: pnpm i --frozen-lockfile + - name: Build run: pnpm run build + - name: Run Autodl uses: actions/github-script@v7 env: @@ -51,6 +56,7 @@ jobs: const {runAutodl} = await import("${{ github.workspace }}/dist/ghw_run_autodl.js") await runAutodl(github, core, context, "${{ inputs.manufacturers || '' }}") + - name: Create Autodl release uses: actions/github-script@v7 with: @@ -58,6 +64,7 @@ jobs: const {createAutodlRelease} = await import("${{ github.workspace }}/dist/ghw_create_autodl_release.js") await createAutodlRelease(github, core, context) + - name: Commit changes run: | git config --global user.name 'github-actions[bot]' diff --git a/.github/workflows/update_ota_pr.yml b/.github/workflows/update_manifests.yml similarity index 50% rename from .github/workflows/update_ota_pr.yml rename to .github/workflows/update_manifests.yml index 12303744..1d5a98dc 100644 --- a/.github/workflows/update_ota_pr.yml +++ b/.github/workflows/update_manifests.yml @@ -1,50 +1,43 @@ -name: Update OTA PR +name: Update manifests on: - pull_request: - types: [opened, synchronize, reopened, edited, closed] + push: branches: [master] paths: ['images/**'] permissions: contents: write - pull-requests: write jobs: - update-pr: + update-manifests: runs-on: ubuntu-latest - # don't run if PR was closed without merge, or explicitly disabled - if: | - !contains(github.event.pull_request.labels.*.name, 'ignore-ota-workflow') && (github.event.action != 'closed' || github.event.pull_request.merged == true) steps: - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 with: version: 9 + - uses: actions/setup-node@v4 with: node-version: 20 registry-url: https://registry.npmjs.org/ cache: pnpm + - name: Install dependencies run: pnpm i --frozen-lockfile + - name: Build run: pnpm run build - - name: Get changed files - run: | - files=$(gh pr view ${{ github.event.pull_request.number }} --json files -q '.files[].path' | tr '\n' ',') - echo "files=$files" >> $GITHUB_OUTPUT - id: changed_files - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Update PR + + - name: Update manifests uses: actions/github-script@v7 with: script: | - const {updateOtaPR} = await import("${{ github.workspace }}/dist/ghw_update_ota_pr.js") + const {updateManifests} = await import("${{ github.workspace }}/dist/ghw_update_manifests.js") + + await updateManifests(github, core, context) - await updateOtaPR(github, core, context, "${{steps.changed_files.outputs.files}}") - name: Commit changes on push - if: github.event.pull_request.merged == true run: | git config --global user.name 'github-actions[bot]' git config --global user.email 'github-actions[bot]@users.noreply.github.com' diff --git a/.gitignore b/.gitignore index df88e58c..68165b48 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ dist/ temp/ tmp/ .jest-tmp/ +pr/ tsconfig.tsbuildinfo # used by tests diff --git a/package.json b/package.json index f9fd9b7d..583348cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zigbee-ota", - "version": "1.0.0", + "version": "1.1.0", "repository": { "type": "git", "url": "git+https://github.com/Koenkk/zigbee-OTA.git" @@ -47,12 +47,12 @@ "devDependencies": { "@actions/core": "^1.11.1", "@actions/github": "^6.0.0", - "@eslint/core": "^0.7.0", + "@eslint/core": "^0.8.0", "@eslint/js": "^9.13.0", "@ianvs/prettier-plugin-sort-imports": "^4.3.1", "@octokit/rest": "^21.0.2", "@types/jest": "^29.5.14", - "@types/node": "^22.8.1", + "@types/node": "^22.8.6", "eslint": "^9.13.0", "eslint-config-prettier": "^9.1.0", "jest": "^29.7.0", @@ -60,6 +60,6 @@ "ts-jest": "^29.2.5", "ts-node": "^10.9.2", "typescript": "^5.6.3", - "typescript-eslint": "^8.12.0" + "typescript-eslint": "^8.12.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 28f06e33..a0acb727 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,8 +19,8 @@ importers: specifier: ^6.0.0 version: 6.0.0 '@eslint/core': - specifier: ^0.7.0 - version: 0.7.0 + specifier: ^0.8.0 + version: 0.8.0 '@eslint/js': specifier: ^9.13.0 version: 9.13.0 @@ -34,8 +34,8 @@ importers: specifier: ^29.5.14 version: 29.5.14 '@types/node': - specifier: ^22.8.1 - version: 22.8.1 + specifier: ^22.8.6 + version: 22.8.6 eslint: specifier: ^9.13.0 version: 9.13.0 @@ -44,22 +44,22 @@ importers: version: 9.1.0(eslint@9.13.0) jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@22.8.1)(ts-node@10.9.2(@types/node@22.8.1)(typescript@5.6.3)) + version: 29.7.0(@types/node@22.8.6)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3)) prettier: specifier: ^3.3.3 version: 3.3.3 ts-jest: specifier: ^29.2.5 - version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@22.8.1)(ts-node@10.9.2(@types/node@22.8.1)(typescript@5.6.3)))(typescript@5.6.3) + version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@22.8.6)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3)))(typescript@5.6.3) ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@22.8.1)(typescript@5.6.3) + version: 10.9.2(@types/node@22.8.6)(typescript@5.6.3) typescript: specifier: ^5.6.3 version: 5.6.3 typescript-eslint: - specifier: ^8.12.0 - version: 8.12.0(eslint@9.13.0)(typescript@5.6.3) + specifier: ^8.12.2 + version: 8.12.2(eslint@9.13.0)(typescript@5.6.3) packages: @@ -82,20 +82,20 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@babel/code-frame@7.26.0': - resolution: {integrity: sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g==} + '@babel/code-frame@7.26.2': + resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.26.0': - resolution: {integrity: sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA==} + '@babel/compat-data@7.26.2': + resolution: {integrity: sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==} engines: {node: '>=6.9.0'} '@babel/core@7.26.0': resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} engines: {node: '>=6.9.0'} - '@babel/generator@7.26.0': - resolution: {integrity: sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w==} + '@babel/generator@7.26.2': + resolution: {integrity: sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==} engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@7.25.9': @@ -132,8 +132,8 @@ packages: resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.26.1': - resolution: {integrity: sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==} + '@babel/parser@7.26.2': + resolution: {integrity: sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==} engines: {node: '>=6.0.0'} hasBin: true @@ -265,6 +265,10 @@ packages: resolution: {integrity: sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/core@0.8.0': + resolution: {integrity: sha512-ncQZoR8YJtXIrBuJo1vDlIIR8+uoyYj2tRXE/RbZ3KHWYXNLcPeOgNKRBzXSZ/yQbVObVS8JGbhzvpifU+eQqw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/eslintrc@3.1.0': resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -577,8 +581,8 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@22.8.1': - resolution: {integrity: sha512-k6Gi8Yyo8EtrNtkHXutUu2corfDf9su95VYVP10aGYMMROM6SAItZi0w1XszA6RtWTHSVp5OeFof37w0IEqCQg==} + '@types/node@22.8.6': + resolution: {integrity: sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==} '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} @@ -589,8 +593,8 @@ packages: '@types/yargs@17.0.33': resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - '@typescript-eslint/eslint-plugin@8.12.0': - resolution: {integrity: sha512-uRqchEKT0/OwDePTwCjSFO2aH4zccdeQ7DgAzM/8fuXc+PAXvpdMRbuo+oCmK1lSfXssk2UUBNiWihobKxQp/g==} + '@typescript-eslint/eslint-plugin@8.12.2': + resolution: {integrity: sha512-gQxbxM8mcxBwaEmWdtLCIGLfixBMHhQjBqR8sVWNTPpcj45WlYL2IObS/DNMLH1DBP0n8qz+aiiLTGfopPEebw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 @@ -600,8 +604,8 @@ packages: typescript: optional: true - '@typescript-eslint/parser@8.12.0': - resolution: {integrity: sha512-7U20duDQWAOhCk2VtyY41Vor/CJjiEW063Zel9aoRXq89FQ/jr+0e0m3kxh9Sk5SFW9B1AblVIBtXd+1xQ1NWQ==} + '@typescript-eslint/parser@8.12.2': + resolution: {integrity: sha512-MrvlXNfGPLH3Z+r7Tk+Z5moZAc0dzdVjTgUgwsdGweH7lydysQsnSww3nAmsq8blFuRD5VRlAr9YdEFw3e6PBw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -610,12 +614,12 @@ packages: typescript: optional: true - '@typescript-eslint/scope-manager@8.12.0': - resolution: {integrity: sha512-jbuCXK18iEshRFUtlCIMAmOKA6OAsKjo41UcXPqx7ZWh2b4cmg6pV/pNcZSB7oW9mtgF95yizr7Jnwt3IUD2pA==} + '@typescript-eslint/scope-manager@8.12.2': + resolution: {integrity: sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@8.12.0': - resolution: {integrity: sha512-cHioAZO/nLgyzTmwv7gWIjEKMHSbioKEZqLCaItTn7RvJP1QipuGVwEjPJa6Kv9u9UiUMVAESY9JH186TjKITw==} + '@typescript-eslint/type-utils@8.12.2': + resolution: {integrity: sha512-bwuU4TAogPI+1q/IJSKuD4shBLc/d2vGcRT588q+jzayQyjVK2X6v/fbR4InY2U2sgf8MEvVCqEWUzYzgBNcGQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' @@ -623,12 +627,12 @@ packages: typescript: optional: true - '@typescript-eslint/types@8.12.0': - resolution: {integrity: sha512-Cc+iNtqBJ492f8KLEmKXe1l6683P0MlFO8Bk1NMphnzVIGH4/Wn9kvandFH+gYR1DDUjH/hgeWRGdO5Tj8gjYg==} + '@typescript-eslint/types@8.12.2': + resolution: {integrity: sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.12.0': - resolution: {integrity: sha512-a4koVV7HHVOQWcGb6ZcAlunJnAdwo/CITRbleQBSjq5+2WLoAJQCAAiecvrAdSM+n/man6Ghig5YgdGVIC6xqw==} + '@typescript-eslint/typescript-estree@8.12.2': + resolution: {integrity: sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' @@ -636,14 +640,14 @@ packages: typescript: optional: true - '@typescript-eslint/utils@8.12.0': - resolution: {integrity: sha512-5i1tqLwlf0fpX1j05paNKyIzla/a4Y3Xhh6AFzi0do/LDJLvohtZYaisaTB9kq0D4uBocAxWDTGzNMOCCwIgXA==} + '@typescript-eslint/utils@8.12.2': + resolution: {integrity: sha512-UTTuDIX3fkfAz6iSVa5rTuSfWIYZ6ATtEocQ/umkRSyC9O919lbZ8dcH7mysshrCdrAM03skJOEYaBugxN+M6A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - '@typescript-eslint/visitor-keys@8.12.0': - resolution: {integrity: sha512-2rXkr+AtZZLuNY18aUjv5wtB9oUiwY1WnNi7VTsdCdy1m958ULeUKoAegldQTjqpbpNJ5cQ4egR8/bh5tbrKKQ==} + '@typescript-eslint/visitor-keys@8.12.2': + resolution: {integrity: sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} acorn-jsx@5.3.2: @@ -774,8 +778,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001673: - resolution: {integrity: sha512-WTrjUCSMp3LYX0nE12ECkV0a+e6LC85E0Auz75555/qr78Oc8YWhEPNfDd6SHdtlCMSzqtuXY0uyEMNRcsKpKw==} + caniuse-lite@1.0.30001676: + resolution: {integrity: sha512-Qz6zwGCiPghQXGJvgQAem79esjitvJ+CxSbSQkW9H/UX5hg8XM88d4lp2W+MEQ81j+Hip58Il+jGVdazk1z9cw==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -879,8 +883,8 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - electron-to-chromium@1.5.48: - resolution: {integrity: sha512-FXULnNK7ACNI9MTMOVAzUGiz/YrK9Kcb0s/JT4aJgsam7Eh6XYe7Y6q95lPq+VdBe1DpT2eTnfXFtnuPGCks4w==} + electron-to-chromium@1.5.50: + resolution: {integrity: sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==} emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} @@ -913,16 +917,16 @@ packages: peerDependencies: eslint: '>=7.0.0' - eslint-scope@8.1.0: - resolution: {integrity: sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==} + eslint-scope@8.2.0: + resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-visitor-keys@4.1.0: - resolution: {integrity: sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==} + eslint-visitor-keys@4.2.0: + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint@9.13.0: @@ -935,8 +939,8 @@ packages: jiti: optional: true - espree@10.2.0: - resolution: {integrity: sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==} + espree@10.3.0: + resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} esprima@4.0.1: @@ -1700,8 +1704,8 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - ts-api-utils@1.3.0: - resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + ts-api-utils@1.4.0: + resolution: {integrity: sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==} engines: {node: '>=16'} peerDependencies: typescript: '>=4.2.0' @@ -1760,8 +1764,8 @@ packages: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} - typescript-eslint@8.12.0: - resolution: {integrity: sha512-m8aQM4pqc17dcD3BsQzUqVXkcclCspuCCv7GhYlwMWNYAXFV8xJkn8vUM8YxoR78BY6S+NX/J7rfNVaGNLgXgQ==} + typescript-eslint@8.12.2: + resolution: {integrity: sha512-UbuVUWSrHVR03q9CWx+JDHeO6B/Hr9p4U5lRH++5tq/EbFq1faYZe50ZSBePptgfIKLEti0aPQ3hFgnPVcd8ZQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' @@ -1887,23 +1891,23 @@ snapshots: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 - '@babel/code-frame@7.26.0': + '@babel/code-frame@7.26.2': dependencies: '@babel/helper-validator-identifier': 7.25.9 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.26.0': {} + '@babel/compat-data@7.26.2': {} '@babel/core@7.26.0': dependencies: '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.26.0 - '@babel/generator': 7.26.0 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.2 '@babel/helper-compilation-targets': 7.25.9 '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) '@babel/helpers': 7.26.0 - '@babel/parser': 7.26.1 + '@babel/parser': 7.26.2 '@babel/template': 7.25.9 '@babel/traverse': 7.25.9 '@babel/types': 7.26.0 @@ -1915,9 +1919,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.26.0': + '@babel/generator@7.26.2': dependencies: - '@babel/parser': 7.26.1 + '@babel/parser': 7.26.2 '@babel/types': 7.26.0 '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 @@ -1925,7 +1929,7 @@ snapshots: '@babel/helper-compilation-targets@7.25.9': dependencies: - '@babel/compat-data': 7.26.0 + '@babel/compat-data': 7.26.2 '@babel/helper-validator-option': 7.25.9 browserslist: 4.24.2 lru-cache: 5.1.1 @@ -1960,7 +1964,7 @@ snapshots: '@babel/template': 7.25.9 '@babel/types': 7.26.0 - '@babel/parser@7.26.1': + '@babel/parser@7.26.2': dependencies: '@babel/types': 7.26.0 @@ -2051,15 +2055,15 @@ snapshots: '@babel/template@7.25.9': dependencies: - '@babel/code-frame': 7.26.0 - '@babel/parser': 7.26.1 + '@babel/code-frame': 7.26.2 + '@babel/parser': 7.26.2 '@babel/types': 7.26.0 '@babel/traverse@7.25.9': dependencies: - '@babel/code-frame': 7.26.0 - '@babel/generator': 7.26.0 - '@babel/parser': 7.26.1 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.2 + '@babel/parser': 7.26.2 '@babel/template': 7.25.9 '@babel/types': 7.26.0 debug: 4.3.7 @@ -2095,11 +2099,13 @@ snapshots: '@eslint/core@0.7.0': {} + '@eslint/core@0.8.0': {} + '@eslint/eslintrc@3.1.0': dependencies: ajv: 6.12.6 debug: 4.3.7 - espree: 10.2.0 + espree: 10.3.0 globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.0 @@ -2133,8 +2139,8 @@ snapshots: '@ianvs/prettier-plugin-sort-imports@4.3.1(prettier@3.3.3)': dependencies: '@babel/core': 7.26.0 - '@babel/generator': 7.26.0 - '@babel/parser': 7.26.1 + '@babel/generator': 7.26.2 + '@babel/parser': 7.26.2 '@babel/traverse': 7.25.9 '@babel/types': 7.26.0 prettier: 3.3.3 @@ -2168,27 +2174,27 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 22.8.1 + '@types/node': 22.8.6 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0(ts-node@10.9.2(@types/node@22.8.1)(typescript@5.6.3))': + '@jest/core@29.7.0(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.8.1 + '@types/node': 22.8.6 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.8.1)(ts-node@10.9.2(@types/node@22.8.1)(typescript@5.6.3)) + jest-config: 29.7.0(@types/node@22.8.6)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -2213,7 +2219,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.8.1 + '@types/node': 22.8.6 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -2231,7 +2237,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.8.1 + '@types/node': 22.8.6 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -2253,7 +2259,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 22.8.1 + '@types/node': 22.8.6 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -2323,7 +2329,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.8.1 + '@types/node': 22.8.6 '@types/yargs': 17.0.33 chalk: 4.1.2 @@ -2497,7 +2503,7 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.26.1 + '@babel/parser': 7.26.2 '@babel/types': 7.26.0 '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 @@ -2509,7 +2515,7 @@ snapshots: '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.26.1 + '@babel/parser': 7.26.2 '@babel/types': 7.26.0 '@types/babel__traverse@7.20.6': @@ -2520,7 +2526,7 @@ snapshots: '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 22.8.1 + '@types/node': 22.8.6 '@types/istanbul-lib-coverage@2.0.6': {} @@ -2539,7 +2545,7 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/node@22.8.1': + '@types/node@22.8.6': dependencies: undici-types: 6.19.8 @@ -2551,30 +2557,30 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.12.0(@typescript-eslint/parser@8.12.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)': + '@typescript-eslint/eslint-plugin@8.12.2(@typescript-eslint/parser@8.12.2(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.12.0(eslint@9.13.0)(typescript@5.6.3) - '@typescript-eslint/scope-manager': 8.12.0 - '@typescript-eslint/type-utils': 8.12.0(eslint@9.13.0)(typescript@5.6.3) - '@typescript-eslint/utils': 8.12.0(eslint@9.13.0)(typescript@5.6.3) - '@typescript-eslint/visitor-keys': 8.12.0 + '@typescript-eslint/parser': 8.12.2(eslint@9.13.0)(typescript@5.6.3) + '@typescript-eslint/scope-manager': 8.12.2 + '@typescript-eslint/type-utils': 8.12.2(eslint@9.13.0)(typescript@5.6.3) + '@typescript-eslint/utils': 8.12.2(eslint@9.13.0)(typescript@5.6.3) + '@typescript-eslint/visitor-keys': 8.12.2 eslint: 9.13.0 graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.6.3) + ts-api-utils: 1.4.0(typescript@5.6.3) optionalDependencies: typescript: 5.6.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.12.0(eslint@9.13.0)(typescript@5.6.3)': + '@typescript-eslint/parser@8.12.2(eslint@9.13.0)(typescript@5.6.3)': dependencies: - '@typescript-eslint/scope-manager': 8.12.0 - '@typescript-eslint/types': 8.12.0 - '@typescript-eslint/typescript-estree': 8.12.0(typescript@5.6.3) - '@typescript-eslint/visitor-keys': 8.12.0 + '@typescript-eslint/scope-manager': 8.12.2 + '@typescript-eslint/types': 8.12.2 + '@typescript-eslint/typescript-estree': 8.12.2(typescript@5.6.3) + '@typescript-eslint/visitor-keys': 8.12.2 debug: 4.3.7 eslint: 9.13.0 optionalDependencies: @@ -2582,54 +2588,54 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.12.0': + '@typescript-eslint/scope-manager@8.12.2': dependencies: - '@typescript-eslint/types': 8.12.0 - '@typescript-eslint/visitor-keys': 8.12.0 + '@typescript-eslint/types': 8.12.2 + '@typescript-eslint/visitor-keys': 8.12.2 - '@typescript-eslint/type-utils@8.12.0(eslint@9.13.0)(typescript@5.6.3)': + '@typescript-eslint/type-utils@8.12.2(eslint@9.13.0)(typescript@5.6.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.12.0(typescript@5.6.3) - '@typescript-eslint/utils': 8.12.0(eslint@9.13.0)(typescript@5.6.3) + '@typescript-eslint/typescript-estree': 8.12.2(typescript@5.6.3) + '@typescript-eslint/utils': 8.12.2(eslint@9.13.0)(typescript@5.6.3) debug: 4.3.7 - ts-api-utils: 1.3.0(typescript@5.6.3) + ts-api-utils: 1.4.0(typescript@5.6.3) optionalDependencies: typescript: 5.6.3 transitivePeerDependencies: - eslint - supports-color - '@typescript-eslint/types@8.12.0': {} + '@typescript-eslint/types@8.12.2': {} - '@typescript-eslint/typescript-estree@8.12.0(typescript@5.6.3)': + '@typescript-eslint/typescript-estree@8.12.2(typescript@5.6.3)': dependencies: - '@typescript-eslint/types': 8.12.0 - '@typescript-eslint/visitor-keys': 8.12.0 + '@typescript-eslint/types': 8.12.2 + '@typescript-eslint/visitor-keys': 8.12.2 debug: 4.3.7 fast-glob: 3.3.2 is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.6.3 - ts-api-utils: 1.3.0(typescript@5.6.3) + ts-api-utils: 1.4.0(typescript@5.6.3) optionalDependencies: typescript: 5.6.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.12.0(eslint@9.13.0)(typescript@5.6.3)': + '@typescript-eslint/utils@8.12.2(eslint@9.13.0)(typescript@5.6.3)': dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@9.13.0) - '@typescript-eslint/scope-manager': 8.12.0 - '@typescript-eslint/types': 8.12.0 - '@typescript-eslint/typescript-estree': 8.12.0(typescript@5.6.3) + '@typescript-eslint/scope-manager': 8.12.2 + '@typescript-eslint/types': 8.12.2 + '@typescript-eslint/typescript-estree': 8.12.2(typescript@5.6.3) eslint: 9.13.0 transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/visitor-keys@8.12.0': + '@typescript-eslint/visitor-keys@8.12.2': dependencies: - '@typescript-eslint/types': 8.12.0 + '@typescript-eslint/types': 8.12.2 eslint-visitor-keys: 3.4.3 acorn-jsx@5.3.2(acorn@8.14.0): @@ -2756,8 +2762,8 @@ snapshots: browserslist@4.24.2: dependencies: - caniuse-lite: 1.0.30001673 - electron-to-chromium: 1.5.48 + caniuse-lite: 1.0.30001676 + electron-to-chromium: 1.5.50 node-releases: 2.0.18 update-browserslist-db: 1.1.1(browserslist@4.24.2) @@ -2777,7 +2783,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001673: {} + caniuse-lite@1.0.30001676: {} chalk@4.1.2: dependencies: @@ -2812,13 +2818,13 @@ snapshots: convert-source-map@2.0.0: {} - create-jest@29.7.0(@types/node@22.8.1)(ts-node@10.9.2(@types/node@22.8.1)(typescript@5.6.3)): + create-jest@29.7.0(@types/node@22.8.6)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3)): 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@22.8.1)(ts-node@10.9.2(@types/node@22.8.1)(typescript@5.6.3)) + jest-config: 29.7.0(@types/node@22.8.6)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -2859,7 +2865,7 @@ snapshots: dependencies: jake: 10.9.2 - electron-to-chromium@1.5.48: {} + electron-to-chromium@1.5.50: {} emittery@0.13.1: {} @@ -2881,14 +2887,14 @@ snapshots: dependencies: eslint: 9.13.0 - eslint-scope@8.1.0: + eslint-scope@8.2.0: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 eslint-visitor-keys@3.4.3: {} - eslint-visitor-keys@4.1.0: {} + eslint-visitor-keys@4.2.0: {} eslint@9.13.0: dependencies: @@ -2909,9 +2915,9 @@ snapshots: cross-spawn: 7.0.3 debug: 4.3.7 escape-string-regexp: 4.0.0 - eslint-scope: 8.1.0 - eslint-visitor-keys: 4.1.0 - espree: 10.2.0 + eslint-scope: 8.2.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 esquery: 1.6.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 @@ -2930,11 +2936,11 @@ snapshots: transitivePeerDependencies: - supports-color - espree@10.2.0: + espree@10.3.0: dependencies: acorn: 8.14.0 acorn-jsx: 5.3.2(acorn@8.14.0) - eslint-visitor-keys: 4.1.0 + eslint-visitor-keys: 4.2.0 esprima@4.0.1: {} @@ -3135,7 +3141,7 @@ snapshots: istanbul-lib-instrument@5.2.1: dependencies: '@babel/core': 7.26.0 - '@babel/parser': 7.26.1 + '@babel/parser': 7.26.2 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 @@ -3145,7 +3151,7 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: '@babel/core': 7.26.0 - '@babel/parser': 7.26.1 + '@babel/parser': 7.26.2 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 7.6.3 @@ -3196,7 +3202,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.8.1 + '@types/node': 22.8.6 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.3 @@ -3216,16 +3222,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@22.8.1)(ts-node@10.9.2(@types/node@22.8.1)(typescript@5.6.3)): + jest-cli@29.7.0(@types/node@22.8.6)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.8.1)(typescript@5.6.3)) + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.8.1)(ts-node@10.9.2(@types/node@22.8.1)(typescript@5.6.3)) + create-jest: 29.7.0(@types/node@22.8.6)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3)) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@22.8.1)(ts-node@10.9.2(@types/node@22.8.1)(typescript@5.6.3)) + jest-config: 29.7.0(@types/node@22.8.6)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -3235,7 +3241,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@22.8.1)(ts-node@10.9.2(@types/node@22.8.1)(typescript@5.6.3)): + jest-config@29.7.0(@types/node@22.8.6)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3)): dependencies: '@babel/core': 7.26.0 '@jest/test-sequencer': 29.7.0 @@ -3260,8 +3266,8 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 22.8.1 - ts-node: 10.9.2(@types/node@22.8.1)(typescript@5.6.3) + '@types/node': 22.8.6 + ts-node: 10.9.2(@types/node@22.8.6)(typescript@5.6.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -3290,7 +3296,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.8.1 + '@types/node': 22.8.6 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -3300,7 +3306,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 22.8.1 + '@types/node': 22.8.6 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -3326,7 +3332,7 @@ snapshots: jest-message-util@29.7.0: dependencies: - '@babel/code-frame': 7.26.0 + '@babel/code-frame': 7.26.2 '@jest/types': 29.6.3 '@types/stack-utils': 2.0.3 chalk: 4.1.2 @@ -3339,7 +3345,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.8.1 + '@types/node': 22.8.6 jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -3374,7 +3380,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.8.1 + '@types/node': 22.8.6 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -3402,7 +3408,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.8.1 + '@types/node': 22.8.6 chalk: 4.1.2 cjs-module-lexer: 1.4.1 collect-v8-coverage: 1.0.2 @@ -3423,7 +3429,7 @@ snapshots: jest-snapshot@29.7.0: dependencies: '@babel/core': 7.26.0 - '@babel/generator': 7.26.0 + '@babel/generator': 7.26.2 '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) '@babel/types': 7.26.0 @@ -3448,7 +3454,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.8.1 + '@types/node': 22.8.6 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -3467,7 +3473,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.8.1 + '@types/node': 22.8.6 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -3476,17 +3482,17 @@ snapshots: jest-worker@29.7.0: dependencies: - '@types/node': 22.8.1 + '@types/node': 22.8.6 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@22.8.1)(ts-node@10.9.2(@types/node@22.8.1)(typescript@5.6.3)): + jest@29.7.0(@types/node@22.8.6)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.8.1)(typescript@5.6.3)) + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3)) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@22.8.1)(ts-node@10.9.2(@types/node@22.8.1)(typescript@5.6.3)) + jest-cli: 29.7.0(@types/node@22.8.6)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -3648,7 +3654,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.26.0 + '@babel/code-frame': 7.26.2 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -3822,16 +3828,16 @@ snapshots: dependencies: is-number: 7.0.0 - ts-api-utils@1.3.0(typescript@5.6.3): + ts-api-utils@1.4.0(typescript@5.6.3): dependencies: typescript: 5.6.3 - ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@22.8.1)(ts-node@10.9.2(@types/node@22.8.1)(typescript@5.6.3)))(typescript@5.6.3): + ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@22.8.6)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3)))(typescript@5.6.3): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@22.8.1)(ts-node@10.9.2(@types/node@22.8.1)(typescript@5.6.3)) + jest: 29.7.0(@types/node@22.8.6)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3)) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -3845,14 +3851,14 @@ snapshots: '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.26.0) - ts-node@10.9.2(@types/node@22.8.1)(typescript@5.6.3): + ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 22.8.1 + '@types/node': 22.8.6 acorn: 8.14.0 acorn-walk: 8.3.4 arg: 4.1.3 @@ -3873,11 +3879,11 @@ snapshots: type-fest@0.21.3: {} - typescript-eslint@8.12.0(eslint@9.13.0)(typescript@5.6.3): + typescript-eslint@8.12.2(eslint@9.13.0)(typescript@5.6.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.12.0(@typescript-eslint/parser@8.12.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3) - '@typescript-eslint/parser': 8.12.0(eslint@9.13.0)(typescript@5.6.3) - '@typescript-eslint/utils': 8.12.0(eslint@9.13.0)(typescript@5.6.3) + '@typescript-eslint/eslint-plugin': 8.12.2(@typescript-eslint/parser@8.12.2(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3) + '@typescript-eslint/parser': 8.12.2(eslint@9.13.0)(typescript@5.6.3) + '@typescript-eslint/utils': 8.12.2(eslint@9.13.0)(typescript@5.6.3) optionalDependencies: typescript: 5.6.3 transitivePeerDependencies: diff --git a/src/common.ts b/src/common.ts index ff97cf4c..279e5c6b 100644 --- a/src/common.ts +++ b/src/common.ts @@ -19,6 +19,13 @@ export const BASE_INDEX_MANIFEST_FILENAME = 'index.json'; export const PREV_INDEX_MANIFEST_FILENAME = 'index1.json'; export const CACHE_DIR = '.cache'; export const TMP_DIR = 'tmp'; +export const PR_ARTIFACT_DIR = 'pr'; +export const PR_DIFF_FILENAME = 'PR_DIFF'; +export const PR_ERROR_FILENAME = 'PR_ERROR'; +export const PR_NUMBER_FILENAME = 'PR_NUMBER'; +export const PR_ARTIFACT_DIFF_FILEPATH = path.join(PR_ARTIFACT_DIR, PR_DIFF_FILENAME); +export const PR_ARTIFACT_ERROR_FILEPATH = path.join(PR_ARTIFACT_DIR, PR_ERROR_FILENAME); +export const PR_ARTIFACT_NUMBER_FILEPATH = path.join(PR_ARTIFACT_DIR, PR_NUMBER_FILENAME); /** * 'ikea_new' first, to prioritize downloads from new URL */ diff --git a/src/ghw_check_ota_pr.ts b/src/ghw_check_ota_pr.ts new file mode 100644 index 00000000..bf5f8d9c --- /dev/null +++ b/src/ghw_check_ota_pr.ts @@ -0,0 +1,68 @@ +import type CoreApi from '@actions/core'; +import type {Context} from '@actions/github/lib/context'; +import type {Octokit} from '@octokit/rest'; + +import assert from 'assert'; +import {existsSync, mkdirSync, writeFileSync} from 'fs'; + +import { + BASE_INDEX_MANIFEST_FILENAME, + execute, + PR_ARTIFACT_DIFF_FILEPATH, + PR_ARTIFACT_DIR, + PR_ARTIFACT_ERROR_FILEPATH, + PR_ARTIFACT_NUMBER_FILEPATH, + PREV_INDEX_MANIFEST_FILENAME, + readManifest, + writeManifest, +} from './common.js'; +import {getChangedOtaFiles} from './ghw_get_changed_ota_files.js'; +import {processOtaFiles} from './ghw_process_ota_files.js'; + +function throwError(comment: string): void { + writeFileSync(PR_ARTIFACT_ERROR_FILEPATH, comment); + + throw new Error(comment); +} + +export async function checkOtaPR(github: Octokit, core: typeof CoreApi, context: Context): Promise { + assert(context.payload.pull_request, 'Not a pull request'); + assert(!context.payload.pull_request.merged, 'Should not be executed on a merged pull request'); + + if (!existsSync(PR_ARTIFACT_DIR)) { + mkdirSync(PR_ARTIFACT_DIR, {recursive: true}); + } + + writeFileSync(PR_ARTIFACT_NUMBER_FILEPATH, context.issue.number.toString(10), 'utf8'); + + const baseManifest = readManifest(BASE_INDEX_MANIFEST_FILENAME); + const prevManifest = readManifest(PREV_INDEX_MANIFEST_FILENAME); + + try { + const filePaths = await getChangedOtaFiles( + github, + core, + context, + `${context.payload.pull_request.base.sha}...${context.payload.pull_request.head.sha}`, + true, + ); + + await processOtaFiles(github, core, context, filePaths, baseManifest, prevManifest); + } catch (error) { + throwError((error as Error).message); + } + + writeManifest(PREV_INDEX_MANIFEST_FILENAME, prevManifest); + writeManifest(BASE_INDEX_MANIFEST_FILENAME, baseManifest); + + core.info(`Prev manifest has ${prevManifest.length} images.`); + core.info(`Base manifest has ${baseManifest.length} images.`); + + const diff = await execute(`git diff`); + + core.startGroup('diff'); + core.info(diff); + core.endGroup(); + + writeFileSync(PR_ARTIFACT_DIFF_FILEPATH, diff); +} diff --git a/src/ghw_create_autodl_release.ts b/src/ghw_create_autodl_release.ts index fb49c158..8d779f77 100644 --- a/src/ghw_create_autodl_release.ts +++ b/src/ghw_create_autodl_release.ts @@ -2,8 +2,9 @@ import type CoreApi from '@actions/core'; import type {Context} from '@actions/github/lib/context'; import type {Octokit} from '@octokit/rest'; +import type {RepoImageMeta} from './types.js'; + import {BASE_IMAGES_DIR, BASE_INDEX_MANIFEST_FILENAME, execute, PREV_IMAGES_DIR, PREV_INDEX_MANIFEST_FILENAME, readManifest} from './common.js'; -import {RepoImageMeta} from './types.js'; // about 3 lines const MAX_RELEASE_NOTES_LENGTH = 380; diff --git a/src/ghw_get_changed_ota_files.ts b/src/ghw_get_changed_ota_files.ts new file mode 100644 index 00000000..3a77d2f4 --- /dev/null +++ b/src/ghw_get_changed_ota_files.ts @@ -0,0 +1,34 @@ +import type CoreApi from '@actions/core'; +import type {Context} from '@actions/github/lib/context'; +import type {Octokit} from '@octokit/rest'; + +import assert from 'assert'; + +import {BASE_IMAGES_DIR} from './common.js'; + +export async function getChangedOtaFiles( + github: Octokit, + core: typeof CoreApi, + context: Context, + basehead: string, + isPR: boolean, +): Promise { + // NOTE: includes up to 300 files, per https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#compare-two-commits + const compare = await github.rest.repos.compareCommitsWithBasehead({ + owner: context.repo.owner, + repo: context.repo.repo, + basehead, + }); + + assert(compare.data.files && compare.data.files.length > 0, 'No file'); + + core.info(`Changed files: ${compare.data.files.map((f) => f.filename).join(', ')}`); + + const fileList = compare.data.files.filter((f) => f.filename.startsWith(`${BASE_IMAGES_DIR}/`)); + + if (isPR && fileList.length !== compare.data.files.length) { + throw new Error(`Detected changes in files outside of \`images\` directory. This is not allowed for a pull request with OTA files.`); + } + + return fileList.map((f) => f.filename); +} diff --git a/src/ghw_update_ota_pr.ts b/src/ghw_process_ota_files.ts similarity index 58% rename from src/ghw_update_ota_pr.ts rename to src/ghw_process_ota_files.ts index efe1fc17..70519954 100644 --- a/src/ghw_update_ota_pr.ts +++ b/src/ghw_process_ota_files.ts @@ -2,9 +2,8 @@ import type CoreApi from '@actions/core'; import type {Context} from '@actions/github/lib/context'; import type {Octokit} from '@octokit/rest'; -import type {ExtraMetas, GHExtraMetas} from './types'; +import type {ExtraMetas, GHExtraMetas, RepoImageMeta} from './types.js'; -import assert from 'assert'; import {readFileSync, renameSync} from 'fs'; import path from 'path'; @@ -12,8 +11,6 @@ import { addImageToBase, addImageToPrev, BASE_IMAGES_DIR, - BASE_INDEX_MANIFEST_FILENAME, - execute, findMatchImage, getOutDir, getParsedImageStatus, @@ -21,10 +18,7 @@ import { ParsedImageStatus, parseImageHeader, PREV_IMAGES_DIR, - PREV_INDEX_MANIFEST_FILENAME, - readManifest, UPGRADE_FILE_IDENTIFIER, - writeManifest, } from './common.js'; const EXTRA_METAS_PR_BODY_START_TAG = '```json'; @@ -75,97 +69,46 @@ async function parsePRBodyExtraMetas(github: Octokit, core: typeof CoreApi, cont } } } catch (error) { - const failureComment = `Invalid extra metas in pull request body: ` + (error as Error).message; - - core.error(failureComment); - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body: failureComment, - }); - - throw new Error(failureComment); + throw new Error(`Invalid extra metas in pull request body: ${(error as Error).message}`); } } return extraMetas; } -export async function updateOtaPR(github: Octokit, core: typeof CoreApi, context: Context, fileParam: string): Promise { - assert(fileParam, 'No file found in pull request.'); - assert(context.payload.pull_request, 'Not a pull request'); - - const fileParamArr = fileParam.trim().split(','); - // take care of empty strings (GH workflow adds a comma at end), ignore files not stored in images dir - const fileList = fileParamArr.filter((f) => f.startsWith(`${BASE_IMAGES_DIR}/`)); - - assert(fileList.length > 0, 'No image found in pull request.'); - core.info(`Images in pull request: ${fileList}.`); - - const fileListWrongDir = fileParamArr.filter((f) => f.startsWith(`${PREV_IMAGES_DIR}/`)); - - if (fileListWrongDir.length > 0) { - const failureComment = `Detected files in 'images1': -\`\`\` -${fileListWrongDir.join('\n')} -\`\`\` -Please move all files to 'images' (in appropriate subfolders). The pull request will automatically determine the proper location on merge.`; - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body: failureComment, - }); - - throw new Error(failureComment); - } - - const fileListNoIndex = fileParamArr.filter((f) => f.startsWith(BASE_INDEX_MANIFEST_FILENAME) || f.startsWith(PREV_INDEX_MANIFEST_FILENAME)); - - if (fileListNoIndex.length > 0) { - const failureComment = `Detected manual changes in ${fileListNoIndex.join(', ')}. Please remove these changes. The pull request will automatically determine the manifests on merge.`; - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body: failureComment, - }); - - throw new Error(failureComment); - } - - // called at the top, fail early if invalid PR body metas +export async function processOtaFiles( + github: Octokit, + core: typeof CoreApi, + context: Context, + filePaths: string[], + baseManifest: RepoImageMeta[], + prevManifest: RepoImageMeta[], +): Promise { const extraMetas = await parsePRBodyExtraMetas(github, core, context); - const baseManifest = readManifest(BASE_INDEX_MANIFEST_FILENAME); - const prevManifest = readManifest(PREV_INDEX_MANIFEST_FILENAME); - for (const file of fileList) { - core.startGroup(file); - core.info(`Processing '${file}'...`); + for (const filePath of filePaths) { + core.startGroup(filePath); + const logPrefix = `[${filePath}]`; let failureComment: string = ''; try { - const firmwareFileName = path.basename(file); - const manufacturer = file.replace(BASE_IMAGES_DIR, '').replace(firmwareFileName, '').replaceAll('/', '').trim(); + const firmwareFileName = path.basename(filePath); + const manufacturer = filePath.replace(BASE_IMAGES_DIR, '').replace(firmwareFileName, '').replaceAll('/', '').trim(); if (!manufacturer) { - throw new Error(`\`${file}\` should be in its associated manufacturer subfolder.`); + throw new Error(`File should be in its associated manufacturer subfolder`); } - const firmwareBuffer = Buffer.from(readFileSync(file)); + const firmwareBuffer = Buffer.from(readFileSync(filePath)); const parsedImage = parseImageHeader(firmwareBuffer.subarray(firmwareBuffer.indexOf(UPGRADE_FILE_IDENTIFIER))); - core.info(`[${file}] Parsed image header:`); + core.info(`${logPrefix} Parsed image header:`); core.info(JSON.stringify(parsedImage, undefined, 2)); const fileExtraMetas = getFileExtraMetas(extraMetas, firmwareFileName); - core.info(`[${file}] Extra metas:`); + core.info(`${logPrefix} Extra metas:`); core.info(JSON.stringify(fileExtraMetas, undefined, 2)); const baseOutDir = getOutDir(manufacturer, BASE_IMAGES_DIR); @@ -200,7 +143,7 @@ ${JSON.stringify(parsedImage, undefined, 2)} case ParsedImageStatus.NEWER: case ParsedImageStatus.NEW: { addImageToPrev( - `[${file}]`, + logPrefix, statusToPrev === ParsedImageStatus.NEWER, prevManifest, prevMatchIndex, @@ -214,7 +157,7 @@ ${JSON.stringify(parsedImage, undefined, 2)} fileExtraMetas, () => { // relocate file to prev - renameSync(file, file.replace(`${BASE_IMAGES_DIR}/`, `${PREV_IMAGES_DIR}/`)); + renameSync(filePath, filePath.replace(`${BASE_IMAGES_DIR}/`, `${PREV_IMAGES_DIR}/`)); }, ); @@ -240,7 +183,7 @@ ${JSON.stringify(parsedImage, undefined, 2)} case ParsedImageStatus.NEWER: case ParsedImageStatus.NEW: { addImageToBase( - `[${file}]`, + logPrefix, statusToBase === ParsedImageStatus.NEWER, prevManifest, prevOutDir, @@ -267,41 +210,10 @@ ${JSON.stringify(parsedImage, undefined, 2)} } if (failureComment) { - core.error(`[${file}] ` + failureComment); - await github.rest.pulls.createReviewComment({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.issue.number, - body: failureComment, - commit_id: context.payload.pull_request.head.sha, - path: file, - subject_type: 'file', - }); - - throw new Error(failureComment); + core.endGroup(); + throw new Error(`${logPrefix} ${failureComment}`); } core.endGroup(); } - - writeManifest(PREV_INDEX_MANIFEST_FILENAME, prevManifest); - writeManifest(BASE_INDEX_MANIFEST_FILENAME, baseManifest); - - core.info(`Prev manifest has ${prevManifest.length} images.`); - core.info(`Base manifest has ${baseManifest.length} images.`); - - if (!context.payload.pull_request.merged) { - const diff = await execute(`git diff`); - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body: `Merging this pull request will add these changes in a following commit: -\`\`\`diff -${diff} -\`\`\` -`, - }); - } } diff --git a/src/ghw_report_ota_pr.ts b/src/ghw_report_ota_pr.ts new file mode 100644 index 00000000..f90f8167 --- /dev/null +++ b/src/ghw_report_ota_pr.ts @@ -0,0 +1,82 @@ +import type CoreApi from '@actions/core'; +import type {Context} from '@actions/github/lib/context'; +import type {Octokit} from '@octokit/rest'; + +import assert from 'assert'; +import {existsSync, readFileSync, writeFileSync} from 'fs'; + +import {execute, PR_ARTIFACT_DIR, PR_DIFF_FILENAME, PR_ERROR_FILENAME, PR_NUMBER_FILENAME} from './common.js'; + +export async function reportOtaPR(github: Octokit, core: typeof CoreApi, context: Context): Promise { + assert(context.payload.workflow_run, 'Not a workflow run'); + + // XXX: context.payload.workflow_run is not typed... + const workflow_run = context.payload.workflow_run as Awaited>['data']; + + // workflow_run.conclusion: action_required, cancelled, failure, neutral, skipped, stale, success, timed_out, startup_failure, null + if (workflow_run.conclusion !== 'success' && workflow_run.conclusion !== 'failure') { + core.info(`Ignoring workflow run ${workflow_run.html_url} with conclusion ${workflow_run.conclusion}.`); + + return; + } + + const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: workflow_run.id, + }); + const matchArtifact = artifacts.data.artifacts.find((artifact) => artifact.name == PR_ARTIFACT_DIR); + + assert(matchArtifact, `No artifact found for ${workflow_run.url}`); + + const download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + const artifactZipFileName = `${PR_ARTIFACT_DIR}.zip`; + + writeFileSync(artifactZipFileName, Buffer.from(download.data as ArrayBuffer)); + + const unzipOutput = await execute(`unzip ${artifactZipFileName}`); + + core.info(unzipOutput); + + assert(existsSync(PR_NUMBER_FILENAME), `Invalid artifact for ${workflow_run.html_url}`); + + const prNumber = parseInt(readFileSync(PR_NUMBER_FILENAME, 'utf8'), 10); + + core.info(`Running for pr#${prNumber} for ${workflow_run.html_url}`); + + if (workflow_run.conclusion === 'failure') { + assert(existsSync(PR_ERROR_FILENAME), `Workflow failed but could not find ${PR_ERROR_FILENAME} for ${workflow_run.html_url}`); + + const prError = readFileSync(PR_ERROR_FILENAME, 'utf8'); + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: prError, + }); + + throw new Error(prError); + } else if (workflow_run.conclusion === 'success') { + assert(existsSync(PR_DIFF_FILENAME), `Workflow succeeded but could not find ${PR_DIFF_FILENAME} for ${workflow_run.html_url}`); + + const prDiff = readFileSync(PR_DIFF_FILENAME, 'utf8'); + + core.info(prDiff); + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: `Merging this pull request will add these changes in a following commit: +\`\`\`diff +${prDiff} +\`\`\` +`, + }); + } +} diff --git a/src/ghw_update_manifests.ts b/src/ghw_update_manifests.ts new file mode 100644 index 00000000..14e0ecf4 --- /dev/null +++ b/src/ghw_update_manifests.ts @@ -0,0 +1,26 @@ +import type CoreApi from '@actions/core'; +import type {Context} from '@actions/github/lib/context'; +import type {Octokit} from '@octokit/rest'; + +import assert from 'assert'; + +import {BASE_INDEX_MANIFEST_FILENAME, PREV_INDEX_MANIFEST_FILENAME, readManifest, writeManifest} from './common.js'; +import {getChangedOtaFiles} from './ghw_get_changed_ota_files.js'; +import {processOtaFiles} from './ghw_process_ota_files.js'; + +export async function updateManifests(github: Octokit, core: typeof CoreApi, context: Context): Promise { + assert(context.eventName === 'push', 'Not a push'); + + const filePaths = await getChangedOtaFiles(github, core, context, `${context.payload.before}...${context.payload.after}`, false); + const baseManifest = readManifest(BASE_INDEX_MANIFEST_FILENAME); + const prevManifest = readManifest(PREV_INDEX_MANIFEST_FILENAME); + + // will throw if anything goes wrong + await processOtaFiles(github, core, context, filePaths, baseManifest, prevManifest); + + writeManifest(PREV_INDEX_MANIFEST_FILENAME, prevManifest); + writeManifest(BASE_INDEX_MANIFEST_FILENAME, baseManifest); + + core.info(`Prev manifest has ${prevManifest.length} images.`); + core.info(`Base manifest has ${baseManifest.length} images.`); +} diff --git a/src/index.ts b/src/index.ts index 1ff5d22c..c0dfafbf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,13 @@ export * as common from './common.js'; +export {checkOtaPR} from './ghw_check_ota_pr.js'; export {concatCaCerts} from './ghw_concat_cacerts.js'; export {createAutodlRelease} from './ghw_create_autodl_release.js'; export {createPRToDefault} from './ghw_create_pr_to_default.js'; +export {getChangedOtaFiles} from './ghw_get_changed_ota_files.js'; export {overwriteCache} from './ghw_overwrite_cache.js'; +export {processOtaFiles} from './ghw_process_ota_files.js'; +export {reportOtaPR} from './ghw_report_ota_pr.js'; export {reProcessAllImages} from './ghw_reprocess_all_images.js'; export {runAutodl} from './ghw_run_autodl.js'; -export {updateOtaPR} from './ghw_update_ota_pr.js'; +export {updateManifests} from './ghw_update_manifests.js'; export {processFirmwareImage} from './process_firmware_image.js'; diff --git a/tests/data.test.ts b/tests/data.test.ts index ddec426b..e67a617c 100644 --- a/tests/data.test.ts +++ b/tests/data.test.ts @@ -202,7 +202,7 @@ export const getImageOriginalDirPath = (imageName: string): string => { return path.join('tests', common.BASE_IMAGES_DIR, imageName); }; -export const useImage = (imageName: string, outDir: string = BASE_IMAGES_TEST_DIR_PATH): string => { +export const useImage = (imageName: string, outDir: string = BASE_IMAGES_TEST_DIR_PATH): {filename: string} => { const realPath = path.join(outDir, imageName); if (!existsSync(outDir)) { @@ -212,7 +212,7 @@ export const useImage = (imageName: string, outDir: string = BASE_IMAGES_TEST_DI copyFileSync(getImageOriginalDirPath(imageName), realPath); // return as posix for github match - return path.posix.join(BASE_IMAGES_TEST_DIR_PATH.replaceAll('\\', '/'), imageName); + return {filename: path.posix.join(outDir.replaceAll('\\', '/'), imageName)}; }; export const withExtraMetas = (meta: RepoImageMeta, extraMetas: ExtraMetas): RepoImageMeta => { diff --git a/tests/ghw_update_ota_pr.test.ts b/tests/ghw_check_ota_pr.test.ts similarity index 77% rename from tests/ghw_update_ota_pr.test.ts rename to tests/ghw_check_ota_pr.test.ts index 3439165d..e72746ac 100644 --- a/tests/ghw_update_ota_pr.test.ts +++ b/tests/ghw_check_ota_pr.test.ts @@ -4,10 +4,11 @@ import type {Octokit} from '@octokit/rest'; import type {RepoImageMeta} from '../src/types'; -import {rmSync} from 'fs'; +import {existsSync, readFileSync, rmSync} from 'fs'; +import path from 'path'; import * as common from '../src/common'; -import {updateOtaPR} from '../src/ghw_update_ota_pr'; +import {checkOtaPR} from '../src/ghw_check_ota_pr'; import { BASE_IMAGES_TEST_DIR_PATH, getAdjustedContent, @@ -27,17 +28,10 @@ import { const github = { rest: { - issues: { - createComment: jest.fn< - ReturnType, - Parameters, - unknown - >(), - }, - pulls: { - createReviewComment: jest.fn< - ReturnType, - Parameters, + repos: { + compareCommitsWithBasehead: jest.fn< + ReturnType, + Parameters, unknown >(), }, @@ -59,6 +53,9 @@ const context: Partial = { head: { sha: 'abcd', }, + base: { + sha: 'zyxw', + }, }, }, issue: { @@ -72,13 +69,14 @@ const context: Partial = { }, }; -describe('Github Workflow: Update OTA PR', () => { +describe('Github Workflow: Check OTA PR', () => { let baseManifest: RepoImageMeta[]; let prevManifest: RepoImageMeta[]; let readManifestSpy: jest.SpyInstance; let writeManifestSpy: jest.SpyInstance; let addImageToBaseSpy: jest.SpyInstance; let addImageToPrevSpy: jest.SpyInstance; + let filePaths: ReturnType[] = []; const getManifest = (fileName: string): RepoImageMeta[] => { if (fileName === common.BASE_INDEX_MANIFEST_FILENAME) { @@ -141,15 +139,21 @@ describe('Github Workflow: Update OTA PR', () => { beforeEach(() => { resetManifests(); + filePaths = []; readManifestSpy = jest.spyOn(common, 'readManifest').mockImplementation(getManifest); writeManifestSpy = jest.spyOn(common, 'writeManifest').mockImplementation(setManifest); addImageToBaseSpy = jest.spyOn(common, 'addImageToBase'); addImageToPrevSpy = jest.spyOn(common, 'addImageToPrev'); + github.rest.repos.compareCommitsWithBasehead.mockImplementation( + // @ts-expect-error mock + () => ({data: {files: filePaths}}), + ); }); afterEach(() => { rmSync(BASE_IMAGES_TEST_DIR_PATH, {recursive: true, force: true}); rmSync(PREV_IMAGES_TEST_DIR_PATH, {recursive: true, force: true}); + rmSync(common.PR_ARTIFACT_DIR, {recursive: true, force: true}); }); // XXX: Util @@ -162,128 +166,125 @@ describe('Github Workflow: Update OTA PR', () => { // }) it('hard failure from outside PR context', async () => { - const fileParam: string = `images/test.ota`; + filePaths = [useImage(IMAGE_V14_1)]; await expect(async () => { // @ts-expect-error mock - await updateOtaPR(github, core, {payload: {}}, fileParam); + await checkOtaPR(github, core, {payload: {}}); }).rejects.toThrow(`Not a pull request`); expectNoChanges(true); }); - it('hard failure without fileParam', async () => { - // NOTE: this path should always be prevented by workflow `paths` filter - const fileParam: string = ''; + it('hard failure from merged PR context', async () => { + filePaths = [useImage(IMAGE_V14_1)]; await expect(async () => { // @ts-expect-error mock - await updateOtaPR(github, core, context, fileParam); - }).rejects.toThrow(`No file found in pull request.`); + await checkOtaPR(github, core, {payload: {pull_request: {merged: true}}}); + }).rejects.toThrow(`Should not be executed on a merged pull request`); expectNoChanges(true); }); - it('failure with images in images1', async () => { - const fileParam: string = `images1/test2.ota,images/test.ota`; + it('hard failure with no file changed', async () => { + filePaths = []; await expect(async () => { // @ts-expect-error mock - await updateOtaPR(github, core, context, fileParam); - }).rejects.toThrow(expect.objectContaining({message: expect.stringContaining(`Detected files in 'images1'`)})); + await checkOtaPR(github, core, context); + }).rejects.toThrow(`No file`); - expectNoChanges(true); - expect(github.rest.issues.createComment).toHaveBeenCalledTimes(1); + expectNoChanges(false); + expect(existsSync(common.PR_ARTIFACT_NUMBER_FILEPATH)).toStrictEqual(true); + expect(readFileSync(common.PR_ARTIFACT_NUMBER_FILEPATH, 'utf8')).toStrictEqual(`${context.payload?.pull_request?.number}`); + expect(existsSync(common.PR_ARTIFACT_DIFF_FILEPATH)).toStrictEqual(false); + expect(existsSync(common.PR_ARTIFACT_ERROR_FILEPATH)).toStrictEqual(true); + expect(readFileSync(common.PR_ARTIFACT_ERROR_FILEPATH, 'utf8')).toStrictEqual(`No file`); }); - it('failure with edited manifest', async () => { - const fileParam: string = `index.json,images/test.ota`; + it('failure with file outside of images directory', async () => { + filePaths = [useImage(IMAGE_V13_1, PREV_IMAGES_TEST_DIR_PATH), useImage(IMAGE_V14_1)]; await expect(async () => { // @ts-expect-error mock - await updateOtaPR(github, core, context, fileParam); - }).rejects.toThrow(expect.objectContaining({message: expect.stringContaining(`Detected manual changes in index.json`)})); + await checkOtaPR(github, core, context); + }).rejects.toThrow(expect.objectContaining({message: expect.stringContaining(`Detected changes in files outside`)})); - expectNoChanges(true); - expect(github.rest.issues.createComment).toHaveBeenCalledTimes(1); + expectNoChanges(false); }); - it('failure when no subfolder (manufacturer)', async () => { - const fileParam: string = `images/test.ota`; + it('failure when no manufacturer subfolder', async () => { + filePaths = [useImage(IMAGE_V14_1, common.BASE_IMAGES_DIR)]; await expect(async () => { // @ts-expect-error mock - await updateOtaPR(github, core, context, fileParam); - }).rejects.toThrow( - expect.objectContaining({message: expect.stringContaining(`\`images/test.ota\` should be in its associated manufacturer subfolder.`)}), - ); + await checkOtaPR(github, core, context); + }).rejects.toThrow(expect.objectContaining({message: expect.stringContaining(`File should be in its associated manufacturer subfolder`)})); expectNoChanges(false); - expect(github.rest.pulls.createReviewComment).toHaveBeenCalledTimes(1); + + rmSync(path.join(common.BASE_IMAGES_DIR, IMAGE_V14_1), {force: true}); }); it('failure with invalid OTA file', async () => { - const fileParam: string = useImage(IMAGE_INVALID); + filePaths = [useImage(IMAGE_INVALID)]; await expect(async () => { // @ts-expect-error mock - await updateOtaPR(github, core, context, fileParam); + await checkOtaPR(github, core, context); }).rejects.toThrow(expect.objectContaining({message: expect.stringContaining(`Not a valid OTA file`)})); expectNoChanges(false); - expect(github.rest.pulls.createReviewComment).toHaveBeenCalledTimes(1); }); it('failure with identical OTA file', async () => { setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]); - const fileParam: string = useImage(IMAGE_V14_1); + filePaths = [useImage(IMAGE_V14_1)]; await expect(async () => { // @ts-expect-error mock - await updateOtaPR(github, core, context, fileParam); + await checkOtaPR(github, core, context); }).rejects.toThrow(expect.objectContaining({message: expect.stringContaining(`Conflict with image at index \`0\``)})); expectNoChanges(false); - expect(github.rest.pulls.createReviewComment).toHaveBeenCalledTimes(1); }); it('failure with older OTA file that has identical in prev', async () => { setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]); setManifest(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V13_1_METAS]); - const fileParam: string = useImage(IMAGE_V13_1); + filePaths = [useImage(IMAGE_V13_1)]; await expect(async () => { // @ts-expect-error mock - await updateOtaPR(github, core, context, fileParam); + await checkOtaPR(github, core, context); }).rejects.toThrow( expect.objectContaining({message: expect.stringContaining(`an equal or better match is already present in prev manifest`)}), ); expectNoChanges(false); - expect(github.rest.pulls.createReviewComment).toHaveBeenCalledTimes(1); }); it('failure with older OTA file that has newer in prev', async () => { setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]); setManifest(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V13_1_METAS]); - const fileParam: string = useImage(IMAGE_V12_1); + filePaths = [useImage(IMAGE_V12_1)]; await expect(async () => { // @ts-expect-error mock - await updateOtaPR(github, core, context, fileParam); + await checkOtaPR(github, core, context); }).rejects.toThrow( expect.objectContaining({message: expect.stringContaining(`an equal or better match is already present in prev manifest`)}), ); expectNoChanges(false); - expect(github.rest.pulls.createReviewComment).toHaveBeenCalledTimes(1); }); it('success into base', async () => { - const fileParam: string = useImage(IMAGE_V14_1); + filePaths = [useImage(IMAGE_V14_1)]; // @ts-expect-error mock - await updateOtaPR(github, core, context, fileParam); + await checkOtaPR(github, core, context); expect(readManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME); expect(readManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME); @@ -291,15 +292,19 @@ describe('Github Workflow: Update OTA PR', () => { expect(addImageToPrevSpy).toHaveBeenCalledTimes(0); expect(writeManifestSpy).toHaveBeenCalledTimes(2); expect(writeManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]); + expect(existsSync(common.PR_ARTIFACT_NUMBER_FILEPATH)).toStrictEqual(true); + expect(readFileSync(common.PR_ARTIFACT_NUMBER_FILEPATH, 'utf8')).toStrictEqual(`${context.payload?.pull_request?.number}`); + expect(existsSync(common.PR_ARTIFACT_DIFF_FILEPATH)).toStrictEqual(true); + expect(existsSync(common.PR_ARTIFACT_ERROR_FILEPATH)).toStrictEqual(false); }); it('success into prev', async () => { setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V14_1_METAS]); - const fileParam: string = useImage(IMAGE_V13_1); + filePaths = [useImage(IMAGE_V13_1)]; // @ts-expect-error mock - await updateOtaPR(github, core, context, fileParam); + await checkOtaPR(github, core, context); expect(readManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME); expect(readManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME); @@ -310,10 +315,10 @@ describe('Github Workflow: Update OTA PR', () => { }); it('success with newer than current without existing prev', async () => { - const fileParam: string = [useImage(IMAGE_V13_1), useImage(IMAGE_V14_1)].join(','); + filePaths = [useImage(IMAGE_V13_1), useImage(IMAGE_V14_1)]; // @ts-expect-error mock - await updateOtaPR(github, core, context, fileParam); + await checkOtaPR(github, core, context); expect(readManifestSpy).toHaveBeenCalledTimes(2); expect(addImageToBaseSpy).toHaveBeenCalledTimes(2); // adds both, relocates first during second processing @@ -324,10 +329,10 @@ describe('Github Workflow: Update OTA PR', () => { }); it('success with newer than current with existing prev', async () => { - const fileParam: string = [useImage(IMAGE_V12_1), useImage(IMAGE_V13_1), useImage(IMAGE_V14_1)].join(','); + filePaths = [useImage(IMAGE_V12_1), useImage(IMAGE_V13_1), useImage(IMAGE_V14_1)]; // @ts-expect-error mock - await updateOtaPR(github, core, context, fileParam); + await checkOtaPR(github, core, context); expect(readManifestSpy).toHaveBeenCalledTimes(2); expect(addImageToBaseSpy).toHaveBeenCalledTimes(3); // adds both, relocates first during second processing @@ -339,10 +344,10 @@ describe('Github Workflow: Update OTA PR', () => { it('success with older that is newer than prev', async () => { setManifest(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V12_1_METAS]); - const fileParam: string = [useImage(IMAGE_V14_1), useImage(IMAGE_V13_1)].join(','); + filePaths = [useImage(IMAGE_V14_1), useImage(IMAGE_V13_1)]; // @ts-expect-error mock - await updateOtaPR(github, core, context, fileParam); + await checkOtaPR(github, core, context); expect(readManifestSpy).toHaveBeenCalledTimes(2); expect(addImageToBaseSpy).toHaveBeenCalledTimes(1); @@ -354,10 +359,10 @@ describe('Github Workflow: Update OTA PR', () => { it('success with newer with missing file', async () => { setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [IMAGE_V13_1_METAS]); - const fileParam: string = [useImage(IMAGE_V14_1)].join(','); + filePaths = [useImage(IMAGE_V14_1)]; // @ts-expect-error mock - await updateOtaPR(github, core, context, fileParam); + await checkOtaPR(github, core, context); expect(readManifestSpy).toHaveBeenCalledTimes(2); expect(addImageToBaseSpy).toHaveBeenCalledTimes(1); @@ -368,10 +373,10 @@ describe('Github Workflow: Update OTA PR', () => { }); it('success with multiple different files', async () => { - const fileParam: string = [useImage(IMAGE_V14_2), useImage(IMAGE_V14_1)].join(','); + filePaths = [useImage(IMAGE_V14_2), useImage(IMAGE_V14_1)]; // @ts-expect-error mock - await updateOtaPR(github, core, context, fileParam); + await checkOtaPR(github, core, context); expect(readManifestSpy).toHaveBeenCalledTimes(2); expect(addImageToBaseSpy).toHaveBeenCalledTimes(2); // adds both, relocates first during second processing @@ -382,11 +387,11 @@ describe('Github Workflow: Update OTA PR', () => { }); it('success with extra metas', async () => { - const fileParam: string = useImage(IMAGE_V14_1); + filePaths = [useImage(IMAGE_V14_1)]; const newContext = withBody(`Text before start tag \`\`\`json {"manufacturerName": ["lixee"]} \`\`\` Text after end tag`); // @ts-expect-error mock - await updateOtaPR(github, core, newContext, fileParam); + await checkOtaPR(github, core, newContext); expect(readManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME); expect(readManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME); @@ -399,7 +404,7 @@ describe('Github Workflow: Update OTA PR', () => { }); it('success with all extra metas', async () => { - const fileParam: string = useImage(IMAGE_V14_1); + filePaths = [useImage(IMAGE_V14_1)]; const newContext = withBody(`Text before start tag \`\`\`json { @@ -416,7 +421,7 @@ describe('Github Workflow: Update OTA PR', () => { Text after end tag`); // @ts-expect-error mock - await updateOtaPR(github, core, newContext, fileParam); + await checkOtaPR(github, core, newContext); expect(readManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME); expect(readManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME); @@ -438,18 +443,17 @@ Text after end tag`); }); it('failure with invalid extra metas', async () => { - const fileParam: string = useImage(IMAGE_V14_1); + filePaths = [useImage(IMAGE_V14_1)]; const newContext = withBody(`Text before start tag \`\`\`json {"manufacturerName": "myManuf"} \`\`\` Text after end tag`); await expect(async () => { // @ts-expect-error mock - await updateOtaPR(github, core, newContext, fileParam); + await checkOtaPR(github, core, newContext); }).rejects.toThrow( expect.objectContaining({message: expect.stringContaining(`Invalid format for 'manufacturerName', expected 'array of string' type.`)}), ); - expectNoChanges(true); - expect(github.rest.issues.createComment).toHaveBeenCalledTimes(1); + expectNoChanges(false); }); it.each([ @@ -464,21 +468,20 @@ Text after end tag`); ['modelId'], ['releaseNotes'], ])('failure with invalid type for extra meta %s', async (metaName) => { - const fileParam: string = useImage(IMAGE_V14_1); + filePaths = [useImage(IMAGE_V14_1)]; // use object since no value type is ever expected to be object const newContext = withBody(`Text before start tag \`\`\`json {"${metaName}": {}} \`\`\` Text after end tag`); await expect(async () => { // @ts-expect-error mock - await updateOtaPR(github, core, newContext, fileParam); + await checkOtaPR(github, core, newContext); }).rejects.toThrow(expect.objectContaining({message: expect.stringContaining(`Invalid format for '${metaName}'`)})); - expectNoChanges(true); - expect(github.rest.issues.createComment).toHaveBeenCalledTimes(1); + expectNoChanges(false); }); it('success with multiple files and specific extra metas', async () => { - const fileParam: string = [useImage(IMAGE_V13_1), useImage(IMAGE_V14_1)].join(','); + filePaths = [useImage(IMAGE_V13_1), useImage(IMAGE_V14_1)]; const newContext = withBody(`Text before start tag \`\`\`json [ @@ -489,7 +492,7 @@ Text after end tag`); Text after end tag`); // @ts-expect-error mock - await updateOtaPR(github, core, newContext, fileParam); + await checkOtaPR(github, core, newContext); expect(readManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME); expect(readManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME); @@ -505,7 +508,7 @@ Text after end tag`); }); it('success with multiple files and specific extra metas, ignore without fileName', async () => { - const fileParam: string = [useImage(IMAGE_V13_1), useImage(IMAGE_V14_1)].join(','); + filePaths = [useImage(IMAGE_V13_1), useImage(IMAGE_V14_1)]; const newContext = withBody(`Text before start tag \`\`\`json [ @@ -516,7 +519,7 @@ Text after end tag`); Text after end tag`); // @ts-expect-error mock - await updateOtaPR(github, core, newContext, fileParam); + await checkOtaPR(github, core, newContext); expect(readManifestSpy).toHaveBeenCalledWith(common.BASE_INDEX_MANIFEST_FILENAME); expect(readManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME); diff --git a/tests/ghw_report_ota_pr.test.ts b/tests/ghw_report_ota_pr.test.ts new file mode 100644 index 00000000..ddb72b30 --- /dev/null +++ b/tests/ghw_report_ota_pr.test.ts @@ -0,0 +1,29 @@ +// import type CoreApi from '@actions/core'; +// import type {Context} from '@actions/github/lib/context'; + +import type {Octokit} from '@octokit/rest'; + +const github = { + rest: { + issues: { + createComment: jest.fn< + ReturnType, + Parameters, + unknown + >(), + }, + pulls: { + createReviewComment: jest.fn< + ReturnType, + Parameters, + unknown + >(), + }, + }, +}; + +describe('Github Workflow: Check OTA PR', () => { + it('passes', async () => { + console.log(github); + }); +}); diff --git a/tests/ghw_reprocess_all_images.test.ts b/tests/ghw_reprocess_all_images.test.ts index 2f3f1f75..c53a3462 100644 --- a/tests/ghw_reprocess_all_images.test.ts +++ b/tests/ghw_reprocess_all_images.test.ts @@ -245,7 +245,7 @@ describe('Github Workflow: Re-Process All Images', () => { // @ts-expect-error mocked as needed await reProcessAllImages(github, core, context, true, true); - expect(existsSync(imagePath)).toStrictEqual(false); + expect(existsSync(imagePath.filename)).toStrictEqual(false); expect(readManifestSpy).toHaveBeenCalledTimes(2); expect(writeManifestSpy).toHaveBeenCalledTimes(2); expectWriteNoChange(1, common.PREV_INDEX_MANIFEST_FILENAME); @@ -261,9 +261,9 @@ describe('Github Workflow: Re-Process All Images', () => { // @ts-expect-error mocked as needed await reProcessAllImages(github, core, context, true, true); - expect(existsSync(image1Path)).toStrictEqual(false); - expect(existsSync(image2Path)).toStrictEqual(false); - expect(existsSync(image3Path)).toStrictEqual(false); + expect(existsSync(image1Path.filename)).toStrictEqual(false); + expect(existsSync(image2Path.filename)).toStrictEqual(false); + expect(existsSync(image3Path.filename)).toStrictEqual(false); expect(readManifestSpy).toHaveBeenCalledTimes(2); expect(writeManifestSpy).toHaveBeenCalledTimes(2); expectWriteNoChange(1, common.PREV_INDEX_MANIFEST_FILENAME); @@ -281,7 +281,7 @@ describe('Github Workflow: Re-Process All Images', () => { await reProcessAllImages(github, core, context, false, true); const newPath = path.join(NOT_IN_BASE_MANIFEST_IMAGE_DIR_PATH, IMAGE_V12_1); - expect(existsSync(oldPath)).toStrictEqual(false); + expect(existsSync(oldPath.filename)).toStrictEqual(false); expect(existsSync(newPath)).toStrictEqual(true); expect(readManifestSpy).toHaveBeenCalledTimes(2); expect(writeManifestSpy).toHaveBeenCalledTimes(3); @@ -302,11 +302,11 @@ describe('Github Workflow: Re-Process All Images', () => { const newPath2 = path.join(NOT_IN_BASE_MANIFEST_IMAGE_DIR_PATH, IMAGE_V12_1); const newPath3 = path.join(NOT_IN_PREV_MANIFEST_IMAGE_DIR_PATH, IMAGE_V12_1); expect(existsSync(newPath1)).toStrictEqual(true); - expect(existsSync(oldPath1)).toStrictEqual(false); + expect(existsSync(oldPath1.filename)).toStrictEqual(false); expect(existsSync(newPath2)).toStrictEqual(true); - expect(existsSync(oldPath2)).toStrictEqual(false); + expect(existsSync(oldPath2.filename)).toStrictEqual(false); expect(existsSync(newPath3)).toStrictEqual(true); - expect(existsSync(oldPath3)).toStrictEqual(false); + expect(existsSync(oldPath3.filename)).toStrictEqual(false); expect(readManifestSpy).toHaveBeenCalledTimes(2); expect(writeManifestSpy).toHaveBeenCalledTimes(4); expect(writeManifestSpy).toHaveBeenNthCalledWith(1, NOT_IN_PREV_MANIFEST_FILEPATH, expect.any(Array)); @@ -321,7 +321,7 @@ describe('Github Workflow: Re-Process All Images', () => { // @ts-expect-error mocked as needed await reProcessAllImages(github, core, context, false, true); - expect(existsSync(oldPath)).toStrictEqual(false); + expect(existsSync(oldPath.filename)).toStrictEqual(false); expect(readManifestSpy).toHaveBeenCalledTimes(2); expect(writeManifestSpy).toHaveBeenCalledTimes(2); expect(writeManifestSpy).toHaveBeenNthCalledWith(1, common.PREV_INDEX_MANIFEST_FILENAME, []); @@ -338,7 +338,7 @@ describe('Github Workflow: Re-Process All Images', () => { // @ts-expect-error mocked as needed await reProcessAllImages(github, core, context, true, true); - expect(existsSync(oldPath)).toStrictEqual(false); + expect(existsSync(oldPath.filename)).toStrictEqual(false); expect(readManifestSpy).toHaveBeenCalledTimes(2); expect(writeManifestSpy).toHaveBeenCalledTimes(2); expect(writeManifestSpy).toHaveBeenNthCalledWith(1, common.PREV_INDEX_MANIFEST_FILENAME, []); @@ -354,7 +354,7 @@ describe('Github Workflow: Re-Process All Images', () => { // @ts-expect-error mocked as needed await reProcessAllImages(github, core, context, true, true); - expect(existsSync(imagePath)).toStrictEqual(true); + expect(existsSync(imagePath.filename)).toStrictEqual(true); expect(readManifestSpy).toHaveBeenCalledTimes(2); expect(writeManifestSpy).toHaveBeenCalledTimes(2); expect(writeManifestSpy).toHaveBeenNthCalledWith(1, common.PREV_INDEX_MANIFEST_FILENAME, []); @@ -369,9 +369,9 @@ describe('Github Workflow: Re-Process All Images', () => { oldMetas.url = baseUrl + escape(newName); setManifest(common.BASE_INDEX_MANIFEST_FILENAME, [oldMetas]); const imagePath = useImage(IMAGE_V14_1, BASE_IMAGES_TEST_DIR_PATH); - const baseName = path.basename(imagePath); - const renamedPath = imagePath.replace(baseName, newName); - renameSync(imagePath, renamedPath); + const baseName = path.basename(imagePath.filename); + const renamedPath = imagePath.filename.replace(baseName, newName); + renameSync(imagePath.filename, renamedPath); console.log(newName, oldMetas.url, renamedPath); // @ts-expect-error mocked as needed @@ -399,7 +399,7 @@ describe('Github Workflow: Re-Process All Images', () => { // @ts-expect-error mocked as needed await reProcessAllImages(github, core, context, true, true); - expect(existsSync(image1Path)).toStrictEqual(true); + expect(existsSync(image1Path.filename)).toStrictEqual(true); expect(readManifestSpy).toHaveBeenCalledTimes(2); expect(writeManifestSpy).toHaveBeenCalledTimes(2); expect(writeManifestSpy).toHaveBeenNthCalledWith(1, common.PREV_INDEX_MANIFEST_FILENAME, []); @@ -418,7 +418,7 @@ describe('Github Workflow: Re-Process All Images', () => { // @ts-expect-error mocked as needed await reProcessAllImages(github, core, context, true, true); - expect(existsSync(image1Path)).toStrictEqual(true); + expect(existsSync(image1Path.filename)).toStrictEqual(true); expect(readManifestSpy).toHaveBeenCalledTimes(2); expect(writeManifestSpy).toHaveBeenCalledTimes(2); expect(writeManifestSpy).toHaveBeenNthCalledWith(1, common.PREV_INDEX_MANIFEST_FILENAME, []); diff --git a/tests/jest.config.ts b/tests/jest.config.ts index eba6e626..e96ba28c 100644 --- a/tests/jest.config.ts +++ b/tests/jest.config.ts @@ -26,7 +26,13 @@ const config: JestConfigWithTsJest = { collectCoverage: false, // An array of glob patterns indicating a set of files for which coverage information should be collected - collectCoverageFrom: ['src/ghw_update_ota_pr.ts', 'src/process_firmware_image.ts', 'src/ghw_reprocess_all_images.ts'], + collectCoverageFrom: [ + 'src/ghw_check_ota_pr.ts', + 'src/ghw_get_changed_ota_files.ts', + 'src/ghw_process_ota_files.ts', + 'src/process_firmware_image.ts', + 'src/ghw_reprocess_all_images.ts', + ], // The directory where Jest should output its coverage files coverageDirectory: 'coverage', From 7af537f73d41032e91355245c87f61384c65ce36 Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Thu, 31 Oct 2024 21:30:16 +0100 Subject: [PATCH 2/2] fix --- tests/ghw_report_ota_pr.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ghw_report_ota_pr.test.ts b/tests/ghw_report_ota_pr.test.ts index ddb72b30..db4f9a2c 100644 --- a/tests/ghw_report_ota_pr.test.ts +++ b/tests/ghw_report_ota_pr.test.ts @@ -22,7 +22,7 @@ const github = { }, }; -describe('Github Workflow: Check OTA PR', () => { +describe('Github Workflow: Report OTA PR', () => { it('passes', async () => { console.log(github); });