@elench/testkit 0.1.65 → 0.1.66
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/app/browser-bridge.mjs +66 -0
- package/lib/app/configs.mjs +81 -0
- package/lib/app/configs.test.mjs +34 -0
- package/lib/cli/command-helpers.mjs +2 -10
- package/lib/cli/commands/browser/serve.mjs +3 -62
- package/lib/cli/db.mjs +3 -68
- package/lib/config/binaries.mjs +34 -0
- package/lib/config/database.mjs +9 -6
- package/lib/config/index.mjs +2 -31
- package/lib/config/runtime.mjs +24 -95
- package/lib/config/validation.mjs +18 -62
- package/lib/coverage/backend-discovery.mjs +68 -85
- package/lib/coverage/backend-discovery.test.mjs +55 -46
- package/lib/coverage/graph-builder.mjs +5 -5
- package/lib/coverage/next-ir-to-graph.mjs +0 -1
- package/lib/coverage/routing.mjs +2 -29
- package/lib/coverage/routing.test.mjs +0 -16
- package/lib/coverage/shared.mjs +22 -82
- package/lib/database/fingerprint.mjs +1 -1
- package/lib/known-failures/github-cache.mjs +159 -0
- package/lib/known-failures/github-transport.mjs +174 -0
- package/lib/known-failures/github.mjs +17 -325
- package/lib/runner/default-runtime-runner.mjs +4 -10
- package/lib/runner/execution-config.mjs +12 -83
- package/lib/runner/live-run.mjs +45 -0
- package/lib/runner/managed-processes.mjs +29 -0
- package/lib/runner/orchestrator.mjs +57 -188
- package/lib/runner/playwright-runner.mjs +4 -11
- package/lib/runner/run-finalization.mjs +132 -0
- package/lib/runner/run-guards.mjs +45 -0
- package/lib/runner/runtime-preparation.mjs +1 -1
- package/lib/runner/services.mjs +3 -4
- package/lib/runner/template-steps.mjs +8 -45
- package/lib/runner/template.mjs +7 -28
- package/lib/shared/configured-steps.mjs +178 -0
- package/lib/shared/configured-steps.test.mjs +73 -0
- package/lib/shared/execution-schema.mjs +74 -0
- package/lib/shared/execution-schema.test.mjs +26 -0
- package/node_modules/@elench/next-analysis/dist/api-routes.d.ts +7 -0
- package/node_modules/@elench/next-analysis/dist/api-routes.d.ts.map +1 -0
- package/node_modules/@elench/next-analysis/dist/api-routes.js +66 -0
- package/node_modules/@elench/next-analysis/dist/api-routes.js.map +1 -0
- package/node_modules/@elench/next-analysis/dist/app-root.d.ts +2 -0
- package/node_modules/@elench/next-analysis/dist/app-root.d.ts.map +1 -0
- package/node_modules/@elench/next-analysis/dist/app-root.js +7 -0
- package/node_modules/@elench/next-analysis/dist/app-root.js.map +1 -0
- package/node_modules/@elench/next-analysis/dist/backend-links.d.ts +8 -0
- package/node_modules/@elench/next-analysis/dist/backend-links.d.ts.map +1 -0
- package/node_modules/@elench/next-analysis/dist/backend-links.js +30 -0
- package/node_modules/@elench/next-analysis/dist/backend-links.js.map +1 -0
- package/node_modules/@elench/next-analysis/dist/index.d.ts +11 -0
- package/node_modules/@elench/next-analysis/dist/index.d.ts.map +1 -0
- package/node_modules/@elench/next-analysis/dist/index.js +10 -0
- package/node_modules/@elench/next-analysis/dist/index.js.map +1 -0
- package/node_modules/@elench/next-analysis/dist/pages.d.ts +7 -0
- package/node_modules/@elench/next-analysis/dist/pages.d.ts.map +1 -0
- package/node_modules/@elench/next-analysis/dist/pages.js +47 -0
- package/node_modules/@elench/next-analysis/dist/pages.js.map +1 -0
- package/node_modules/@elench/next-analysis/dist/project.d.ts +3 -0
- package/node_modules/@elench/next-analysis/dist/project.d.ts.map +1 -0
- package/node_modules/@elench/next-analysis/dist/project.js +102 -0
- package/node_modules/@elench/next-analysis/dist/project.js.map +1 -0
- package/node_modules/@elench/next-analysis/dist/route-tree.d.ts +7 -0
- package/node_modules/@elench/next-analysis/dist/route-tree.d.ts.map +1 -0
- package/node_modules/@elench/next-analysis/dist/route-tree.js +575 -0
- package/node_modules/@elench/next-analysis/dist/route-tree.js.map +1 -0
- package/node_modules/@elench/next-analysis/dist/routes.d.ts +6 -0
- package/node_modules/@elench/next-analysis/dist/routes.d.ts.map +1 -0
- package/node_modules/@elench/next-analysis/dist/routes.js +41 -0
- package/node_modules/@elench/next-analysis/dist/routes.js.map +1 -0
- package/node_modules/@elench/next-analysis/dist/server-actions.d.ts +7 -0
- package/node_modules/@elench/next-analysis/dist/server-actions.d.ts.map +1 -0
- package/node_modules/@elench/next-analysis/dist/server-actions.js +37 -0
- package/node_modules/@elench/next-analysis/dist/server-actions.js.map +1 -0
- package/node_modules/@elench/next-analysis/dist/shared.d.ts +57 -0
- package/node_modules/@elench/next-analysis/dist/shared.d.ts.map +1 -0
- package/node_modules/@elench/next-analysis/dist/shared.js +229 -0
- package/node_modules/@elench/next-analysis/dist/shared.js.map +1 -0
- package/node_modules/@elench/next-analysis/dist/swc.d.ts +53 -0
- package/node_modules/@elench/next-analysis/dist/swc.d.ts.map +1 -0
- package/node_modules/@elench/next-analysis/dist/swc.js +387 -0
- package/node_modules/@elench/next-analysis/dist/swc.js.map +1 -0
- package/node_modules/@elench/next-analysis/dist/types.d.ts +125 -0
- package/node_modules/@elench/next-analysis/dist/types.d.ts.map +1 -0
- package/node_modules/@elench/next-analysis/dist/types.js +2 -0
- package/node_modules/@elench/next-analysis/dist/types.js.map +1 -0
- package/node_modules/@elench/next-analysis/package.json +15 -2
- package/node_modules/@elench/testkit-bridge/dist/index.d.ts +36 -0
- package/node_modules/@elench/testkit-bridge/dist/index.d.ts.map +1 -0
- package/node_modules/@elench/testkit-bridge/dist/index.js +538 -0
- package/node_modules/@elench/testkit-bridge/dist/index.js.map +1 -0
- package/node_modules/@elench/testkit-bridge/package.json +16 -5
- package/node_modules/@elench/testkit-protocol/dist/index.d.ts +190 -0
- package/node_modules/@elench/testkit-protocol/dist/index.d.ts.map +1 -0
- package/node_modules/@elench/testkit-protocol/dist/index.js +296 -0
- package/node_modules/@elench/testkit-protocol/dist/index.js.map +1 -0
- package/node_modules/@elench/testkit-protocol/package.json +14 -7
- package/node_modules/@elench/ts-analysis/dist/callables.d.ts +8 -0
- package/node_modules/@elench/ts-analysis/dist/callables.d.ts.map +1 -0
- package/node_modules/@elench/ts-analysis/dist/callables.js +126 -0
- package/node_modules/@elench/ts-analysis/dist/callables.js.map +1 -0
- package/node_modules/@elench/ts-analysis/dist/exports.d.ts +6 -0
- package/node_modules/@elench/ts-analysis/dist/exports.d.ts.map +1 -0
- package/node_modules/@elench/ts-analysis/dist/exports.js +70 -0
- package/node_modules/@elench/ts-analysis/dist/exports.js.map +1 -0
- package/node_modules/@elench/ts-analysis/dist/index.d.ts +10 -0
- package/node_modules/@elench/ts-analysis/dist/index.d.ts.map +1 -0
- package/node_modules/@elench/ts-analysis/{src/index.mjs → dist/index.js} +9 -14
- package/node_modules/@elench/ts-analysis/dist/index.js.map +1 -0
- package/node_modules/@elench/ts-analysis/dist/jsx.d.ts +9 -0
- package/node_modules/@elench/ts-analysis/dist/jsx.d.ts.map +1 -0
- package/node_modules/@elench/ts-analysis/dist/jsx.js +68 -0
- package/node_modules/@elench/ts-analysis/dist/jsx.js.map +1 -0
- package/node_modules/@elench/ts-analysis/dist/project.d.ts +5 -0
- package/node_modules/@elench/ts-analysis/dist/project.d.ts.map +1 -0
- package/node_modules/@elench/ts-analysis/dist/project.js +90 -0
- package/node_modules/@elench/ts-analysis/dist/project.js.map +1 -0
- package/node_modules/@elench/ts-analysis/dist/requests.d.ts +6 -0
- package/node_modules/@elench/ts-analysis/dist/requests.d.ts.map +1 -0
- package/node_modules/@elench/ts-analysis/dist/requests.js +140 -0
- package/node_modules/@elench/ts-analysis/dist/requests.js.map +1 -0
- package/node_modules/@elench/ts-analysis/dist/resolution.d.ts +4 -0
- package/node_modules/@elench/ts-analysis/dist/resolution.d.ts.map +1 -0
- package/node_modules/@elench/ts-analysis/dist/resolution.js +53 -0
- package/node_modules/@elench/ts-analysis/dist/resolution.js.map +1 -0
- package/node_modules/@elench/ts-analysis/dist/shared.d.ts +6 -0
- package/node_modules/@elench/ts-analysis/dist/shared.d.ts.map +1 -0
- package/node_modules/@elench/ts-analysis/dist/shared.js +31 -0
- package/node_modules/@elench/ts-analysis/dist/shared.js.map +1 -0
- package/node_modules/@elench/ts-analysis/dist/syntax.d.ts +7 -0
- package/node_modules/@elench/ts-analysis/dist/syntax.d.ts.map +1 -0
- package/node_modules/@elench/ts-analysis/dist/syntax.js +27 -0
- package/node_modules/@elench/ts-analysis/dist/syntax.js.map +1 -0
- package/node_modules/@elench/ts-analysis/dist/types.d.ts +58 -0
- package/node_modules/@elench/ts-analysis/dist/types.d.ts.map +1 -0
- package/node_modules/@elench/ts-analysis/dist/types.js +2 -0
- package/node_modules/@elench/ts-analysis/dist/types.js.map +1 -0
- package/node_modules/@elench/ts-analysis/package.json +18 -2
- package/node_modules/typescript/LICENSE.txt +55 -0
- package/node_modules/typescript/README.md +50 -0
- package/node_modules/typescript/SECURITY.md +41 -0
- package/node_modules/typescript/ThirdPartyNoticeText.txt +193 -0
- package/node_modules/typescript/bin/tsc +2 -0
- package/node_modules/typescript/bin/tsserver +2 -0
- package/node_modules/typescript/lib/_tsc.js +133818 -0
- package/node_modules/typescript/lib/_tsserver.js +659 -0
- package/node_modules/typescript/lib/_typingsInstaller.js +222 -0
- package/node_modules/typescript/lib/cs/diagnosticMessages.generated.json +2122 -0
- package/node_modules/typescript/lib/de/diagnosticMessages.generated.json +2122 -0
- package/node_modules/typescript/lib/es/diagnosticMessages.generated.json +2122 -0
- package/node_modules/typescript/lib/fr/diagnosticMessages.generated.json +2122 -0
- package/node_modules/typescript/lib/it/diagnosticMessages.generated.json +2122 -0
- package/node_modules/typescript/lib/ja/diagnosticMessages.generated.json +2122 -0
- package/node_modules/typescript/lib/ko/diagnosticMessages.generated.json +2122 -0
- package/node_modules/typescript/lib/lib.d.ts +22 -0
- package/node_modules/typescript/lib/lib.decorators.d.ts +384 -0
- package/node_modules/typescript/lib/lib.decorators.legacy.d.ts +22 -0
- package/node_modules/typescript/lib/lib.dom.asynciterable.d.ts +41 -0
- package/node_modules/typescript/lib/lib.dom.d.ts +39429 -0
- package/node_modules/typescript/lib/lib.dom.iterable.d.ts +571 -0
- package/node_modules/typescript/lib/lib.es2015.collection.d.ts +147 -0
- package/node_modules/typescript/lib/lib.es2015.core.d.ts +597 -0
- package/node_modules/typescript/lib/lib.es2015.d.ts +28 -0
- package/node_modules/typescript/lib/lib.es2015.generator.d.ts +77 -0
- package/node_modules/typescript/lib/lib.es2015.iterable.d.ts +605 -0
- package/node_modules/typescript/lib/lib.es2015.promise.d.ts +81 -0
- package/node_modules/typescript/lib/lib.es2015.proxy.d.ts +128 -0
- package/node_modules/typescript/lib/lib.es2015.reflect.d.ts +144 -0
- package/node_modules/typescript/lib/lib.es2015.symbol.d.ts +46 -0
- package/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts +326 -0
- package/node_modules/typescript/lib/lib.es2016.array.include.d.ts +116 -0
- package/node_modules/typescript/lib/lib.es2016.d.ts +21 -0
- package/node_modules/typescript/lib/lib.es2016.full.d.ts +23 -0
- package/node_modules/typescript/lib/lib.es2016.intl.d.ts +31 -0
- package/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts +21 -0
- package/node_modules/typescript/lib/lib.es2017.d.ts +26 -0
- package/node_modules/typescript/lib/lib.es2017.date.d.ts +31 -0
- package/node_modules/typescript/lib/lib.es2017.full.d.ts +23 -0
- package/node_modules/typescript/lib/lib.es2017.intl.d.ts +44 -0
- package/node_modules/typescript/lib/lib.es2017.object.d.ts +49 -0
- package/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts +135 -0
- package/node_modules/typescript/lib/lib.es2017.string.d.ts +45 -0
- package/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts +53 -0
- package/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts +77 -0
- package/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts +53 -0
- package/node_modules/typescript/lib/lib.es2018.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2018.full.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2018.intl.d.ts +83 -0
- package/node_modules/typescript/lib/lib.es2018.promise.d.ts +30 -0
- package/node_modules/typescript/lib/lib.es2018.regexp.d.ts +37 -0
- package/node_modules/typescript/lib/lib.es2019.array.d.ts +79 -0
- package/node_modules/typescript/lib/lib.es2019.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2019.full.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2019.intl.d.ts +23 -0
- package/node_modules/typescript/lib/lib.es2019.object.d.ts +33 -0
- package/node_modules/typescript/lib/lib.es2019.string.d.ts +37 -0
- package/node_modules/typescript/lib/lib.es2019.symbol.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2020.bigint.d.ts +765 -0
- package/node_modules/typescript/lib/lib.es2020.d.ts +27 -0
- package/node_modules/typescript/lib/lib.es2020.date.d.ts +42 -0
- package/node_modules/typescript/lib/lib.es2020.full.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2020.intl.d.ts +474 -0
- package/node_modules/typescript/lib/lib.es2020.number.d.ts +28 -0
- package/node_modules/typescript/lib/lib.es2020.promise.d.ts +47 -0
- package/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts +99 -0
- package/node_modules/typescript/lib/lib.es2020.string.d.ts +44 -0
- package/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts +41 -0
- package/node_modules/typescript/lib/lib.es2021.d.ts +23 -0
- package/node_modules/typescript/lib/lib.es2021.full.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2021.intl.d.ts +166 -0
- package/node_modules/typescript/lib/lib.es2021.promise.d.ts +48 -0
- package/node_modules/typescript/lib/lib.es2021.string.d.ts +33 -0
- package/node_modules/typescript/lib/lib.es2021.weakref.d.ts +78 -0
- package/node_modules/typescript/lib/lib.es2022.array.d.ts +121 -0
- package/node_modules/typescript/lib/lib.es2022.d.ts +25 -0
- package/node_modules/typescript/lib/lib.es2022.error.d.ts +75 -0
- package/node_modules/typescript/lib/lib.es2022.full.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2022.intl.d.ts +145 -0
- package/node_modules/typescript/lib/lib.es2022.object.d.ts +26 -0
- package/node_modules/typescript/lib/lib.es2022.regexp.d.ts +39 -0
- package/node_modules/typescript/lib/lib.es2022.string.d.ts +25 -0
- package/node_modules/typescript/lib/lib.es2023.array.d.ts +924 -0
- package/node_modules/typescript/lib/lib.es2023.collection.d.ts +21 -0
- package/node_modules/typescript/lib/lib.es2023.d.ts +22 -0
- package/node_modules/typescript/lib/lib.es2023.full.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2023.intl.d.ts +56 -0
- package/node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts +65 -0
- package/node_modules/typescript/lib/lib.es2024.collection.d.ts +29 -0
- package/node_modules/typescript/lib/lib.es2024.d.ts +26 -0
- package/node_modules/typescript/lib/lib.es2024.full.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2024.object.d.ts +29 -0
- package/node_modules/typescript/lib/lib.es2024.promise.d.ts +35 -0
- package/node_modules/typescript/lib/lib.es2024.regexp.d.ts +25 -0
- package/node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts +68 -0
- package/node_modules/typescript/lib/lib.es2024.string.d.ts +29 -0
- package/node_modules/typescript/lib/lib.es5.d.ts +4601 -0
- package/node_modules/typescript/lib/lib.es6.d.ts +23 -0
- package/node_modules/typescript/lib/lib.esnext.array.d.ts +35 -0
- package/node_modules/typescript/lib/lib.esnext.collection.d.ts +96 -0
- package/node_modules/typescript/lib/lib.esnext.d.ts +29 -0
- package/node_modules/typescript/lib/lib.esnext.decorators.d.ts +28 -0
- package/node_modules/typescript/lib/lib.esnext.disposable.d.ts +193 -0
- package/node_modules/typescript/lib/lib.esnext.error.d.ts +24 -0
- package/node_modules/typescript/lib/lib.esnext.float16.d.ts +445 -0
- package/node_modules/typescript/lib/lib.esnext.full.d.ts +24 -0
- package/node_modules/typescript/lib/lib.esnext.intl.d.ts +21 -0
- package/node_modules/typescript/lib/lib.esnext.iterator.d.ts +148 -0
- package/node_modules/typescript/lib/lib.esnext.promise.d.ts +34 -0
- package/node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts +25 -0
- package/node_modules/typescript/lib/lib.scripthost.d.ts +322 -0
- package/node_modules/typescript/lib/lib.webworker.asynciterable.d.ts +41 -0
- package/node_modules/typescript/lib/lib.webworker.d.ts +13150 -0
- package/node_modules/typescript/lib/lib.webworker.importscripts.d.ts +23 -0
- package/node_modules/typescript/lib/lib.webworker.iterable.d.ts +340 -0
- package/node_modules/typescript/lib/pl/diagnosticMessages.generated.json +2122 -0
- package/node_modules/typescript/lib/pt-br/diagnosticMessages.generated.json +2122 -0
- package/node_modules/typescript/lib/ru/diagnosticMessages.generated.json +2122 -0
- package/node_modules/typescript/lib/tr/diagnosticMessages.generated.json +2122 -0
- package/node_modules/typescript/lib/tsc.js +8 -0
- package/node_modules/typescript/lib/tsserver.js +8 -0
- package/node_modules/typescript/lib/tsserverlibrary.d.ts +17 -0
- package/node_modules/typescript/lib/tsserverlibrary.js +21 -0
- package/node_modules/typescript/lib/typesMap.json +497 -0
- package/node_modules/typescript/lib/typescript.d.ts +11437 -0
- package/node_modules/typescript/lib/typescript.js +200276 -0
- package/node_modules/typescript/lib/typingsInstaller.js +8 -0
- package/node_modules/typescript/lib/watchGuard.js +53 -0
- package/node_modules/typescript/lib/zh-cn/diagnosticMessages.generated.json +2122 -0
- package/node_modules/typescript/lib/zh-tw/diagnosticMessages.generated.json +2122 -0
- package/node_modules/typescript/package.json +120 -0
- package/package.json +12 -9
- package/lib/coverage/fs-walk.mjs +0 -64
- package/node_modules/@elench/next-analysis/src/api-routes.mjs +0 -81
- package/node_modules/@elench/next-analysis/src/api-routes.test.mjs +0 -22
- package/node_modules/@elench/next-analysis/src/app-root.mjs +0 -7
- package/node_modules/@elench/next-analysis/src/backend-links.mjs +0 -31
- package/node_modules/@elench/next-analysis/src/index.mjs +0 -21
- package/node_modules/@elench/next-analysis/src/pages.mjs +0 -68
- package/node_modules/@elench/next-analysis/src/project.mjs +0 -94
- package/node_modules/@elench/next-analysis/src/project.test.mjs +0 -35
- package/node_modules/@elench/next-analysis/src/route-tree.mjs +0 -621
- package/node_modules/@elench/next-analysis/src/routes.mjs +0 -41
- package/node_modules/@elench/next-analysis/src/routes.test.mjs +0 -25
- package/node_modules/@elench/next-analysis/src/server-actions.mjs +0 -53
- package/node_modules/@elench/next-analysis/src/server-actions.test.mjs +0 -37
- package/node_modules/@elench/next-analysis/src/shared.mjs +0 -209
- package/node_modules/@elench/next-analysis/src/swc.mjs +0 -388
- package/node_modules/@elench/testkit-bridge/src/index.mjs +0 -583
- package/node_modules/@elench/testkit-bridge/src/index.test.mjs +0 -409
- package/node_modules/@elench/testkit-protocol/src/index.d.ts +0 -231
- package/node_modules/@elench/testkit-protocol/src/index.mjs +0 -265
- package/node_modules/@elench/testkit-protocol/src/index.test.mjs +0 -242
- package/node_modules/@elench/ts-analysis/src/callables.mjs +0 -135
- package/node_modules/@elench/ts-analysis/src/callables.test.mjs +0 -55
- package/node_modules/@elench/ts-analysis/src/exports.mjs +0 -69
- package/node_modules/@elench/ts-analysis/src/exports.test.mjs +0 -50
- package/node_modules/@elench/ts-analysis/src/jsx.mjs +0 -69
- package/node_modules/@elench/ts-analysis/src/jsx.test.mjs +0 -43
- package/node_modules/@elench/ts-analysis/src/project.mjs +0 -100
- package/node_modules/@elench/ts-analysis/src/project.test.mjs +0 -54
- package/node_modules/@elench/ts-analysis/src/requests.mjs +0 -141
- package/node_modules/@elench/ts-analysis/src/requests.test.mjs +0 -35
- package/node_modules/@elench/ts-analysis/src/resolution.mjs +0 -53
- package/node_modules/@elench/ts-analysis/src/shared.mjs +0 -32
- package/node_modules/@elench/ts-analysis/src/syntax.mjs +0 -27
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
export function loadIssueCache(productDir, cachePath, schemaVersion) {
|
|
5
|
+
const filePath = path.join(productDir, ...cachePath);
|
|
6
|
+
if (!fs.existsSync(filePath)) {
|
|
7
|
+
return {
|
|
8
|
+
schemaVersion,
|
|
9
|
+
entries: {},
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
15
|
+
if (parsed?.schemaVersion !== schemaVersion || typeof parsed.entries !== "object") {
|
|
16
|
+
return {
|
|
17
|
+
schemaVersion,
|
|
18
|
+
entries: {},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return parsed;
|
|
22
|
+
} catch {
|
|
23
|
+
return {
|
|
24
|
+
schemaVersion,
|
|
25
|
+
entries: {},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function resolveIssueCache(cache, issueNumbersByRepo, config, now) {
|
|
31
|
+
const issuesByRepo = new Map();
|
|
32
|
+
const missingByRepo = new Map();
|
|
33
|
+
const staleByRepo = new Map();
|
|
34
|
+
const ttlMs = config.cacheTtlSeconds * 1000;
|
|
35
|
+
|
|
36
|
+
for (const [repo, numbers] of issueNumbersByRepo.entries()) {
|
|
37
|
+
const cachedIssues = new Map();
|
|
38
|
+
const missing = [];
|
|
39
|
+
const stale = [];
|
|
40
|
+
|
|
41
|
+
for (const number of numbers) {
|
|
42
|
+
const key = buildIssueCacheKey(repo, number);
|
|
43
|
+
const cached = cache.entries[key];
|
|
44
|
+
if (!cached) {
|
|
45
|
+
missing.push(number);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const checkedAt = Date.parse(cached.checkedAt);
|
|
50
|
+
const normalized = {
|
|
51
|
+
repo,
|
|
52
|
+
number,
|
|
53
|
+
exists: Boolean(cached.exists),
|
|
54
|
+
title: normalizeOptionalString(cached.title),
|
|
55
|
+
state: normalizeOptionalString(cached.state),
|
|
56
|
+
url: normalizeOptionalString(cached.url),
|
|
57
|
+
checkedAt: cached.checkedAt || null,
|
|
58
|
+
source: "cache",
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
if (!Number.isFinite(checkedAt) || now - checkedAt > ttlMs) {
|
|
62
|
+
stale.push(number);
|
|
63
|
+
cachedIssues.set(number, normalized);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
cachedIssues.set(number, normalized);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
issuesByRepo.set(repo, cachedIssues);
|
|
71
|
+
if (missing.length > 0 || stale.length > 0) {
|
|
72
|
+
missingByRepo.set(repo, [...new Set([...missing, ...stale])].sort((a, b) => a - b));
|
|
73
|
+
}
|
|
74
|
+
if (stale.length > 0) {
|
|
75
|
+
staleByRepo.set(repo, stale.sort((a, b) => a - b));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
issuesByRepo,
|
|
81
|
+
missingByRepo,
|
|
82
|
+
staleByRepo,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function updateIssueCache(cache, issuesByRepo, now, schemaVersion) {
|
|
87
|
+
cache.schemaVersion = schemaVersion;
|
|
88
|
+
cache.entries = cache.entries || {};
|
|
89
|
+
const checkedAt = new Date(now).toISOString();
|
|
90
|
+
|
|
91
|
+
for (const [repo, issues] of issuesByRepo.entries()) {
|
|
92
|
+
for (const [number, issue] of issues.entries()) {
|
|
93
|
+
issue.checkedAt = checkedAt;
|
|
94
|
+
cache.entries[buildIssueCacheKey(repo, number)] = {
|
|
95
|
+
exists: issue.exists,
|
|
96
|
+
title: issue.title,
|
|
97
|
+
state: issue.state,
|
|
98
|
+
url: issue.url,
|
|
99
|
+
checkedAt,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function writeIssueCache(productDir, cachePath, cache) {
|
|
106
|
+
const filePath = path.join(productDir, ...cachePath);
|
|
107
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
108
|
+
fs.writeFileSync(filePath, `${JSON.stringify(cache, null, 2)}\n`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function applyStaleCacheFallback(issuesByRepo, staleByRepo, findings) {
|
|
112
|
+
let usedStale = false;
|
|
113
|
+
for (const [repo, numbers] of staleByRepo.entries()) {
|
|
114
|
+
const issues = issuesByRepo.get(repo);
|
|
115
|
+
if (!issues) continue;
|
|
116
|
+
for (const number of numbers) {
|
|
117
|
+
if (issues.has(number)) {
|
|
118
|
+
usedStale = true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (usedStale) {
|
|
123
|
+
findings.push({
|
|
124
|
+
code: "used_stale_cache",
|
|
125
|
+
severity: "warning",
|
|
126
|
+
message: "Used stale cached GitHub issue metadata because fresh validation failed",
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return issuesByRepo;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function mergeIssuesByRepo(left, right) {
|
|
133
|
+
const merged = new Map();
|
|
134
|
+
for (const [repo, issues] of left.entries()) {
|
|
135
|
+
merged.set(repo, new Map(issues.entries()));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
for (const [repo, issues] of right.entries()) {
|
|
139
|
+
if (!merged.has(repo)) {
|
|
140
|
+
merged.set(repo, new Map());
|
|
141
|
+
}
|
|
142
|
+
const mergedIssues = merged.get(repo);
|
|
143
|
+
for (const [number, issue] of issues.entries()) {
|
|
144
|
+
mergedIssues.set(number, issue);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return merged;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function buildIssueCacheKey(repo, number) {
|
|
152
|
+
return `${repo}#${number}`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function normalizeOptionalString(value) {
|
|
156
|
+
if (typeof value !== "string") return null;
|
|
157
|
+
const normalized = value.trim();
|
|
158
|
+
return normalized.length > 0 ? normalized : null;
|
|
159
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
|
+
import { promisify } from "util";
|
|
3
|
+
|
|
4
|
+
const execFileAsync = promisify(execFile);
|
|
5
|
+
|
|
6
|
+
export function parseGitHubRepoSlug(remoteUrl) {
|
|
7
|
+
const normalized = normalizeOptionalString(remoteUrl);
|
|
8
|
+
if (!normalized) return null;
|
|
9
|
+
|
|
10
|
+
let match = normalized.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/i);
|
|
11
|
+
if (match) return `${match[1]}/${match[2]}`;
|
|
12
|
+
|
|
13
|
+
match = normalized.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/i);
|
|
14
|
+
if (match) return `${match[1]}/${match[2]}`;
|
|
15
|
+
|
|
16
|
+
match = normalized.match(/^ssh:\/\/git@github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/i);
|
|
17
|
+
if (match) return `${match[1]}/${match[2]}`;
|
|
18
|
+
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function createDefaultGitHubIssueTransport(env = process.env) {
|
|
23
|
+
const token = env.GH_TOKEN || env.GITHUB_TOKEN || null;
|
|
24
|
+
if (token) {
|
|
25
|
+
return {
|
|
26
|
+
type: "token",
|
|
27
|
+
async fetchRepoIssues(repo, numbers) {
|
|
28
|
+
return fetchRepoIssuesViaToken(repo, numbers, token);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
await execFileAsync("gh", ["auth", "status"], {
|
|
35
|
+
encoding: "utf8",
|
|
36
|
+
env,
|
|
37
|
+
maxBuffer: 1024 * 1024,
|
|
38
|
+
});
|
|
39
|
+
return {
|
|
40
|
+
type: "gh",
|
|
41
|
+
async fetchRepoIssues(repo, numbers) {
|
|
42
|
+
return fetchRepoIssuesViaGh(repo, numbers, env);
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
} catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function fetchIssuesByRepo(client, issueNumbersByRepo) {
|
|
51
|
+
const issuesByRepo = new Map();
|
|
52
|
+
|
|
53
|
+
for (const [repo, numbers] of issueNumbersByRepo.entries()) {
|
|
54
|
+
const issueMap = new Map();
|
|
55
|
+
const chunks = chunkValues(numbers, 40);
|
|
56
|
+
for (const chunk of chunks) {
|
|
57
|
+
const fetched = await client.fetchRepoIssues(repo, chunk);
|
|
58
|
+
for (const [number, issue] of fetched.entries()) {
|
|
59
|
+
issueMap.set(number, issue);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
issuesByRepo.set(repo, issueMap);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return issuesByRepo;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function fetchRepoIssuesViaToken(repo, numbers, token) {
|
|
69
|
+
const query = buildIssueQuery(repo, numbers);
|
|
70
|
+
const response = await fetch("https://api.github.com/graphql", {
|
|
71
|
+
method: "POST",
|
|
72
|
+
headers: {
|
|
73
|
+
"Content-Type": "application/json",
|
|
74
|
+
Accept: "application/vnd.github+json",
|
|
75
|
+
Authorization: `Bearer ${token}`,
|
|
76
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
77
|
+
},
|
|
78
|
+
body: JSON.stringify({ query }),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const payload = await response.json().catch(() => null);
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
`GitHub GraphQL request failed with ${response.status}${payload?.message ? `: ${payload.message}` : ""}`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
if (Array.isArray(payload?.errors) && payload.errors.length > 0) {
|
|
88
|
+
throw new Error(payload.errors.map((error) => error.message).join("; "));
|
|
89
|
+
}
|
|
90
|
+
return normalizeGraphqlIssuesResponse(repo, numbers, payload?.data);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function fetchRepoIssuesViaGh(repo, numbers, env) {
|
|
94
|
+
const query = buildIssueQuery(repo, numbers);
|
|
95
|
+
const { stdout } = await execFileAsync("gh", ["api", "graphql", "-f", `query=${query}`], {
|
|
96
|
+
encoding: "utf8",
|
|
97
|
+
env,
|
|
98
|
+
maxBuffer: 1024 * 1024,
|
|
99
|
+
});
|
|
100
|
+
const payload = JSON.parse(stdout);
|
|
101
|
+
if (Array.isArray(payload?.errors) && payload.errors.length > 0) {
|
|
102
|
+
throw new Error(payload.errors.map((error) => error.message).join("; "));
|
|
103
|
+
}
|
|
104
|
+
return normalizeGraphqlIssuesResponse(repo, numbers, payload?.data);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function buildIssueQuery(repo, numbers) {
|
|
108
|
+
const [owner, name] = repo.split("/");
|
|
109
|
+
return `
|
|
110
|
+
query TestkitKnownFailureIssues {
|
|
111
|
+
repository(owner: ${JSON.stringify(owner)}, name: ${JSON.stringify(name)}) {
|
|
112
|
+
${numbers
|
|
113
|
+
.map(
|
|
114
|
+
(number) => `
|
|
115
|
+
issue_${number}: issue(number: ${number}) {
|
|
116
|
+
number
|
|
117
|
+
title
|
|
118
|
+
state
|
|
119
|
+
url
|
|
120
|
+
}
|
|
121
|
+
`
|
|
122
|
+
)
|
|
123
|
+
.join("\n")}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function normalizeGraphqlIssuesResponse(repo, numbers, data) {
|
|
130
|
+
const map = new Map();
|
|
131
|
+
const repository = data?.repository || null;
|
|
132
|
+
for (const number of numbers) {
|
|
133
|
+
const issue = repository?.[`issue_${number}`] || null;
|
|
134
|
+
if (!issue) {
|
|
135
|
+
map.set(number, {
|
|
136
|
+
repo,
|
|
137
|
+
number,
|
|
138
|
+
exists: false,
|
|
139
|
+
title: null,
|
|
140
|
+
state: null,
|
|
141
|
+
url: null,
|
|
142
|
+
checkedAt: null,
|
|
143
|
+
source: "github",
|
|
144
|
+
});
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
map.set(number, {
|
|
149
|
+
repo,
|
|
150
|
+
number: issue.number,
|
|
151
|
+
exists: true,
|
|
152
|
+
title: normalizeOptionalString(issue.title),
|
|
153
|
+
state: normalizeOptionalString(issue.state),
|
|
154
|
+
url: normalizeOptionalString(issue.url),
|
|
155
|
+
checkedAt: null,
|
|
156
|
+
source: "github",
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
return map;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function normalizeOptionalString(value) {
|
|
163
|
+
if (typeof value !== "string") return null;
|
|
164
|
+
const normalized = value.trim();
|
|
165
|
+
return normalized.length > 0 ? normalized : null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function chunkValues(values, size) {
|
|
169
|
+
const chunks = [];
|
|
170
|
+
for (let index = 0; index < values.length; index += size) {
|
|
171
|
+
chunks.push(values.slice(index, index + size));
|
|
172
|
+
}
|
|
173
|
+
return chunks;
|
|
174
|
+
}
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import { execFile } from "child_process";
|
|
4
|
-
import { promisify } from "util";
|
|
5
1
|
import { findMatchingKnownFailureEntries } from "./index.mjs";
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
import {
|
|
3
|
+
applyStaleCacheFallback,
|
|
4
|
+
loadIssueCache,
|
|
5
|
+
mergeIssuesByRepo,
|
|
6
|
+
resolveIssueCache,
|
|
7
|
+
updateIssueCache,
|
|
8
|
+
writeIssueCache,
|
|
9
|
+
} from "./github-cache.mjs";
|
|
10
|
+
import {
|
|
11
|
+
createDefaultGitHubIssueTransport,
|
|
12
|
+
fetchIssuesByRepo,
|
|
13
|
+
parseGitHubRepoSlug,
|
|
14
|
+
} from "./github-transport.mjs";
|
|
15
|
+
export { parseGitHubRepoSlug } from "./github-transport.mjs";
|
|
8
16
|
const DEFAULT_CACHE_TTL_SECONDS = 15 * 60;
|
|
9
17
|
const CACHE_SCHEMA_VERSION = 1;
|
|
10
18
|
const CACHE_PATH = [".testkit", "known-failures", "github-issues-cache.json"];
|
|
@@ -60,7 +68,7 @@ export async function validateKnownFailureIssues({
|
|
|
60
68
|
const issueNumbersByRepo = groupIssueNumbersByRepo(document.entries);
|
|
61
69
|
const repoSlug = gitMetadata?.repoSlug || null;
|
|
62
70
|
const remoteUrl = gitMetadata?.remoteUrl || null;
|
|
63
|
-
const cache = loadIssueCache(productDir);
|
|
71
|
+
const cache = loadIssueCache(productDir, CACHE_PATH, CACHE_SCHEMA_VERSION);
|
|
64
72
|
const cacheResolution = resolveIssueCache(cache, issueNumbersByRepo, normalizedConfig, now);
|
|
65
73
|
const availabilityFindings = [];
|
|
66
74
|
let issuesByRepo = cacheResolution.issuesByRepo;
|
|
@@ -71,8 +79,8 @@ export async function validateKnownFailureIssues({
|
|
|
71
79
|
try {
|
|
72
80
|
const fetchedIssues = await fetchIssuesByRepo(client, cacheResolution.missingByRepo);
|
|
73
81
|
issuesByRepo = mergeIssuesByRepo(issuesByRepo, fetchedIssues);
|
|
74
|
-
updateIssueCache(cache, fetchedIssues, now);
|
|
75
|
-
writeIssueCache(productDir, cache);
|
|
82
|
+
updateIssueCache(cache, fetchedIssues, now, CACHE_SCHEMA_VERSION);
|
|
83
|
+
writeIssueCache(productDir, CACHE_PATH, cache);
|
|
76
84
|
} catch (error) {
|
|
77
85
|
issuesByRepo = applyStaleCacheFallback(
|
|
78
86
|
issuesByRepo,
|
|
@@ -191,167 +199,6 @@ export function buildKnownFailureIssueValidationSummaryLines(result) {
|
|
|
191
199
|
];
|
|
192
200
|
}
|
|
193
201
|
|
|
194
|
-
export function parseGitHubRepoSlug(remoteUrl) {
|
|
195
|
-
const normalized = normalizeOptionalString(remoteUrl);
|
|
196
|
-
if (!normalized) return null;
|
|
197
|
-
|
|
198
|
-
let match = normalized.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/i);
|
|
199
|
-
if (match) return `${match[1]}/${match[2]}`;
|
|
200
|
-
|
|
201
|
-
match = normalized.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/i);
|
|
202
|
-
if (match) return `${match[1]}/${match[2]}`;
|
|
203
|
-
|
|
204
|
-
match = normalized.match(/^ssh:\/\/git@github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/i);
|
|
205
|
-
if (match) return `${match[1]}/${match[2]}`;
|
|
206
|
-
|
|
207
|
-
return null;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
async function createDefaultGitHubIssueTransport() {
|
|
211
|
-
const token = process.env.GH_TOKEN || process.env.GITHUB_TOKEN || null;
|
|
212
|
-
if (token) {
|
|
213
|
-
return {
|
|
214
|
-
type: "token",
|
|
215
|
-
async fetchRepoIssues(repo, numbers) {
|
|
216
|
-
return fetchRepoIssuesViaToken(repo, numbers, token);
|
|
217
|
-
},
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
try {
|
|
222
|
-
await execFileAsync("gh", ["auth", "status"], {
|
|
223
|
-
encoding: "utf8",
|
|
224
|
-
env: process.env,
|
|
225
|
-
maxBuffer: 1024 * 1024,
|
|
226
|
-
});
|
|
227
|
-
return {
|
|
228
|
-
type: "gh",
|
|
229
|
-
async fetchRepoIssues(repo, numbers) {
|
|
230
|
-
return fetchRepoIssuesViaGh(repo, numbers);
|
|
231
|
-
},
|
|
232
|
-
};
|
|
233
|
-
} catch {
|
|
234
|
-
return null;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
async function fetchIssuesByRepo(client, issueNumbersByRepo) {
|
|
239
|
-
const issuesByRepo = new Map();
|
|
240
|
-
|
|
241
|
-
for (const [repo, numbers] of issueNumbersByRepo.entries()) {
|
|
242
|
-
const issueMap = new Map();
|
|
243
|
-
const chunks = chunkValues(numbers, 40);
|
|
244
|
-
for (const chunk of chunks) {
|
|
245
|
-
const fetched = await client.fetchRepoIssues(repo, chunk);
|
|
246
|
-
for (const [number, issue] of fetched.entries()) {
|
|
247
|
-
issueMap.set(number, issue);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
issuesByRepo.set(repo, issueMap);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
return issuesByRepo;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
async function fetchRepoIssuesViaToken(repo, numbers, token) {
|
|
257
|
-
const query = buildIssueQuery(repo, numbers);
|
|
258
|
-
const response = await fetch("https://api.github.com/graphql", {
|
|
259
|
-
method: "POST",
|
|
260
|
-
headers: {
|
|
261
|
-
"Content-Type": "application/json",
|
|
262
|
-
Accept: "application/vnd.github+json",
|
|
263
|
-
Authorization: `Bearer ${token}`,
|
|
264
|
-
"X-GitHub-Api-Version": "2022-11-28",
|
|
265
|
-
},
|
|
266
|
-
body: JSON.stringify({ query }),
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
const payload = await response.json().catch(() => null);
|
|
270
|
-
if (!response.ok) {
|
|
271
|
-
throw new Error(
|
|
272
|
-
`GitHub GraphQL request failed with ${response.status}${
|
|
273
|
-
payload?.message ? `: ${payload.message}` : ""
|
|
274
|
-
}`
|
|
275
|
-
);
|
|
276
|
-
}
|
|
277
|
-
if (Array.isArray(payload?.errors) && payload.errors.length > 0) {
|
|
278
|
-
throw new Error(payload.errors.map((error) => error.message).join("; "));
|
|
279
|
-
}
|
|
280
|
-
return normalizeGraphqlIssuesResponse(repo, numbers, payload?.data);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
async function fetchRepoIssuesViaGh(repo, numbers) {
|
|
284
|
-
const query = buildIssueQuery(repo, numbers);
|
|
285
|
-
const { stdout } = await execFileAsync(
|
|
286
|
-
"gh",
|
|
287
|
-
["api", "graphql", "-f", `query=${query}`],
|
|
288
|
-
{
|
|
289
|
-
encoding: "utf8",
|
|
290
|
-
env: process.env,
|
|
291
|
-
maxBuffer: 1024 * 1024,
|
|
292
|
-
}
|
|
293
|
-
);
|
|
294
|
-
const payload = JSON.parse(stdout);
|
|
295
|
-
if (Array.isArray(payload?.errors) && payload.errors.length > 0) {
|
|
296
|
-
throw new Error(payload.errors.map((error) => error.message).join("; "));
|
|
297
|
-
}
|
|
298
|
-
return normalizeGraphqlIssuesResponse(repo, numbers, payload?.data);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
function buildIssueQuery(repo, numbers) {
|
|
302
|
-
const [owner, name] = repo.split("/");
|
|
303
|
-
return `
|
|
304
|
-
query TestkitKnownFailureIssues {
|
|
305
|
-
repository(owner: ${JSON.stringify(owner)}, name: ${JSON.stringify(name)}) {
|
|
306
|
-
${numbers
|
|
307
|
-
.map(
|
|
308
|
-
(number) => `
|
|
309
|
-
issue_${number}: issue(number: ${number}) {
|
|
310
|
-
number
|
|
311
|
-
title
|
|
312
|
-
state
|
|
313
|
-
url
|
|
314
|
-
}
|
|
315
|
-
`
|
|
316
|
-
)
|
|
317
|
-
.join("\n")}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
`;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
function normalizeGraphqlIssuesResponse(repo, numbers, data) {
|
|
324
|
-
const map = new Map();
|
|
325
|
-
const repository = data?.repository || null;
|
|
326
|
-
for (const number of numbers) {
|
|
327
|
-
const issue = repository?.[`issue_${number}`] || null;
|
|
328
|
-
if (!issue) {
|
|
329
|
-
map.set(number, {
|
|
330
|
-
repo,
|
|
331
|
-
number,
|
|
332
|
-
exists: false,
|
|
333
|
-
title: null,
|
|
334
|
-
state: null,
|
|
335
|
-
url: null,
|
|
336
|
-
checkedAt: null,
|
|
337
|
-
source: "github",
|
|
338
|
-
});
|
|
339
|
-
continue;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
map.set(number, {
|
|
343
|
-
repo,
|
|
344
|
-
number: issue.number,
|
|
345
|
-
exists: true,
|
|
346
|
-
title: normalizeOptionalString(issue.title),
|
|
347
|
-
state: normalizeOptionalString(issue.state),
|
|
348
|
-
url: normalizeOptionalString(issue.url),
|
|
349
|
-
checkedAt: null,
|
|
350
|
-
source: "github",
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
return map;
|
|
354
|
-
}
|
|
355
202
|
|
|
356
203
|
function collectObservedKnownFailureEntries(document, runArtifact, statusArtifact) {
|
|
357
204
|
const observedTests = collectObservedTests(runArtifact, statusArtifact);
|
|
@@ -583,161 +430,6 @@ function buildIssueValidationSummary(entries, globalFindings) {
|
|
|
583
430
|
};
|
|
584
431
|
}
|
|
585
432
|
|
|
586
|
-
function loadIssueCache(productDir) {
|
|
587
|
-
const filePath = getIssueCachePath(productDir);
|
|
588
|
-
if (!fs.existsSync(filePath)) {
|
|
589
|
-
return {
|
|
590
|
-
schemaVersion: CACHE_SCHEMA_VERSION,
|
|
591
|
-
entries: {},
|
|
592
|
-
};
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
try {
|
|
596
|
-
const parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
597
|
-
if (parsed?.schemaVersion !== CACHE_SCHEMA_VERSION || typeof parsed.entries !== "object") {
|
|
598
|
-
return {
|
|
599
|
-
schemaVersion: CACHE_SCHEMA_VERSION,
|
|
600
|
-
entries: {},
|
|
601
|
-
};
|
|
602
|
-
}
|
|
603
|
-
return parsed;
|
|
604
|
-
} catch {
|
|
605
|
-
return {
|
|
606
|
-
schemaVersion: CACHE_SCHEMA_VERSION,
|
|
607
|
-
entries: {},
|
|
608
|
-
};
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
function resolveIssueCache(cache, issueNumbersByRepo, config, now) {
|
|
613
|
-
const issuesByRepo = new Map();
|
|
614
|
-
const missingByRepo = new Map();
|
|
615
|
-
const staleByRepo = new Map();
|
|
616
|
-
const ttlMs = config.cacheTtlSeconds * 1000;
|
|
617
|
-
|
|
618
|
-
for (const [repo, numbers] of issueNumbersByRepo.entries()) {
|
|
619
|
-
const cachedIssues = new Map();
|
|
620
|
-
const missing = [];
|
|
621
|
-
const stale = [];
|
|
622
|
-
|
|
623
|
-
for (const number of numbers) {
|
|
624
|
-
const key = buildIssueCacheKey(repo, number);
|
|
625
|
-
const cached = cache.entries[key];
|
|
626
|
-
if (!cached) {
|
|
627
|
-
missing.push(number);
|
|
628
|
-
continue;
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
const checkedAt = Date.parse(cached.checkedAt);
|
|
632
|
-
const normalized = {
|
|
633
|
-
repo,
|
|
634
|
-
number,
|
|
635
|
-
exists: Boolean(cached.exists),
|
|
636
|
-
title: normalizeOptionalString(cached.title),
|
|
637
|
-
state: normalizeOptionalString(cached.state),
|
|
638
|
-
url: normalizeOptionalString(cached.url),
|
|
639
|
-
checkedAt: cached.checkedAt || null,
|
|
640
|
-
source: "cache",
|
|
641
|
-
};
|
|
642
|
-
|
|
643
|
-
if (!Number.isFinite(checkedAt) || now - checkedAt > ttlMs) {
|
|
644
|
-
stale.push(number);
|
|
645
|
-
cachedIssues.set(number, normalized);
|
|
646
|
-
continue;
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
cachedIssues.set(number, normalized);
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
issuesByRepo.set(repo, cachedIssues);
|
|
653
|
-
if (missing.length > 0 || stale.length > 0) {
|
|
654
|
-
missingByRepo.set(repo, [...new Set([...missing, ...stale])].sort((a, b) => a - b));
|
|
655
|
-
}
|
|
656
|
-
if (stale.length > 0) {
|
|
657
|
-
staleByRepo.set(repo, stale.sort((a, b) => a - b));
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
return {
|
|
662
|
-
issuesByRepo,
|
|
663
|
-
missingByRepo,
|
|
664
|
-
staleByRepo,
|
|
665
|
-
};
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
function updateIssueCache(cache, issuesByRepo, now) {
|
|
669
|
-
cache.schemaVersion = CACHE_SCHEMA_VERSION;
|
|
670
|
-
cache.entries = cache.entries || {};
|
|
671
|
-
const checkedAt = new Date(now).toISOString();
|
|
672
|
-
|
|
673
|
-
for (const [repo, issues] of issuesByRepo.entries()) {
|
|
674
|
-
for (const [number, issue] of issues.entries()) {
|
|
675
|
-
issue.checkedAt = checkedAt;
|
|
676
|
-
cache.entries[buildIssueCacheKey(repo, number)] = {
|
|
677
|
-
exists: issue.exists,
|
|
678
|
-
title: issue.title,
|
|
679
|
-
state: issue.state,
|
|
680
|
-
url: issue.url,
|
|
681
|
-
checkedAt,
|
|
682
|
-
};
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
function writeIssueCache(productDir, cache) {
|
|
688
|
-
const filePath = getIssueCachePath(productDir);
|
|
689
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
690
|
-
fs.writeFileSync(filePath, `${JSON.stringify(cache, null, 2)}\n`);
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
function getIssueCachePath(productDir) {
|
|
694
|
-
return path.join(productDir, ...CACHE_PATH);
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
function applyStaleCacheFallback(issuesByRepo, staleByRepo, findings) {
|
|
698
|
-
let usedStale = false;
|
|
699
|
-
for (const [repo, numbers] of staleByRepo.entries()) {
|
|
700
|
-
const issues = issuesByRepo.get(repo);
|
|
701
|
-
if (!issues) continue;
|
|
702
|
-
for (const number of numbers) {
|
|
703
|
-
if (issues.has(number)) {
|
|
704
|
-
usedStale = true;
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
if (usedStale) {
|
|
709
|
-
findings.push({
|
|
710
|
-
code: "used_stale_cache",
|
|
711
|
-
severity: "warning",
|
|
712
|
-
message: "Used stale cached GitHub issue metadata because fresh validation failed",
|
|
713
|
-
});
|
|
714
|
-
}
|
|
715
|
-
return issuesByRepo;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
function mergeIssuesByRepo(left, right) {
|
|
719
|
-
const merged = new Map();
|
|
720
|
-
for (const [repo, issues] of left.entries()) {
|
|
721
|
-
merged.set(repo, new Map(issues.entries()));
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
for (const [repo, issues] of right.entries()) {
|
|
725
|
-
if (!merged.has(repo)) {
|
|
726
|
-
merged.set(repo, new Map());
|
|
727
|
-
}
|
|
728
|
-
const mergedIssues = merged.get(repo);
|
|
729
|
-
for (const [number, issue] of issues.entries()) {
|
|
730
|
-
mergedIssues.set(number, issue);
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
return merged;
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
function buildIssueCacheKey(repo, number) {
|
|
738
|
-
return `${repo}#${number}`;
|
|
739
|
-
}
|
|
740
|
-
|
|
741
433
|
function normalizeIssueState(value) {
|
|
742
434
|
const normalized = normalizeOptionalString(value);
|
|
743
435
|
if (!normalized) return null;
|