Split workflow processes. (#588)

* Split workflow processes.

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