diff --git a/.github/workflows/autogen/.eslintrc.yml b/.github/workflows/autogen/.eslintrc.yml new file mode 100644 index 000000000..4bad4ebe4 --- /dev/null +++ b/.github/workflows/autogen/.eslintrc.yml @@ -0,0 +1,14 @@ +env: + browser: true + es2021: true +extends: standard +parserOptions: + ecmaVersion: latest + sourceType: module +overrides: + - files: ["*.mjs"] + parserOptions: + ecmaVersion: latest + sourceType: module +rules: + "no-template-curly-in-string": "off" diff --git a/.github/workflows/autogen/README.md b/.github/workflows/autogen/README.md new file mode 100644 index 000000000..c75fe5a1b --- /dev/null +++ b/.github/workflows/autogen/README.md @@ -0,0 +1,42 @@ +# Solo autogen tool + +## Description + +The Solo autogen tool is used to add e2e test cases that need to be ran independently as their own job into the GitHub workflows and into the solo package.json + +## Usage + +from solo root directory: +```bash +cd .github/workflows/autogen +npm install +npm run autogen +``` + +Use git to detect file changes and validate that they are correct. + +The templates need to be maintained, you can either make changes directly to the templates and then run the tool, or make changes in both the workflow yaml files and the templates. Should the templates fall out of sync, then you can update the templates so that when autogen runs again, the git diff will better match. +```bash +template.flow-build-application.yaml +template.flow-pull-request-checks.yaml +template.zxc-code-analysis.yaml +template.zxc-env-vars.yaml + ``` +For new e2e test jobs update the `/.github/workflows/templates/config.yaml`, adding a new item to the tests object with a name and jestPostfix attribute. + +NOTE: IntelliJ copy/paste will alter the escape sequences, you might have to manually type it in, clone a line, or use an external text editor. + +e.g.: +```yaml + - name: Mirror Node + jestPostfix: --testRegex=\".*\\/e2e\\/commands\\/mirror_node\\.test\\.mjs\" + +``` + +## Development + +To run lint fix: +```bash +cd .github/workflows/autogen +eslint --fix . +``` diff --git a/.github/workflows/autogen/autogen.mjs b/.github/workflows/autogen/autogen.mjs new file mode 100755 index 000000000..a16d7818c --- /dev/null +++ b/.github/workflows/autogen/autogen.mjs @@ -0,0 +1,20 @@ +#!/usr/bin/env node +/** + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the ""License""); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an ""AS IS"" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import * as fnm from './src/index.mjs' + +fnm.main(process.argv) diff --git a/.github/workflows/autogen/package-lock.json b/.github/workflows/autogen/package-lock.json new file mode 100644 index 000000000..7d5d3e2a2 --- /dev/null +++ b/.github/workflows/autogen/package-lock.json @@ -0,0 +1,1077 @@ +{ + "name": "add-e2e-test", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "add-e2e-test", + "version": "0.0.1", + "license": "Apache2.0", + "os": [ + "darwin", + "linux", + "win32" + ], + "dependencies": { + "change-case": "^5.4.4", + "js-yaml": "^4.1.0" + }, + "bin": { + "autogen": "autogen.mjs" + }, + "devDependencies": { + "eslint": "^9.10.0" + }, + "engines": { + "node": ">=20.14.0", + "npm": ">=9.8.1" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.10.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.10.0.tgz", + "integrity": "sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.1.0.tgz", + "integrity": "sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==", + "dev": true, + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.10.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.10.0.tgz", + "integrity": "sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.18.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.10.0", + "@eslint/plugin-kit": "^0.1.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.0", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.0.2", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.1.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", + "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", + "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", + "dev": true, + "dependencies": { + "acorn": "^8.12.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/.github/workflows/autogen/package.json b/.github/workflows/autogen/package.json new file mode 100644 index 000000000..00b1ae76a --- /dev/null +++ b/.github/workflows/autogen/package.json @@ -0,0 +1,32 @@ +{ + "name": "add-e2e-test", + "version": "0.0.1", + "description": "uses templates to add e2e tests into the workflows and package.json", + "main": "src/index.mjs", + "type": "module", + "bin": { + "autogen": "autogen.mjs" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "autogen": "NODE_OPTIONS=--experimental-vm-modules node --no-deprecation autogen.mjs" + }, + "author": "Swirlds Labs", + "license": "Apache2.0", + "os": [ + "darwin", + "linux", + "win32" + ], + "engines": { + "node": ">=20.14.0", + "npm": ">=9.8.1" + }, + "devDependencies": { + "eslint": "^9.10.0" + }, + "dependencies": { + "change-case": "^5.4.4", + "js-yaml": "^4.1.0" + } +} diff --git a/.github/workflows/autogen/src/index.mjs b/.github/workflows/autogen/src/index.mjs new file mode 100644 index 000000000..8f2ebcd42 --- /dev/null +++ b/.github/workflows/autogen/src/index.mjs @@ -0,0 +1,339 @@ +/** + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the ""License""); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an ""AS IS"" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +'use strict' +import * as yaml from 'js-yaml' +import * as fs from 'node:fs' +import * as path from 'node:path' +import { fileURLToPath } from 'url' +import * as changeCase from 'change-case' + +export const AUTOGENERATE_E2E_TEST_JOBS = '# {AUTOGENERATE-E2E-TEST-JOBS}' +export const AUTOGENERATE_E2E_TEST_JOBS_2 = '# {AUTOGENERATE-E2E-TEST-JOBS-2}' +export const AUTOGENERATE_NEEDS = '# {AUTOGENERATE-NEEDS}' +export const AUTOGENERATE_WITH_SUBDIR = '# {AUTOGENERATE-WITH-SUBDIR}' +export const AUTOGENERATE_WITH_COVERAGE_REPORT = '# {AUTOGENERATE-WITH-COVERAGE-REPORT}' +export const AUTOGENERATE_JOB_OUTPUTS_SUB_DIRS = '# {AUTOGENERATE-JOB-OUTPUTS-SUB-DIRS}' +export const AUTOGENERATE_JOB_OUTPUTS_COVERAGE_REPORTS = '# {AUTOGENERATE-JOB-OUTPUTS-COVERAGE-REPORTS}' +export const AUTOGENERATE_WORKFLOW_OUTPUTS_SUB_DIRS = '# {AUTOGENERATE-WORKFLOW-OUTPUTS-SUB-DIRS}' +export const AUTOGENERATE_WORKFLOW_OUTPUTS_COVERAGE_REPORTS = '# {AUTOGENERATE-WORKFLOW-OUTPUTS-COVERAGE-REPORTS}' +export const AUTOGENERATE_INPUTS_SUB_DIRS = '# {AUTOGENERATE-INPUTS-SUB-DIRS}' +export const AUTOGENERATE_INPUTS_COVERAGE_REPORTS = '# {AUTOGENERATE-INPUTS-COVERAGE-REPORTS}' +export const AUTOGENERATE_DOWNLOAD_JOBS = '# {AUTOGENERATE-DOWNLOAD-JOBS}' + +/** + * @typedef {Object} Test + * @property {string} name + * @property {string} jestPostfix + */ + +/** + * @typedef {Object} Config + * @property {string} downloadArtifactAction + * @property {string} downloadArtifactActionComment + * @property {Test[]} tests + */ + +export function main () { + console.log('Begin autogen...') + + const __filename = fileURLToPath(import.meta.url) // get the resolved path to the file + const __dirname = path.dirname(__filename) // get the name of the directory + const outputDir = path.dirname(path.dirname(__dirname)) + const templateDir = path.join(outputDir, 'templates') + const configFile = path.join(templateDir, 'config.yaml') + const configData = fs.readFileSync(configFile, 'utf8') + const config = /** @type {Config} **/ yaml.load(configData) + + // generate the workflows with changes + buildWorkflows(outputDir, templateDir, config) + + // update the Solo package.json with changes + updatePackageJson(outputDir, config) + + console.log('...end autogen') +} + +/** + * Updates the Solo package.json by auto-generating the e2e test scripts based on + * the values in the config + * @param {string} outputDir + * @param {Config} config + */ +function updatePackageJson (outputDir, config) { + const packageJsonDir = path.dirname(path.dirname(outputDir)) + const packageJsonFile = path.join(packageJsonDir, 'package.json') + const inputData = fs.readFileSync(packageJsonFile, 'utf8') + const inputLines = inputData.split('\n') + const outputLines = [] + const generatedLines = [] + const firstMarker = '"test-e2e-all":' + const secondMarker = '"merge-clean":' + let skipNext = false + + inputLines.forEach(line => { + if (line.includes(firstMarker)) { + outputLines.push(line) + skipNext = true + const spacePrefix = line.substring(0, line.indexOf('"test-e2e')) + + config.tests.forEach(test => { + const formalNounName = test.name + const kebabCase = changeCase.kebabCase(formalNounName) + generatedLines.push(`${spacePrefix}"test-e2e-${kebabCase}": "NODE_OPTIONS=--experimental-vm-modules JEST_SUITE_NAME='Jest E2E ${formalNounName} Tests' JEST_JUNIT_OUTPUT_NAME='junit-e2e-${kebabCase}.xml' jest --runInBand --detectOpenHandles --forceExit --coverage --coverageDirectory='coverage/e2e-${kebabCase}' ${test.jestPostfix}",`) + }) + + outputLines.push(...generatedLines) + } else if (line.includes(secondMarker)) { + outputLines.push(line) + skipNext = false + } else if (skipNext) { + // do nothing, we generate these lines after we see the firstMarker + } else { + outputLines.push(line) + } + }) + console.log(`outputFile: ${packageJsonFile}`) + fs.writeFileSync(packageJsonFile, outputLines.join('\n')) +} + +/** + * Autogenerate the GitHub workflows files with the entries needed to add the + * E2E test jobs + * @param {string} outputDir + * @param {string} templateDir + * @param {Config} config + */ +function buildWorkflows (outputDir, templateDir, config) { + const templates = [] + fs.readdirSync(templateDir).forEach(file => { + if (file.substring(0, 'template'.length) === 'template') { + templates.push(file) + } + }) + + templates.forEach(template => { + const templateFile = path.join(templateDir, template) + const templateData = fs.readFileSync(templateFile, 'utf8') + const templateLines = templateData.split('\n') + const outputFile = path.join(outputDir, template.substring('template.'.length)) + const outputLines = [] + console.log(`outputFile: ${outputFile}`) + + templateLines.forEach(line => { + const trimmedLine = line.trim() + + switch (trimmedLine) { + case AUTOGENERATE_E2E_TEST_JOBS: + case AUTOGENERATE_E2E_TEST_JOBS_2: + case AUTOGENERATE_WORKFLOW_OUTPUTS_SUB_DIRS: + case AUTOGENERATE_WORKFLOW_OUTPUTS_COVERAGE_REPORTS: + case AUTOGENERATE_INPUTS_SUB_DIRS: + case AUTOGENERATE_INPUTS_COVERAGE_REPORTS: + autogenerateYaml(line, config, outputLines, trimmedLine) + break + case AUTOGENERATE_NEEDS: + case AUTOGENERATE_WITH_SUBDIR: + case AUTOGENERATE_WITH_COVERAGE_REPORT: + case AUTOGENERATE_JOB_OUTPUTS_SUB_DIRS: + case AUTOGENERATE_JOB_OUTPUTS_COVERAGE_REPORTS: + autogenerateLine(line, config, outputLines, trimmedLine) + break + case AUTOGENERATE_DOWNLOAD_JOBS: + autogenerateLine(line, config, outputLines, trimmedLine) + outputLines.pop() // remove the extra new line character + break + default: + outputLines.push(line) + } + }) + + fs.writeFileSync(outputFile, outputLines.join('\n')) + }) +} + +/** + * Generates the YAML for the provided templateKey + * @param {string} line + * @param {Config} config + * @param {string[]} outputLines + * @param {string} templateKey + */ +export function autogenerateYaml (line, config, outputLines, templateKey) { + const spacePrefix = line.substring(0, + line.indexOf(templateKey)) + let suppressEmptyLines = false + + config.tests.forEach(test => { + const outputYaml = {} + + switch (templateKey) { + case AUTOGENERATE_E2E_TEST_JOBS: + case AUTOGENERATE_E2E_TEST_JOBS_2: + generateTestJobs(test, templateKey, outputYaml) + break + default: + generateOutputs(test, templateKey, outputYaml) + suppressEmptyLines = true + } + + const yamlLines = yaml.dump(outputYaml, { lineWidth: -1, quotingType: '"' }).split('\n') + + yamlLines.forEach(function (line) { + line = line.replaceAll('¡', '"') + if (/^\s*$/.test(line)) { + if (!suppressEmptyLines) { + outputLines.push(line) + } + } else { + outputLines.push(`${spacePrefix}${line}`) + } + }) + }) + + if (!suppressEmptyLines) { + outputLines.pop() // remove the extra new line character + } +} + +/** + * Generates the output lines for the provided templateKey + * @param {Test} test + * @param {string} templateKey + * @param {Object} outputYaml + */ +export function generateOutputs (test, templateKey, outputYaml) { + const formalNounName = test.name + const kebabCase = changeCase.kebabCase(formalNounName) + const snakeCase = changeCase.snakeCase(formalNounName) + let outputKey + const outputValue = {} + + switch (templateKey) { + case AUTOGENERATE_WORKFLOW_OUTPUTS_SUB_DIRS: + outputKey = `e2e-${kebabCase}-test-subdir` + outputValue.description = `¡E2E ${formalNounName} Test Subdirectory¡` + outputValue.value = '${{ jobs.env-vars.outputs.e2e_' + snakeCase + '_test_subdir }}' + break + case AUTOGENERATE_WORKFLOW_OUTPUTS_COVERAGE_REPORTS: + outputKey = `e2e-${kebabCase}-coverage-report` + outputValue.description = `¡E2E ${formalNounName} Tests Coverage Report¡` + outputValue.value = '${{ jobs.env-vars.outputs.e2e_' + snakeCase + + '_coverage_report }}' + break + case AUTOGENERATE_INPUTS_SUB_DIRS: + outputKey = `e2e-${kebabCase}-test-subdir` + outputValue.description = `¡E2E ${formalNounName} Test Subdirectory:¡` + outputValue.type = 'string' + outputValue.required = false + outputValue.default = `¡e2e-${kebabCase}¡` + break + case AUTOGENERATE_INPUTS_COVERAGE_REPORTS: + outputKey = `e2e-${kebabCase}-coverage-report` + outputValue.description = `¡E2E ${formalNounName} Coverage Report:¡` + outputValue.type = 'string' + outputValue.required = false + outputValue.default = `¡E2E ${formalNounName} Tests Coverage Report¡` + } + + outputYaml[outputKey] = outputValue +} + +/** + * Generates the test jobs for the provided templateKey + * @param {Test} test + * @param {string} templateKey + * @param {Object} outputYaml + */ +export function generateTestJobs (test, templateKey, outputYaml) { + const formalNounName = test.name + const kebabCase = changeCase.kebabCase(formalNounName) + const testJobKey = `e2e-${kebabCase}-tests` + const testJobValue = {} + testJobValue.name = 'E2E Tests' + + if (templateKey === AUTOGENERATE_E2E_TEST_JOBS) { + testJobValue.if = '${{ github.event_name == \'push\' || github.event.inputs.enable-e2e-tests == \'true\' }}' + } else { + testJobValue.if = '${{ !cancelled() && always() }}' + } + + testJobValue.uses = './.github/workflows/zxc-e2e-test.yaml' + testJobValue.needs = ['env-vars', 'code-style'] + testJobValue.with = { + 'custom-job-label': formalNounName, + 'npm-test-script': 'test-${{ needs.env-vars.outputs.e2e-' + + kebabCase + '-test-subdir }}', + 'coverage-subdirectory': '${{ needs.env-vars.outputs.e2e-' + + kebabCase + '-test-subdir }}', + 'coverage-report-name': '${{ needs.env-vars.outputs.e2e-' + + kebabCase + '-coverage-report }}' + } + + outputYaml[testJobKey] = testJobValue +} + +/** + * Generates the output line for the provided templateKey + * @param {string} line + * @param {Config} config + * @param {string[]} outputLines + * @param {string} templateKey + */ +export function autogenerateLine (line, config, outputLines, templateKey) { + const spacePrefix = line.substring(0, + line.indexOf(templateKey)) + + config.tests.forEach(test => { + const formalNounName = test.name + const kebabCase = changeCase.kebabCase(formalNounName) + const snakeCase = changeCase.snakeCase(formalNounName) + let namePart + let namePart2 + + switch (templateKey) { + case AUTOGENERATE_WITH_SUBDIR: + namePart = `e2e-${kebabCase}-test` + outputLines.push(spacePrefix + namePart + '-subdir: ${{ needs.env-vars.outputs.' + namePart + '-subdir }}') + break + case AUTOGENERATE_WITH_COVERAGE_REPORT: + namePart = `e2e-${kebabCase}` + outputLines.push(spacePrefix + namePart + '-coverage-report: ${{ needs.env-vars.outputs.' + namePart + '-coverage-report }}') + break + case AUTOGENERATE_NEEDS: + namePart = `e2e-${kebabCase}-tests` + outputLines.push(`${spacePrefix}- ${namePart}`) + break + case AUTOGENERATE_JOB_OUTPUTS_SUB_DIRS: + namePart = `e2e_${snakeCase}_test_subdir` + namePart2 = `e2e-${kebabCase}` + outputLines.push(`${spacePrefix}${namePart}: ${namePart2}`) + break + case AUTOGENERATE_JOB_OUTPUTS_COVERAGE_REPORTS: + namePart = `e2e_${snakeCase}_coverage_report` + outputLines.push(`${spacePrefix}${namePart}: "E2E ${formalNounName} Tests Coverage Report"`) + break + case AUTOGENERATE_DOWNLOAD_JOBS: + outputLines.push(`${spacePrefix}- name: Download E2E ${formalNounName} Coverage Report`) + outputLines.push(`${spacePrefix} uses: ${config.downloadArtifactAction} # ${config.downloadArtifactActionComment}`) + outputLines.push(spacePrefix + ' if: ${{ (inputs.enable-codecov-analysis || inputs.enable-codacy-coverage) && inputs.enable-e2e-coverage-report && !cancelled() && !failure() }}') + outputLines.push(`${spacePrefix} with:`) + outputLines.push(spacePrefix + ' name: ${{ inputs.e2e-' + kebabCase + '-coverage-report }}') + outputLines.push(spacePrefix + ' path: \'coverage/${{ inputs.e2e-' + kebabCase + '-test-subdir }}\'') + outputLines.push('') + } + }) +} diff --git a/.github/workflows/templates/config.yaml b/.github/workflows/templates/config.yaml new file mode 100644 index 000000000..d1ecdb8b2 --- /dev/null +++ b/.github/workflows/templates/config.yaml @@ -0,0 +1,23 @@ +downloadArtifactAction: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 +downloadArtifactActionComment: v4.1.8 +tests: + # name: capitalized noun form + # testFilePrefix: node-update # for node-update.test.mjs + - name: Standard + jestPostfix: --testPathIgnorePatterns=\".*/unit/.*\" --testPathIgnorePatterns=\".*/e2e/commands/mirror_node.*\" --testPathIgnorePatterns=\".*/e2e/commands/node.*\" --testPathIgnorePatterns=\".*/e2e/commands/relay.*\" + - name: Mirror Node + jestPostfix: --testRegex=\".*\\/e2e\\/commands\\/mirror_node\\.test\\.mjs\" + - name: Node PEM Stop + jestPostfix: --testRegex=\".*\\/e2e\\/commands\\/node_pem_stop\\.test\\.mjs\" + - name: Node PEM Kill + jestPostfix: --testRegex=\".*\\/e2e\\/commands\\/node_pem_kill\\.test\\.mjs\" + - name: Node Local Build + jestPostfix: --testRegex=\".*\\/e2e\\/commands\\/node_local.*\\.test\\.mjs\" + - name: Node Add + jestPostfix: --testRegex=\".*\\/e2e\\/commands\\/node_add.*\\.test\\.mjs\" + - name: Node Update + jestPostfix: --testRegex=\".*\\/e2e\\/commands\\/node_update.*\\.test\\.mjs\" + - name: Node Delete + jestPostfix: --testRegex=\".*\\/e2e\\/commands\\/node_delete.*\\.test\\.mjs\" + - name: Relay + jestPostfix: --testRegex=\".*\\/e2e\\/commands\\/relay\\.test\\.mjs\" diff --git a/.github/workflows/templates/template.flow-build-application.yaml b/.github/workflows/templates/template.flow-build-application.yaml new file mode 100644 index 000000000..51c76dee3 --- /dev/null +++ b/.github/workflows/templates/template.flow-build-application.yaml @@ -0,0 +1,88 @@ +## +# Copyright (C) 2022-2023 Hedera Hashgraph, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +## + +name: "Build Application" +on: + workflow_dispatch: + inputs: + enable-unit-tests: + description: "Unit Testing Enabled" + type: boolean + required: false + default: true + enable-e2e-tests: + description: "E2E Testing Enabled" + type: boolean + required: false + default: false + enable-snyk-scan: + description: "Snyk Scan Enabled" + type: boolean + required: false + default: false + push: + branches: + - main + - 'release/*' + +defaults: + run: + shell: bash + +jobs: + env-vars: + name: Set Environment Variables + uses: ./.github/workflows/zxc-env-vars.yaml + with: + custom-job-label: Set Environment Variables + + code-style: + name: Code Style + uses: ./.github/workflows/zxc-code-style.yaml + with: + custom-job-label: Standard + + unit-tests: + name: Unit Tests + uses: ./.github/workflows/zxc-unit-test.yaml + if: ${{ github.event_name == 'push' || github.event.inputs.enable-unit-tests == 'true' }} + needs: + - code-style + with: + custom-job-label: Standard + + # {AUTOGENERATE-E2E-TEST-JOBS} + + analyze: + name: Analyze + uses: ./.github/workflows/zxc-code-analysis.yaml + needs: + - env-vars + - unit-tests + # {AUTOGENERATE-NEEDS} + if: ${{ (github.event_name == 'push' || github.event.inputs.enable-unit-tests == 'true' || github.event.inputs.enable-e2e-tests == 'true') && !failure() && !cancelled() }} + with: + custom-job-label: Source Code + #enable-snyk-scan: ${{ github.event_name == 'push' || github.event.inputs.enable-snyk-scan == 'true' }} + enable-codecov-analysis: true + enable-codacy-coverage: true + enable-e2e-coverage-report: ${{ github.event_name == 'push' || github.event.inputs.enable-e2e-tests == 'true' }} + # {AUTOGENERATE-WITH-SUBDIR} + # {AUTOGENERATE-WITH-COVERAGE-REPORT} + secrets: + snyk-token: ${{ secrets.SNYK_TOKEN }} + codecov-token: ${{ secrets.CODECOV_TOKEN }} + codacy-project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} diff --git a/.github/workflows/templates/template.flow-pull-request-checks.yaml b/.github/workflows/templates/template.flow-pull-request-checks.yaml new file mode 100644 index 000000000..0c4cb25be --- /dev/null +++ b/.github/workflows/templates/template.flow-pull-request-checks.yaml @@ -0,0 +1,89 @@ +## +# Copyright (C) 2023-2024 Hedera Hashgraph, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +## + +name: "PR Checks" +on: + workflow_dispatch: + pull_request: + types: + - opened + - reopened + - synchronize + +defaults: + run: + shell: bash + +concurrency: + group: pr-checks-${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + env-vars: + name: Set Environment Variables + uses: ./.github/workflows/zxc-env-vars.yaml + with: + custom-job-label: Set Environment Variables + + code-style: + name: Code Style + uses: ./.github/workflows/zxc-code-style.yaml + with: + custom-job-label: Standard + + unit-tests: + name: Unit Tests + uses: ./.github/workflows/zxc-unit-test.yaml + needs: + - code-style + with: + custom-job-label: Standard + + # {AUTOGENERATE-E2E-TEST-JOBS-2} + + codecov: + name: CodeCov + uses: ./.github/workflows/zxc-code-analysis.yaml + needs: + - env-vars + - unit-tests + # {AUTOGENERATE-NEEDS} + if: ${{ github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name }} + with: + custom-job-label: Standard + enable-codecov-analysis: true + enable-e2e-coverage-report: true + # {AUTOGENERATE-WITH-SUBDIR} + # {AUTOGENERATE-WITH-COVERAGE-REPORT} + secrets: + codecov-token: ${{ secrets.CODECOV_TOKEN }} + + codacy-coverage: + name: Codacy + uses: ./.github/workflows/zxc-code-analysis.yaml + needs: + - env-vars + - unit-tests + # {AUTOGENERATE-NEEDS} + if: ${{ github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name }} + with: + custom-job-label: Coverage + enable-codacy-coverage: true + enable-e2e-coverage-report: true + # {AUTOGENERATE-WITH-SUBDIR} + # {AUTOGENERATE-WITH-COVERAGE-REPORT} + secrets: + codacy-project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} diff --git a/.github/workflows/templates/template.zxc-code-analysis.yaml b/.github/workflows/templates/template.zxc-code-analysis.yaml new file mode 100644 index 000000000..7f7a0a762 --- /dev/null +++ b/.github/workflows/templates/template.zxc-code-analysis.yaml @@ -0,0 +1,185 @@ +## +# Copyright (C) 2023-2024 Hedera Hashgraph, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +## + +name: "ZXC: Code Analysis" +# The purpose of this reusable workflow is to perform static code analysis and code coverage reporting. +# This reusable component is called by the following workflows: +# - .github/workflows/flow-pull-request-checks.yaml +# - .github/workflows/flow-build-application.yaml +# +# This workflow is only run if the pull request is coming from the original repository and not a fork. + +on: + workflow_call: + inputs: + enable-codecov-analysis: + description: "CodeCov Analysis Enabled" + type: boolean + required: false + default: false + enable-codacy-coverage: + description: "Codacy Coverage Enabled" + type: boolean + required: false + default: false + enable-e2e-coverage-report: + description: "E2E Coverage Report Enabled" + type: boolean + required: false + default: false + enable-snyk-scan: + description: "Snyk Scan Enabled" + type: boolean + required: false + default: false + node-version: + description: "NodeJS Version:" + type: string + required: false + default: "20.14.0" + custom-job-label: + description: "Custom Job Label:" + type: string + required: false + default: "Analyze" + # {AUTOGENERATE-INPUTS-SUB-DIRS} + # {AUTOGENERATE-INPUTS-COVERAGE-REPORTS} + secrets: + snyk-token: + description: "The Snyk access token is used by Snyk to analyze the code for vulnerabilities " + required: false + codecov-token: + description: "The CodeCov access token is used by CodeCov.io to analyze the code coverage " + required: false + codacy-project-token: + description: "The Codacy project token used to report code coverage." + required: false + +defaults: + run: + shell: bash + +permissions: + contents: read + actions: read + pull-requests: write + checks: write + statuses: write + +jobs: + analyze: + name: ${{ inputs.custom-job-label || 'Analyze' }} + runs-on: solo-linux-medium + steps: + - name: Checkout Code + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + ref: ${{ github.event.workflow_run.head_branch }} + fetch-depth: ${{ inputs.enable-sonar-analysis && '0' || '' }} + + - name: Setup Node with Retry + uses: Wandalen/wretry.action@6feedb7dedadeb826de0f45ff482b53b379a7844 # v3.5.0 + with: + action: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + with: | + node-version: ${{ inputs.node-version }} + cache: npm + attempt_limit: 3 + attempt_delay: 5000 + + - name: Download Unit Test Coverage Report + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + if: ${{ (inputs.enable-codecov-analysis || inputs.enable-codacy-coverage) && !cancelled() && !failure() }} + with: + name: Unit Test Coverage Report + path: 'coverage/unit' + + # {AUTOGENERATE-DOWNLOAD-JOBS} + + - name: Publish To Codecov + uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0 + if: ${{ inputs.enable-codecov-analysis && !cancelled() && !failure() }} + env: + CODECOV_TOKEN: ${{ secrets.codecov-token }} + with: + verbose: true + directory: 'coverage' + + - name: Publish to Codacy + env: + CODACY_PROJECT_TOKEN: ${{ secrets.codacy-project-token }} + if: ${{ inputs.enable-codacy-coverage && !cancelled() && !failure() }} + run: bash <(curl -Ls https://coverage.codacy.com/get.sh) report -l Javascript $(find . -name 'lcov.info' -printf '-r %p ') + + - name: Setup Snyk + env: + SNYK_TOKEN: ${{ secrets.snyk-token }} + if: ${{ inputs.enable-snyk-scan && !cancelled() && !failure() }} + run: npm install -g snyk snyk-to-html @wcj/html-to-markdown-cli + + - name: Snyk Scan + id: snyk + env: + SNYK_TOKEN: ${{ secrets.snyk-token }} + if: ${{ inputs.enable-snyk-scan && !cancelled() && !failure() }} + run: snyk test --org=release-engineering-N6EoZVZn3jw4qNuVkiG5Qs --all-projects --severity-threshold=high --json-file-output=snyk-test.json + + - name: Snyk Code + id: snyk-code + env: + SNYK_TOKEN: ${{ secrets.snyk-token }} + if: ${{ inputs.enable-snyk-scan && !cancelled() && !failure() }} + run: snyk code test --org=release-engineering-N6EoZVZn3jw4qNuVkiG5Qs --severity-threshold=high --json-file-output=snyk-code.json + + - name: Publish Snyk Results + if: ${{ inputs.enable-snyk-scan && !cancelled() && !failure() }} + run: | + if [[ -f "snyk-test.json" && -n "$(cat snyk-test.json | tr -d '[:space:]')" ]]; then + snyk-to-html -i snyk-test.json -o snyk-test.html --summary + html-to-markdown snyk-test.html -o snyk + cat snyk/snyk-test.html.md >> $GITHUB_STEP_SUMMARY + fi + + - name: Publish Snyk Code Results + if: ${{ inputs.enable-snyk-scan && !cancelled() && !failure() }} + run: | + if [[ -f "snyk-code.json" && -n "$(cat snyk-code.json | tr -d '[:space:]')" ]]; then + snyk-to-html -i snyk-code.json -o snyk-code.html --summary + html-to-markdown snyk-code.html -o snyk + cat snyk/snyk-code.html.md >> $GITHUB_STEP_SUMMARY + fi + + - name: Check Snyk Files + if: ${{ always() }} + run: | + echo "::group::Snyk File List" + ls -lah snyk* || true + echo "::endgroup::" + echo "::group::Snyk Test Contents" + cat snyk-test.json || true + echo "::endgroup::" + echo "::group::Snyk Code Contents" + cat snyk-code.json || true + echo "::endgroup::" + + - name: Publish Snyk Reports + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + if: ${{ inputs.enable-snyk-scan && !cancelled() && !failure() }} + with: + name: Snyk Reports + path: | + snyk-*.html + snyk-*.json diff --git a/.github/workflows/templates/template.zxc-env-vars.yaml b/.github/workflows/templates/template.zxc-env-vars.yaml new file mode 100644 index 000000000..d2b3b27c8 --- /dev/null +++ b/.github/workflows/templates/template.zxc-env-vars.yaml @@ -0,0 +1,44 @@ +## +# Copyright (C) 2023-2024 Hedera Hashgraph, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +## + +name: "ZXC: Environment Variables" +# The purpose of this reusable workflow is to provide environment variables for use in re-usable workflows. + +on: + workflow_call: + inputs: + custom-job-label: + description: "Custom Job Label:" + type: string + required: false + default: "Set Environment Variables" + outputs: + # {AUTOGENERATE-WORKFLOW-OUTPUTS-SUB-DIRS} + # {AUTOGENERATE-WORKFLOW-OUTPUTS-COVERAGE-REPORTS} + +defaults: + run: + shell: bash + +jobs: + env-vars: + name: ${{ inputs.custom-job-label || 'Set Environment Variables' }} + runs-on: solo-linux-medium + outputs: + # {AUTOGENERATE-JOB-OUTPUTS-SUB-DIRS} + # {AUTOGENERATE-JOB-OUTPUTS-COVERAGE-REPORTS} + steps: + - run: echo "Exposing environment variables to reusable workflows"