From d3e6d78dacba3338ded915cc759c12120296fcd6 Mon Sep 17 00:00:00 2001 From: DarkIsDude Date: Wed, 25 Feb 2026 16:24:10 +0100 Subject: [PATCH 1/8] =?UTF-8?q?=F0=9F=9A=A8=20add=20eslint=20configuration?= =?UTF-8?q?=20to=20monitor=20async/await?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: CLDSRV-860 --- eslint.config.mjs | 6 ++ package.json | 7 +- yarn.lock | 165 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 175 insertions(+), 3 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 7d2efd9cb0..d647d0f2af 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,4 +1,6 @@ import mocha from "eslint-plugin-mocha"; +import promise from "eslint-plugin-promise"; +import n from "eslint-plugin-n"; import path from "node:path"; import { fileURLToPath } from "node:url"; import js from "@eslint/js"; @@ -15,6 +17,8 @@ const compat = new FlatCompat({ export default [...compat.extends('@scality/scality'), { plugins: { mocha, + promise, + n, }, languageOptions: { @@ -67,5 +71,7 @@ export default [...compat.extends('@scality/scality'), { "quote-props": "off", "mocha/no-exclusive-tests": "error", "no-redeclare": ["error", { "builtinGlobals": false }], + "promise/prefer-await-to-then": "warn", + "n/callback-return": "warn", }, }]; diff --git a/package.json b/package.json index 96646c65a4..9a65954bda 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,8 @@ "eslint": "^9.14.0", "eslint-plugin-import": "^2.31.0", "eslint-plugin-mocha": "^10.5.0", + "eslint-plugin-n": "^17.24.0", + "eslint-plugin-promise": "^7.2.1", "express": "^4.21.1", "ioredis": "^5.4.1", "istanbul": "^0.4.5", @@ -79,6 +81,7 @@ "nyc": "^15.1.0", "pino-pretty": "^13.1.3", "sinon": "^13.0.1", + "ts-morph": "^27.0.2", "tv4": "^1.3.0" }, "resolutions": { @@ -141,6 +144,8 @@ "test_sur": "mocha --reporter mocha-multi-reporters --reporter-options configFile=$INIT_CWD/tests/reporter-config.json --recursive tests/sur --exit", "multiple_backend_test": "CI=true S3BACKEND=mem S3METADATA=mem S3DATA=multiple mocha --reporter mocha-multi-reporters --reporter-options configFile=$INIT_CWD/tests/reporter-config.json -t 20000 --recursive tests/multipleBackend --exit", "cover": "nyc --clean --silent yarn run", - "postcover": "nyc report --report-dir ./coverage/test --reporter=lcov" + "postcover": "nyc report --report-dir ./coverage/test --reporter=lcov", + "count-async": "node scripts/count-async-functions.mjs", + "check-diff-async": "node scripts/check-diff-async.mjs" } } diff --git a/yarn.lock b/yarn.lock index 4d08dade85..5d2370e228 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2749,6 +2749,13 @@ enabled "2.0.x" kuler "^2.0.0" +"@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.4.0", "@eslint-community/eslint-utils@^4.5.0": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz#4e90af67bc51ddee6cdef5284edf572ec376b595" + integrity sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ== + dependencies: + eslint-visitor-keys "^3.4.3" + "@eslint-community/eslint-utils@^4.2.0": version "4.5.1" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz#b0fc7e06d0c94f801537fd4237edc2706d3b8e4c" @@ -2756,6 +2763,11 @@ dependencies: eslint-visitor-keys "^3.4.3" +"@eslint-community/regexpp@^4.11.0": + version "4.12.2" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b" + integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== + "@eslint-community/regexpp@^4.12.1": version "4.12.1" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" @@ -4948,6 +4960,15 @@ resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.0.0.tgz#f193b73dc316c4170f2e82a881da0f550d551b9c" integrity sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA== +"@ts-morph/common@~0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.28.1.tgz#10ec52182d5c310832b669af7784a34fc3da3ca1" + integrity sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g== + dependencies: + minimatch "^10.0.1" + path-browserify "^1.0.1" + tinyglobby "^0.2.14" + "@types/async@^3.2.24": version "3.2.24" resolved "https://registry.yarnpkg.com/@types/async/-/async-3.2.24.tgz#3a96351047575bbcf2340541b2d955a35339608f" @@ -5532,6 +5553,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +balanced-match@^4.0.2: + version "4.0.4" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a" + integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== + base-x@3.0.8: version "3.0.8" resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d" @@ -5623,6 +5649,13 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" +brace-expansion@^5.0.2: + version "5.0.3" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.3.tgz#6a9c6c268f85b53959ec527aeafe0f7300258eef" + integrity sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA== + dependencies: + balanced-match "^4.0.2" + braces@~3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" @@ -5901,6 +5934,11 @@ cluster-key-slot@^1.1.0: resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== +code-block-writer@^13.0.3: + version "13.0.3" + resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-13.0.3.tgz#90f8a84763a5012da7af61319dd638655ae90b5b" + integrity sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg== + color-convert@^1.9.0, color-convert@^1.9.3: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -6433,6 +6471,14 @@ engine.io@~6.6.0: engine.io-parser "~5.2.1" ws "~8.17.1" +enhanced-resolve@^5.17.1: + version "5.19.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz#6687446a15e969eaa63c2fa2694510e17ae6d97c" + integrity sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.3.0" + entities@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" @@ -6638,6 +6684,13 @@ escodegen@1.8.x: optionalDependencies: source-map "~0.2.0" +eslint-compat-utils@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz#7fc92b776d185a70c4070d03fd26fde3d59652e4" + integrity sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q== + dependencies: + semver "^7.5.4" + eslint-import-resolver-node@^0.3.9: version "0.3.9" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" @@ -6654,6 +6707,15 @@ eslint-module-utils@^2.12.0: dependencies: debug "^3.2.7" +eslint-plugin-es-x@^7.8.0: + version "7.8.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz#a207aa08da37a7923f2a9599e6d3eb73f3f92b74" + integrity sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ== + dependencies: + "@eslint-community/eslint-utils" "^4.1.2" + "@eslint-community/regexpp" "^4.11.0" + eslint-compat-utils "^0.5.1" + eslint-plugin-import@^2.31.0: version "2.31.0" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz#310ce7e720ca1d9c0bb3f69adfd1c6bdd7d9e0e7" @@ -6688,6 +6750,28 @@ eslint-plugin-mocha@^10.5.0: globals "^13.24.0" rambda "^7.4.0" +eslint-plugin-n@^17.24.0: + version "17.24.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-17.24.0.tgz#b66fa05f7a6c1ba16768f0921b8974147dddd060" + integrity sha512-/gC7/KAYmfNnPNOb3eu8vw+TdVnV0zhdQwexsw6FLXbhzroVj20vRn2qL8lDWDGnAQ2J8DhdfvXxX9EoxvERvw== + dependencies: + "@eslint-community/eslint-utils" "^4.5.0" + enhanced-resolve "^5.17.1" + eslint-plugin-es-x "^7.8.0" + get-tsconfig "^4.8.1" + globals "^15.11.0" + globrex "^0.1.2" + ignore "^5.3.2" + semver "^7.6.3" + ts-declaration-location "^1.0.6" + +eslint-plugin-promise@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-7.2.1.tgz#a0652195700aea40b926dc3c74b38e373377bfb0" + integrity sha512-SWKjd+EuvWkYaS+uN2csvj0KoP43YTu7+phKQ5v+xw6+A0gutVX2yqCeCkC3uLCJFiPfR2dD8Es5L7yUsmvEaA== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + eslint-scope@^8.3.0: version "8.3.0" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.3.0.tgz#10cd3a918ffdd722f5f3f7b5b83db9b23c87340d" @@ -6956,6 +7040,11 @@ fast-xml-parser@^5.0.7: node-addon-api "^8.3.0" node-gyp "^11.1.0" +fdir@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== + fecha@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" @@ -7253,6 +7342,13 @@ get-symbol-description@^1.1.0: es-errors "^1.3.0" get-intrinsic "^1.2.6" +get-tsconfig@^4.8.1: + version "4.13.6" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.13.6.tgz#2fbfda558a98a691a798f123afd95915badce876" + integrity sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw== + dependencies: + resolve-pkg-maps "^1.0.0" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -7326,6 +7422,11 @@ globals@^14.0.0: resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== +globals@^15.11.0: + version "15.15.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-15.15.0.tgz#7c4761299d41c32b075715a4ce1ede7897ff72a8" + integrity sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg== + globalthis@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" @@ -7334,6 +7435,11 @@ globalthis@^1.0.4: define-properties "^1.2.1" gopd "^1.0.1" +globrex@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" + integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== + google-auth-library@^1.3.1: version "1.6.1" resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-1.6.1.tgz#9c73d831ad720c0c3048ab89d0ffdec714d07dd2" @@ -7370,7 +7476,7 @@ gopd@^1.0.1, gopd@^1.2.0: resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== -graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.2.6: +graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.6: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -7598,7 +7704,7 @@ ignore-by-default@^1.0.1: resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== -ignore@^5.2.0: +ignore@^5.2.0, ignore@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== @@ -8914,6 +9020,13 @@ mime@^2.2.0: dependencies: brace-expansion "^1.1.7" +minimatch@^10.0.1: + version "10.2.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.3.tgz#c0ef582f21071b0123a5bbf275252ebda921fbf6" + integrity sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg== + dependencies: + brace-expansion "^5.0.2" + minimatch@^9.0.4, minimatch@^9.0.5: version "9.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" @@ -9560,6 +9673,11 @@ parseurl@^1.3.3, parseurl@~1.3.3: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -9625,6 +9743,11 @@ picomatch@^2.0.4, picomatch@^2.2.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +picomatch@^4.0.2, picomatch@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" + integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== + pidtree@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a" @@ -9997,6 +10120,11 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + resolve@1.1.x: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" @@ -10133,6 +10261,11 @@ semver@^7.3.5, semver@^7.5.3, semver@^7.5.4: resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== +semver@^7.6.3: + version "7.7.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" + integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== + semver@~5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.1.1.tgz#a3292a373e6f3e0798da0b20641b9a9c5bc47e19" @@ -10716,6 +10849,11 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +tapable@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.3.0.tgz#7e3ea6d5ca31ba8e078b560f0d83ce9a14aa8be6" + integrity sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg== + tar@^7.4.3: version "7.4.3" resolved "https://registry.yarnpkg.com/tar/-/tar-7.4.3.tgz#88bbe9286a3fcd900e94592cda7a22b192e80571" @@ -10759,6 +10897,14 @@ tiny-each-async@2.0.3: resolved "https://registry.yarnpkg.com/tiny-each-async/-/tiny-each-async-2.0.3.tgz#8ebbbfd6d6295f1370003fbb37162afe5a0a51d1" integrity sha512-5ROII7nElnAirvFn8g7H7MtpfV1daMcyfTGQwsn/x2VtyV+VPiO5CjReCJtWLvoKTDEDmZocf3cNPraiMnBXLA== +tinyglobby@^0.2.14: + version "0.2.15" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" + integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== + dependencies: + fdir "^6.5.0" + picomatch "^4.0.3" + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -10815,6 +10961,21 @@ triple-beam@^1.3.0: resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== +ts-declaration-location@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/ts-declaration-location/-/ts-declaration-location-1.0.7.tgz#d4068fe9975828b3b453b3ab112b4711d8267688" + integrity sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA== + dependencies: + picomatch "^4.0.2" + +ts-morph@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-27.0.2.tgz#7b2fcce6822eeca3942fa6c601f159d5920b1422" + integrity sha512-fhUhgeljcrdZ+9DZND1De1029PrE+cMkIP7ooqkLRTrRLTqcki2AstsyJm0vRNbTbVCNJ0idGlbBrfqc7/nA8w== + dependencies: + "@ts-morph/common" "~0.28.1" + code-block-writer "^13.0.3" + tsconfig-paths@^3.15.0: version "3.15.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" From 155ad1912b08fc7fde5f49a7867a256d61952116 Mon Sep 17 00:00:00 2001 From: DarkIsDude Date: Wed, 25 Feb 2026 16:46:58 +0100 Subject: [PATCH 2/8] =?UTF-8?q?=F0=9F=91=B7=20update=20CI=20to=20add=20new?= =?UTF-8?q?=20check=20on=20async/await=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: CLDSRV-860 --- .github/scripts/check-diff-async.mjs | 142 ++++++++++++++++++++++ .github/scripts/count-async-functions.mjs | 89 ++++++++++++++ .github/workflows/tests.yaml | 18 ++- package.json | 4 +- 4 files changed, 250 insertions(+), 3 deletions(-) create mode 100644 .github/scripts/check-diff-async.mjs create mode 100644 .github/scripts/count-async-functions.mjs diff --git a/.github/scripts/check-diff-async.mjs b/.github/scripts/check-diff-async.mjs new file mode 100644 index 0000000000..b725bc40f7 --- /dev/null +++ b/.github/scripts/check-diff-async.mjs @@ -0,0 +1,142 @@ +/** + * Check that all new/modified functions in the current git diff use async/await. + * Fails with exit code 1 if any additions introduce callback-style functions or .then() chains. + * + * Usage: node scripts/check-diff-async.mjs + * In CI: runs against the current PR diff (files changed vs base branch) + */ +import { execSync } from 'node:child_process'; +import { Project, SyntaxKind } from 'ts-morph'; + +const CALLBACK_PARAM_PATTERN = /^(cb|callback|next|done|err)$/i; + +function getChangedJsFiles() { + const base = process.env.GITHUB_BASE_REF + ? `origin/${process.env.GITHUB_BASE_REF}` + : 'HEAD'; + const output = execSync(`git diff --name-only --diff-filter=ACMR ${base} -- '*.js'`, { + encoding: 'utf8', + }).trim(); + + return output ? output.split('\n').filter(f => f.endsWith('.js')) : []; +} + +/** + * Get added line numbers for a file in the current diff. + */ +function getAddedLineNumbers(filePath) { + const base = process.env.GITHUB_BASE_REF + ? `origin/${process.env.GITHUB_BASE_REF}` + : 'HEAD'; + const diff = execSync(`git diff ${base} -- ${filePath}`, { encoding: 'utf8' }); + const addedLines = new Set(); + let currentLine = 0; + + for (const line of diff.split('\n')) { + const hunkMatch = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/); + + if (hunkMatch) { + currentLine = parseInt(hunkMatch[1], 10) - 1; + continue; + } + + if (line.startsWith('+') && !line.startsWith('+++')) { + currentLine++; + addedLines.add(currentLine); + } else if (!line.startsWith('-')) { + currentLine++; + } + } + + return addedLines; +} + +const changedFiles = getChangedJsFiles(); +if (changedFiles.length === 0) { + console.log('No changed JS files to check.'); + process.exit(0); +} + +console.log(`Checking ${changedFiles.length} changed JS file(s) for async/await compliance...\n`); + +const project = new Project({ + compilerOptions: { allowJs: true, noEmit: true }, + skipAddingFilesFromTsConfig: true, +}); + +const filesToCheck = changedFiles.filter(f => + !f.startsWith('tests/') && + !f.startsWith('node_modules/') && + ( + f.startsWith('lib/') || + f.startsWith('bin/') || + !f.includes('/') + ) +); +if (filesToCheck.length === 0) { + console.log('No source JS files in diff (tests and node_modules excluded).'); + process.exit(0); +} + +project.addSourceFilesAtPaths(filesToCheck); + +const violations = []; + +for (const sourceFile of project.getSourceFiles()) { + const filePath = sourceFile.getFilePath().replace(process.cwd() + '/', ''); + const addedLines = getAddedLineNumbers(filePath); + + if (addedLines.size === 0) continue; + + const functions = [ + ...sourceFile.getDescendantsOfKind(SyntaxKind.FunctionDeclaration), + ...sourceFile.getDescendantsOfKind(SyntaxKind.FunctionExpression), + ...sourceFile.getDescendantsOfKind(SyntaxKind.ArrowFunction), + ...sourceFile.getDescendantsOfKind(SyntaxKind.MethodDeclaration), + ]; + + for (const fn of functions) { + if (fn.isAsync()) continue; + + const startLine = fn.getStartLineNumber(); + if (!addedLines.has(startLine)) continue; + + const params = fn.getParameters(); + const lastParam = params[params.length - 1]; + if (lastParam && CALLBACK_PARAM_PATTERN.test(lastParam.getName())) { + violations.push({ + file: filePath, + line: startLine, + type: 'callback', + detail: `function has callback parameter '${lastParam.getName()}'`, + }); + } + } + + const propertyAccesses = sourceFile.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression); + for (const access of propertyAccesses) { + if (access.getName() !== 'then') continue; + const line = access.getStartLineNumber(); + if (addedLines.has(line)) { + violations.push({ + file: filePath, + line, + type: 'then-chain', + detail: 'use await instead of .then()', + }); + } + } +} + +if (violations.length === 0) { + console.log('✓ All new code in the diff uses async/await.'); + process.exit(0); +} + +console.error(`✗ Found ${violations.length} async/await violation(s) in the diff:\n`); +for (const v of violations) { + console.error(` ${v.file}:${v.line} [${v.type}] ${v.detail}`); +} +console.error('\nNew code must use async/await instead of callbacks or .then() chains.'); +console.error('See the async/await migration guide in CONTRIBUTING.md for help.'); +process.exit(1); diff --git a/.github/scripts/count-async-functions.mjs b/.github/scripts/count-async-functions.mjs new file mode 100644 index 0000000000..300d4a70b9 --- /dev/null +++ b/.github/scripts/count-async-functions.mjs @@ -0,0 +1,89 @@ +/** + * Count async vs callback-style functions across the codebase using ts-morph. + * Used in CI to track async/await migration progress over time. + * + * Usage: node scripts/count-async-functions.mjs + */ +import { Project, SyntaxKind } from 'ts-morph'; + +const project = new Project({ + compilerOptions: { + allowJs: true, + noEmit: true, + }, + skipAddingFilesFromTsConfig: true, +}); + +project.addSourceFilesAtPaths([ + 'lib/**/*.js', + 'index.js', + 'dataserver.js', + 'mdserver.js', + 'managementAgent.js', + 'bin/**/*.js' +]); + +let asyncFunctions = 0; +let totalFunctions = 0; +let callbackFunctions = 0; +let thenChains = 0; + +const CALLBACK_PARAM_PATTERN = /^(cb|callback|next|done|err)$/i; + +for (const sourceFile of project.getSourceFiles()) { + const functions = [ + ...sourceFile.getDescendantsOfKind(SyntaxKind.FunctionDeclaration), + ...sourceFile.getDescendantsOfKind(SyntaxKind.FunctionExpression), + ...sourceFile.getDescendantsOfKind(SyntaxKind.ArrowFunction), + ...sourceFile.getDescendantsOfKind(SyntaxKind.MethodDeclaration), + ]; + + for (const fn of functions) { + totalFunctions++; + + if (fn.isAsync()) { + asyncFunctions++; + continue; + } + + const params = fn.getParameters(); + const lastParam = params[params.length - 1]; + if (lastParam && CALLBACK_PARAM_PATTERN.test(lastParam.getName())) { + callbackFunctions++; + } + } + + const propertyAccesses = sourceFile.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression); + for (const access of propertyAccesses) { + if (access.getName() === 'then') { + thenChains++; + } + } +} + +const migrationPercent = totalFunctions > 0 + ? ((asyncFunctions / totalFunctions) * 100).toFixed(1) + : '0.0'; + +console.log('=== Async/Await Migration Progress ==='); +console.log(`Total functions: ${totalFunctions}`); +console.log(`Async functions: ${asyncFunctions} (${migrationPercent}%)`); +console.log(`Callback functions: ${callbackFunctions}`); +console.log(`Remaining .then(): ${thenChains}`); +console.log(''); +console.log(`Migration: ${asyncFunctions}/${totalFunctions} functions (${migrationPercent}%)`); + +if (process.env.GITHUB_STEP_SUMMARY) { + const { writeFileSync, appendFileSync } = await import('node:fs'); + appendFileSync(process.env.GITHUB_STEP_SUMMARY, [ + '## Async/Await Migration Progress', + '', + `| Metric | Count |`, + `|--------|-------|`, + `| Total functions | ${totalFunctions} |`, + `| Async functions | ${asyncFunctions} (${migrationPercent}%) |`, + `| Callback-style functions | ${callbackFunctions} |`, + `| Remaining \`.then()\` chains | ${thenChains} |`, + '', + ].join('\n')); +} diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index cb03872555..f246e939c0 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -96,13 +96,29 @@ jobs: - name: Install python deps run: pip install flake8 - name: Lint Javascript - run: yarn run --silent lint -- --max-warnings 0 + run: yarn run --silent lint - name: Lint Markdown run: yarn run --silent lint_md - name: Lint python run: flake8 $(git ls-files "*.py") - name: Lint Yaml run: yamllint -c yamllint.yml $(git ls-files "*.yml") + - name: Check async/await compliance in diff + run: yarn run check-diff-async + + async-migration-report: + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '22' + cache: yarn + - name: install dependencies + run: yarn install --frozen-lockfile --network-concurrency 1 + - name: Count async/await migration progress + run: yarn run count-async unit-tests: runs-on: ubuntu-24.04 diff --git a/package.json b/package.json index 9a65954bda..81ba9ae638 100644 --- a/package.json +++ b/package.json @@ -145,7 +145,7 @@ "multiple_backend_test": "CI=true S3BACKEND=mem S3METADATA=mem S3DATA=multiple mocha --reporter mocha-multi-reporters --reporter-options configFile=$INIT_CWD/tests/reporter-config.json -t 20000 --recursive tests/multipleBackend --exit", "cover": "nyc --clean --silent yarn run", "postcover": "nyc report --report-dir ./coverage/test --reporter=lcov", - "count-async": "node scripts/count-async-functions.mjs", - "check-diff-async": "node scripts/check-diff-async.mjs" + "count-async": "node .github/scripts/count-async-functions.mjs", + "check-diff-async": "node .github/scripts/check-diff-async.mjs" } } From 869e8b2ab31a022f1d45b02df0ca1a76f98c4e92 Mon Sep 17 00:00:00 2001 From: DarkIsDude Date: Thu, 19 Mar 2026 12:01:24 +0100 Subject: [PATCH 3/8] =?UTF-8?q?=F0=9F=91=B7=20remove=20then=20check=20from?= =?UTF-8?q?=20the=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: CLDSRV-860 --- .github/scripts/check-diff-async.mjs | 20 +++----------------- .github/scripts/count-async-functions.mjs | 2 +- .github/workflows/tests.yaml | 2 ++ 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/.github/scripts/check-diff-async.mjs b/.github/scripts/check-diff-async.mjs index b725bc40f7..9995374968 100644 --- a/.github/scripts/check-diff-async.mjs +++ b/.github/scripts/check-diff-async.mjs @@ -1,6 +1,6 @@ /** * Check that all new/modified functions in the current git diff use async/await. - * Fails with exit code 1 if any additions introduce callback-style functions or .then() chains. + * Fails with exit code 1 if any additions introduce callback-style functions. * * Usage: node scripts/check-diff-async.mjs * In CI: runs against the current PR diff (files changed vs base branch) @@ -8,7 +8,7 @@ import { execSync } from 'node:child_process'; import { Project, SyntaxKind } from 'ts-morph'; -const CALLBACK_PARAM_PATTERN = /^(cb|callback|next|done|err)$/i; +const CALLBACK_PARAM_PATTERN = /^(cb|callback|next|done)$/i; function getChangedJsFiles() { const base = process.env.GITHUB_BASE_REF @@ -112,20 +112,6 @@ for (const sourceFile of project.getSourceFiles()) { }); } } - - const propertyAccesses = sourceFile.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression); - for (const access of propertyAccesses) { - if (access.getName() !== 'then') continue; - const line = access.getStartLineNumber(); - if (addedLines.has(line)) { - violations.push({ - file: filePath, - line, - type: 'then-chain', - detail: 'use await instead of .then()', - }); - } - } } if (violations.length === 0) { @@ -137,6 +123,6 @@ console.error(`✗ Found ${violations.length} async/await violation(s) in the di for (const v of violations) { console.error(` ${v.file}:${v.line} [${v.type}] ${v.detail}`); } -console.error('\nNew code must use async/await instead of callbacks or .then() chains.'); +console.error('\nNew code must use async/await instead of callbacks.'); console.error('See the async/await migration guide in CONTRIBUTING.md for help.'); process.exit(1); diff --git a/.github/scripts/count-async-functions.mjs b/.github/scripts/count-async-functions.mjs index 300d4a70b9..a39b01c84e 100644 --- a/.github/scripts/count-async-functions.mjs +++ b/.github/scripts/count-async-functions.mjs @@ -74,7 +74,7 @@ console.log(''); console.log(`Migration: ${asyncFunctions}/${totalFunctions} functions (${migrationPercent}%)`); if (process.env.GITHUB_STEP_SUMMARY) { - const { writeFileSync, appendFileSync } = await import('node:fs'); + const { appendFileSync } = await import('node:fs'); appendFileSync(process.env.GITHUB_STEP_SUMMARY, [ '## Async/Await Migration Progress', '', diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index f246e939c0..4eb6947a70 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -80,6 +80,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + fetch-depth: 0 - uses: actions/setup-node@v4 with: node-version: '22' From c4d81e154720ecf31a1b92baa6dc54dd4f8fb52e Mon Sep 17 00:00:00 2001 From: DarkIsDude Date: Thu, 19 Mar 2026 14:23:20 +0100 Subject: [PATCH 4/8] =?UTF-8?q?=F0=9F=91=B7=20remove=20async/await=20rule?= =?UTF-8?q?=20in=20the=20CI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: CLDSRV-860 --- .github/workflows/tests.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 4eb6947a70..e1610d4175 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -97,8 +97,9 @@ jobs: cache: pip - name: Install python deps run: pip install flake8 - - name: Lint Javascript - run: yarn run --silent lint + - name: Lint Javascript (strict, excluding async migration rules) + run: | + yarn run --silent lint --max-warnings 0 --rule "promise/prefer-await-to-then: off" --rule "n/callback-return: off" - name: Lint Markdown run: yarn run --silent lint_md - name: Lint python From 8e5ad676ced74f061d239f0f32ad61dc652c92d8 Mon Sep 17 00:00:00 2001 From: DarkIsDude Date: Thu, 19 Mar 2026 16:35:17 +0100 Subject: [PATCH 5/8] =?UTF-8?q?=E2=9C=A8=20add=20count=20array=20source=20?= =?UTF-8?q?path=20from=20package.json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: CLDSRV-860 --- .github/scripts/count-async-functions.mjs | 22 ++++++++++++++-------- package.json | 8 ++++++++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/.github/scripts/count-async-functions.mjs b/.github/scripts/count-async-functions.mjs index a39b01c84e..bb44964477 100644 --- a/.github/scripts/count-async-functions.mjs +++ b/.github/scripts/count-async-functions.mjs @@ -4,8 +4,21 @@ * * Usage: node scripts/count-async-functions.mjs */ +import { readFileSync } from 'node:fs'; import { Project, SyntaxKind } from 'ts-morph'; +function getSourcePathsFromPackageJson() { + const packageJsonPath = new URL('../../package.json', import.meta.url); + const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')); + const paths = packageJson.countAsyncSourcePaths; + + if (Array.isArray(paths) && paths.length > 0 && paths.every(p => typeof p === 'string')) { + return paths; + } + + throw new Error('package.json must define a non-empty string array "countAsyncSourcePaths"'); +} + const project = new Project({ compilerOptions: { allowJs: true, @@ -14,14 +27,7 @@ const project = new Project({ skipAddingFilesFromTsConfig: true, }); -project.addSourceFilesAtPaths([ - 'lib/**/*.js', - 'index.js', - 'dataserver.js', - 'mdserver.js', - 'managementAgent.js', - 'bin/**/*.js' -]); +project.addSourceFilesAtPaths(getSourcePathsFromPackageJson()); let asyncFunctions = 0; let totalFunctions = 0; diff --git a/package.json b/package.json index 81ba9ae638..c9bfb3a7a7 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,14 @@ "jsonwebtoken": "^9.0.0", "nan": "v2.22.0" }, + "countAsyncSourcePaths": [ + "lib/**/*.js", + "index.js", + "dataserver.js", + "mdserver.js", + "managementAgent.js", + "bin/**/*.js" + ], "mocha": { "recursive": true, "timeout": 40000 From b15c6e87a662e09d1caec9b11a697906f8c03136 Mon Sep 17 00:00:00 2001 From: DarkIsDude Date: Thu, 19 Mar 2026 16:46:59 +0100 Subject: [PATCH 6/8] =?UTF-8?q?=E2=9C=85=20create=20test=20for=20new=20scr?= =?UTF-8?q?ipts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: CLDSRV-860 --- .../githubScripts/asyncMigrationScripts.js | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 tests/unit/githubScripts/asyncMigrationScripts.js diff --git a/tests/unit/githubScripts/asyncMigrationScripts.js b/tests/unit/githubScripts/asyncMigrationScripts.js new file mode 100644 index 0000000000..40ce384e13 --- /dev/null +++ b/tests/unit/githubScripts/asyncMigrationScripts.js @@ -0,0 +1,97 @@ +const assert = require('assert'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const { spawnSync } = require('child_process'); + +const repoRoot = path.resolve(__dirname, '../../..'); +const checkDiffScript = path.join(repoRoot, '.github/scripts/check-diff-async.mjs'); +const countAsyncScript = path.join(repoRoot, '.github/scripts/count-async-functions.mjs'); + +function run(command, args, cwd, extraEnv = {}) { + const result = spawnSync(command, args, { + cwd, + env: { ...process.env, ...extraEnv }, + encoding: 'utf8', + }); + assert.strictEqual(result.status, 0, `command failed: ${command} ${args.join(' ')}\n${result.stderr}`); + return result; +} + +function initTempRepo() { + const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'cloudserver-script-tests-')); + run('git', ['init'], dir); + run('git', ['config', 'user.name', 'Copilot Test'], dir); + run('git', ['config', 'user.email', 'copilot-test@example.com'], dir); + + fs.writeFileSync(path.join(dir, 'README.md'), 'test\n'); + run('git', ['add', 'README.md'], dir); + run('git', ['commit', '-m', 'init'], dir); + return dir; +} + +function runNodeScript(scriptPath, cwd, extraEnv = {}) { + return spawnSync(process.execPath, [scriptPath], { + cwd, + env: { ...process.env, ...extraEnv }, + encoding: 'utf8', + }); +} + +describe('CI async migration scripts', () => { + const tempDirs = []; + + after(() => { + tempDirs.forEach(dir => fs.rmSync(dir, { recursive: true, force: true })); + }); + + it('check-diff-async exits successfully when no JS files changed', () => { + const dir = initTempRepo(); + tempDirs.push(dir); + + const result = runNodeScript(checkDiffScript, dir); + assert.strictEqual(result.status, 0, result.stderr); + assert(result.stdout.includes('No changed JS files to check.')); + }); + + it('check-diff-async ignores non-source JS changes', () => { + const dir = initTempRepo(); + tempDirs.push(dir); + + fs.mkdirSync(path.join(dir, 'tests/unit'), { recursive: true }); + fs.writeFileSync(path.join(dir, 'tests/unit/newTest.js'), 'module.exports = () => {};\n'); + run('git', ['add', 'tests/unit/newTest.js'], dir); + + const result = runNodeScript(checkDiffScript, dir); + assert.strictEqual(result.status, 0, result.stderr); + assert(result.stdout.includes('No source JS files in diff')); + }); + + it('check-diff-async fails on newly added callback-style function', () => { + const dir = initTempRepo(); + tempDirs.push(dir); + + fs.mkdirSync(path.join(dir, 'lib'), { recursive: true }); + fs.writeFileSync(path.join(dir, 'lib/newFile.js'), [ + 'function badStyle(param, cb) {', + ' return cb(null, param);', + '}', + '', + ].join('\n')); + run('git', ['add', 'lib/newFile.js'], dir); + + const result = runNodeScript(checkDiffScript, dir); + assert.strictEqual(result.status, 1); + assert(result.stderr.includes('function has callback parameter')); + assert(result.stderr.includes('lib/newFile.js')); + }); + + it('count-async-functions runs and prints summary', function countAsyncTest() { + this.timeout(120000); + const result = runNodeScript(countAsyncScript, repoRoot); + + assert.strictEqual(result.status, 0, result.stderr); + assert(result.stdout.includes('=== Async/Await Migration Progress ===')); + assert(result.stdout.includes('Total functions:')); + }); +}); From 68ce4e6381fae816fb0436c1a8f77c1a5163c735 Mon Sep 17 00:00:00 2001 From: DarkIsDude Date: Thu, 19 Mar 2026 17:06:29 +0100 Subject: [PATCH 7/8] =?UTF-8?q?=F0=9F=94=92=EF=B8=8F=20use=20array=20argum?= =?UTF-8?q?ent=20to=20avoid=20injection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: CLDSRV-860 --- .github/scripts/check-diff-async.mjs | 15 ++++++++++----- .github/scripts/count-async-functions.mjs | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/scripts/check-diff-async.mjs b/.github/scripts/check-diff-async.mjs index 9995374968..18db4d614a 100644 --- a/.github/scripts/check-diff-async.mjs +++ b/.github/scripts/check-diff-async.mjs @@ -5,7 +5,7 @@ * Usage: node scripts/check-diff-async.mjs * In CI: runs against the current PR diff (files changed vs base branch) */ -import { execSync } from 'node:child_process'; +import { execFileSync } from 'node:child_process'; import { Project, SyntaxKind } from 'ts-morph'; const CALLBACK_PARAM_PATTERN = /^(cb|callback|next|done)$/i; @@ -14,9 +14,14 @@ function getChangedJsFiles() { const base = process.env.GITHUB_BASE_REF ? `origin/${process.env.GITHUB_BASE_REF}` : 'HEAD'; - const output = execSync(`git diff --name-only --diff-filter=ACMR ${base} -- '*.js'`, { - encoding: 'utf8', - }).trim(); + const output = execFileSync('git', [ + 'diff', + '--name-only', + '--diff-filter=ACMR', + base, + '--', + '*.js', + ], { encoding: 'utf8' }).trim(); return output ? output.split('\n').filter(f => f.endsWith('.js')) : []; } @@ -28,7 +33,7 @@ function getAddedLineNumbers(filePath) { const base = process.env.GITHUB_BASE_REF ? `origin/${process.env.GITHUB_BASE_REF}` : 'HEAD'; - const diff = execSync(`git diff ${base} -- ${filePath}`, { encoding: 'utf8' }); + const diff = execFileSync('git', ['diff', base, '--', filePath], { encoding: 'utf8' }); const addedLines = new Set(); let currentLine = 0; diff --git a/.github/scripts/count-async-functions.mjs b/.github/scripts/count-async-functions.mjs index bb44964477..46d1a50334 100644 --- a/.github/scripts/count-async-functions.mjs +++ b/.github/scripts/count-async-functions.mjs @@ -34,7 +34,7 @@ let totalFunctions = 0; let callbackFunctions = 0; let thenChains = 0; -const CALLBACK_PARAM_PATTERN = /^(cb|callback|next|done|err)$/i; +const CALLBACK_PARAM_PATTERN = /^(cb|callback|next|done)$/i; for (const sourceFile of project.getSourceFiles()) { const functions = [ From 8612e120301baa163e83541e20dc06966834198f Mon Sep 17 00:00:00 2001 From: DarkIsDude Date: Thu, 19 Mar 2026 17:29:47 +0100 Subject: [PATCH 8/8] =?UTF-8?q?=F0=9F=91=B7=20change=20how=20we=20compute?= =?UTF-8?q?=20the=20migration=20progress?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: CLDSRV-860 --- .github/scripts/count-async-functions.mjs | 13 +++++++++---- .github/workflows/tests.yaml | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/scripts/count-async-functions.mjs b/.github/scripts/count-async-functions.mjs index 46d1a50334..2b4d4c008c 100644 --- a/.github/scripts/count-async-functions.mjs +++ b/.github/scripts/count-async-functions.mjs @@ -67,17 +67,21 @@ for (const sourceFile of project.getSourceFiles()) { } } -const migrationPercent = totalFunctions > 0 +const asyncFunctionPercent = totalFunctions > 0 ? ((asyncFunctions / totalFunctions) * 100).toFixed(1) : '0.0'; +const migrationPercent = (asyncFunctions + callbackFunctions) > 0 + ? ((asyncFunctions / (asyncFunctions + callbackFunctions)) * 100).toFixed(1) + : '0.0'; + console.log('=== Async/Await Migration Progress ==='); console.log(`Total functions: ${totalFunctions}`); -console.log(`Async functions: ${asyncFunctions} (${migrationPercent}%)`); +console.log(`Async functions: ${asyncFunctions} (${asyncFunctionPercent}%)`); console.log(`Callback functions: ${callbackFunctions}`); console.log(`Remaining .then(): ${thenChains}`); console.log(''); -console.log(`Migration: ${asyncFunctions}/${totalFunctions} functions (${migrationPercent}%)`); +console.log(`Migration (trend): ${asyncFunctions}/${asyncFunctions + callbackFunctions} (${migrationPercent}%)`); if (process.env.GITHUB_STEP_SUMMARY) { const { appendFileSync } = await import('node:fs'); @@ -87,9 +91,10 @@ if (process.env.GITHUB_STEP_SUMMARY) { `| Metric | Count |`, `|--------|-------|`, `| Total functions | ${totalFunctions} |`, - `| Async functions | ${asyncFunctions} (${migrationPercent}%) |`, + `| Async functions | ${asyncFunctions} (${asyncFunctionPercent}%) |`, `| Callback-style functions | ${callbackFunctions} |`, `| Remaining \`.then()\` chains | ${thenChains} |`, + `| Migration trend (async / (async + callback)) | ${asyncFunctions}/${asyncFunctions + callbackFunctions} (${migrationPercent}%) |`, '', ].join('\n')); } diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index e1610d4175..e16dbb22c2 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -99,7 +99,7 @@ jobs: run: pip install flake8 - name: Lint Javascript (strict, excluding async migration rules) run: | - yarn run --silent lint --max-warnings 0 --rule "promise/prefer-await-to-then: off" --rule "n/callback-return: off" + yarn run --silent lint -- --max-warnings 0 --rule "promise/prefer-await-to-then: off" --rule "n/callback-return: off" - name: Lint Markdown run: yarn run --silent lint_md - name: Lint python