From 19b0e9489d40b20db46d3eeef1ec47d0abba700a Mon Sep 17 00:00:00 2001 From: Thodoris Greasidis Date: Tue, 19 Dec 2023 01:16:27 +0200 Subject: [PATCH 1/3] Deduplicate node modules --- npm-shrinkwrap.json | 208 ++++++++++++++++++++++---------------------- 1 file changed, 104 insertions(+), 104 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 6a17996d..79eda43c 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1112,12 +1112,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz", - "integrity": "sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dev": true, "dependencies": { - "@babel/types": "^7.23.5", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -1127,9 +1127,9 @@ } }, "node_modules/@babel/generator/node_modules/@babel/types": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", - "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.23.4", @@ -1163,9 +1163,9 @@ } }, "node_modules/@babel/helper-function-name/node_modules/@babel/types": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", - "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.23.4", @@ -1311,9 +1311,9 @@ } }, "node_modules/@babel/template/node_modules/@babel/parser": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", - "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -1323,9 +1323,9 @@ } }, "node_modules/@babel/template/node_modules/@babel/types": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", - "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.23.4", @@ -1358,9 +1358,9 @@ } }, "node_modules/@babel/traverse/node_modules/@babel/parser": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", - "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -1370,9 +1370,9 @@ } }, "node_modules/@babel/traverse/node_modules/@babel/types": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", - "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.23.4", @@ -1781,9 +1781,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -1832,9 +1832,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", - "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2296,9 +2296,9 @@ } }, "node_modules/@oclif/core": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/@oclif/core/-/core-3.14.1.tgz", - "integrity": "sha512-HLFL2s45DFdqYI2CFjVS/CIQ4cQ4yZqH0XqO9nnwcRWYboz2rEW/vLmidjIYGDjh6xA/k5psiAL3O1KEjqSHuQ==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-3.15.0.tgz", + "integrity": "sha512-A1EVh4gv7mqAJ9OGVxLugaLcHvQnGwvOnPToP8OT9AldJ0LwVExOwhhlnOstYca33MIpGH00twYxWMS5nxMuDQ==", "dependencies": { "ansi-escapes": "^4.3.2", "ansi-styles": "^4.3.0", @@ -7091,9 +7091,9 @@ } }, "node_modules/cpu-features": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.8.tgz", - "integrity": "sha512-BbHBvtYhUhksqTjr6bhNOjGgMnhwhGTQmOoZGD+K7BCaQDCuZl/Ve1ZxUSMRwVC4D/rkCPQ2MAIeYzrWyK7eEg==", + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.9.tgz", + "integrity": "sha512-AKjgn2rP2yJyfbepsmLfiYcmtNn/2eUvocUyM/09yB0YDiz39HteK/5/T4Onf0pmdYDMgkBoGvRLvEguzyL7wQ==", "hasInstallScript": true, "optional": true, "dependencies": { @@ -8382,15 +8382,15 @@ } }, "node_modules/eslint": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", - "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.55.0", + "@eslint/js": "8.56.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -8704,9 +8704,9 @@ } }, "node_modules/eslint/node_modules/globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -14764,9 +14764,9 @@ } }, "node_modules/nan": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", - "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==" + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", + "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==" }, "node_modules/nanoid": { "version": "3.1.20", @@ -20324,9 +20324,9 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "node_modules/ssh2": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.14.0.tgz", - "integrity": "sha512-AqzD1UCqit8tbOKoj6ztDDi1ffJZ2rV2SwlgrVVrHPkV5vWqGJOVp5pmtj18PunkPJAuKQsnInyKV+/Nb2bUnA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.15.0.tgz", + "integrity": "sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==", "hasInstallScript": true, "dependencies": { "asn1": "^0.2.6", @@ -20336,8 +20336,8 @@ "node": ">=10.16.0" }, "optionalDependencies": { - "cpu-features": "~0.0.8", - "nan": "^2.17.0" + "cpu-features": "~0.0.9", + "nan": "^2.18.0" } }, "node_modules/ssh2-streams": { @@ -23415,9 +23415,9 @@ } }, "node_modules/yeoman-environment/node_modules/readable-stream": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.2.tgz", - "integrity": "sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.1.tgz", + "integrity": "sha512-uQjbf34vmf/asGnOHQEw07Q4llgMACQZTWWa4MmICS0IKJoHbLwKCy71H3eR99Dw5iYejc6W+pqZZEeqRtUFAw==", "dev": true, "dependencies": { "abort-controller": "^3.0.0", @@ -25521,21 +25521,21 @@ } }, "@babel/generator": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz", - "integrity": "sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dev": true, "requires": { - "@babel/types": "^7.23.5", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "dependencies": { "@babel/types": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", - "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", "dev": true, "requires": { "@babel/helper-string-parser": "^7.23.4", @@ -25562,9 +25562,9 @@ }, "dependencies": { "@babel/types": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", - "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", "dev": true, "requires": { "@babel/helper-string-parser": "^7.23.4", @@ -25678,15 +25678,15 @@ }, "dependencies": { "@babel/parser": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", - "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", "dev": true }, "@babel/types": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", - "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", "dev": true, "requires": { "@babel/helper-string-parser": "^7.23.4", @@ -25715,15 +25715,15 @@ }, "dependencies": { "@babel/parser": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", - "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", "dev": true }, "@babel/types": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", - "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", "dev": true, "requires": { "@babel/helper-string-parser": "^7.23.4", @@ -26054,9 +26054,9 @@ }, "dependencies": { "globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -26086,9 +26086,9 @@ } }, "@eslint/js": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", - "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", "dev": true }, "@gar/promisify": { @@ -26458,9 +26458,9 @@ } }, "@oclif/core": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/@oclif/core/-/core-3.14.1.tgz", - "integrity": "sha512-HLFL2s45DFdqYI2CFjVS/CIQ4cQ4yZqH0XqO9nnwcRWYboz2rEW/vLmidjIYGDjh6xA/k5psiAL3O1KEjqSHuQ==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-3.15.0.tgz", + "integrity": "sha512-A1EVh4gv7mqAJ9OGVxLugaLcHvQnGwvOnPToP8OT9AldJ0LwVExOwhhlnOstYca33MIpGH00twYxWMS5nxMuDQ==", "requires": { "ansi-escapes": "^4.3.2", "ansi-styles": "^4.3.0", @@ -30346,9 +30346,9 @@ } }, "cpu-features": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.8.tgz", - "integrity": "sha512-BbHBvtYhUhksqTjr6bhNOjGgMnhwhGTQmOoZGD+K7BCaQDCuZl/Ve1ZxUSMRwVC4D/rkCPQ2MAIeYzrWyK7eEg==", + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.9.tgz", + "integrity": "sha512-AKjgn2rP2yJyfbepsmLfiYcmtNn/2eUvocUyM/09yB0YDiz39HteK/5/T4Onf0pmdYDMgkBoGvRLvEguzyL7wQ==", "optional": true, "requires": { "buildcheck": "~0.0.6", @@ -31390,15 +31390,15 @@ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" }, "eslint": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", - "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.55.0", + "@eslint/js": "8.56.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -31482,9 +31482,9 @@ } }, "globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -36282,9 +36282,9 @@ } }, "nan": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", - "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==" + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", + "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==" }, "nanoid": { "version": "3.1.20", @@ -40614,14 +40614,14 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "ssh2": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.14.0.tgz", - "integrity": "sha512-AqzD1UCqit8tbOKoj6ztDDi1ffJZ2rV2SwlgrVVrHPkV5vWqGJOVp5pmtj18PunkPJAuKQsnInyKV+/Nb2bUnA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.15.0.tgz", + "integrity": "sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==", "requires": { "asn1": "^0.2.6", "bcrypt-pbkdf": "^1.0.2", - "cpu-features": "~0.0.8", - "nan": "^2.17.0" + "cpu-features": "~0.0.9", + "nan": "^2.18.0" } }, "ssh2-streams": { @@ -42991,9 +42991,9 @@ "dev": true }, "readable-stream": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.2.tgz", - "integrity": "sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.1.tgz", + "integrity": "sha512-uQjbf34vmf/asGnOHQEw07Q4llgMACQZTWWa4MmICS0IKJoHbLwKCy71H3eR99Dw5iYejc6W+pqZZEeqRtUFAw==", "dev": true, "requires": { "abort-controller": "^3.0.0", From 0ba352258482048bbdb840be7ee9958b491f9b6c Mon Sep 17 00:00:00 2001 From: Thodoris Greasidis Date: Mon, 11 Dec 2023 11:40:10 +0200 Subject: [PATCH 2/3] Update dependencies Update @balena/compose from 3.0.5 to 3.2.0 Also updates pinejs-client-request to support using the Retry-After header and dockerode to 3.3.5 to be aligned with @balena/compose. Change-type: patch --- npm-shrinkwrap.json | 144 +++++++++++--------------------------------- package.json | 4 +- 2 files changed, 38 insertions(+), 110 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 79eda43c..8f14e3ce 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -10,7 +10,7 @@ "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@balena/compose": "^3.0.5", + "@balena/compose": "^3.2.0", "@balena/dockerignore": "^1.0.2", "@balena/es-version": "^1.0.1", "@oclif/core": "^3.14.1", @@ -39,7 +39,7 @@ "denymount": "^2.3.0", "docker-modem": "3.0.0", "docker-progress": "^5.1.3", - "dockerode": "3.3.3", + "dockerode": "3.3.5", "ejs": "^3.1.6", "etcher-sdk": "^8.7.0", "event-stream": "3.3.4", @@ -1409,18 +1409,16 @@ } }, "node_modules/@balena/compose": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@balena/compose/-/compose-3.0.5.tgz", - "integrity": "sha512-5w7aCvqKOTTwuosl39Y5+7lpjjr7k4Kx6dao01T0MAomFMlXsBNSzQjw50Xv5khkW7/DaKo0zMJnLsOq3YRDcw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@balena/compose/-/compose-3.2.0.tgz", + "integrity": "sha512-euzygRQadCEzd2j8xMPAMCcPogBAbF2o+f7vcNRXAdCP3q3u/bdBk7yXgWSaLmNilGqdsgeGS8WM0RxmXS9JYA==", "dependencies": { "ajv": "^6.12.3", - "bluebird": "^3.7.2", - "bluebird-lru-cache": "^1.0.1", "docker-file-parser": "^1.0.7", "docker-modem": "^3.0.3", "docker-progress": "^5.1.0", "dockerfile-ast": "^0.2.1", - "dockerode": "3.3.3", + "dockerode": "^3.3.5", "duplexify": "^4.1.2", "event-stream": "^4.0.1", "fp-ts": "^2.8.1", @@ -1431,8 +1429,10 @@ "JSONStream": "^1.3.5", "klaw": "^4.0.1", "lodash": "^4.17.19", + "memoizee": "^0.4.15", "mz": "^2.7.0", "p-map": "^4.0.0", + "pinejs-client-core": "^6.13.0", "pinejs-client-request": "^7.3.5", "request": "^2.88.2", "semver": "^7.3.5", @@ -5773,37 +5773,6 @@ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, - "node_modules/bluebird-lru-cache": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bluebird-lru-cache/-/bluebird-lru-cache-1.0.1.tgz", - "integrity": "sha512-HdQ6UO5vSXG5qK/pE9srZTHCsvRzmzrn7g31XTcKq9JWMK0PG+LJ0mmR6LIYuXO5DeiR+DnLWMnMsYcd0WD2Bg==", - "dependencies": { - "bluebird": "^3.0.6", - "lru-cache": "^3.2.0", - "typed-error": "^2.0.0" - } - }, - "node_modules/bluebird-lru-cache/node_modules/lru-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-3.2.0.tgz", - "integrity": "sha512-91gyOKTc2k66UG6kHiH4h3S2eltcPwE1STVfMYC/NG+nZwf8IIuiamfmpGZjpbbxzSyEJaLC0tNSmhjlQUTJow==", - "dependencies": { - "pseudomap": "^1.0.1" - } - }, - "node_modules/bluebird-lru-cache/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/bluebird-lru-cache/node_modules/typed-error": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/typed-error/-/typed-error-2.0.0.tgz", - "integrity": "sha512-uRrCO6P1KGYMZPykrmBaTnwuxEERymyDkO/WLfO8VcYt4MXw6RrcUoA/yYOt8T2RPZWULDtEKibR/Hlq8Hd4rA==", - "dependencies": { - "tslib": "^1.7.1" - } - }, "node_modules/bluebird-retry": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/bluebird-retry/-/bluebird-retry-0.10.1.tgz", @@ -7849,10 +7818,11 @@ } }, "node_modules/dockerode": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.3.3.tgz", - "integrity": "sha512-lvKV6/NGf2/CYLt5V4c0fd6Fl9XZSCo1Z2HBT9ioKrKLMB2o+gA62Uza8RROpzGvYv57KJx2dKu+ZwSpB//OIA==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.3.5.tgz", + "integrity": "sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA==", "dependencies": { + "@balena/dockerignore": "^1.0.2", "docker-modem": "^3.0.0", "tar-fs": "~2.0.1" }, @@ -16923,9 +16893,9 @@ } }, "node_modules/pinejs-client-core": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/pinejs-client-core/-/pinejs-client-core-6.12.3.tgz", - "integrity": "sha512-Qg9TbiaUlKDqcWCE6ARFFzOvNnJnBmovPifeyhV7ybYVDDpd8WAGrItQSJkOjuk4ZSwcaYMcHJ+FNatBUAnGWw==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/pinejs-client-core/-/pinejs-client-core-6.14.0.tgz", + "integrity": "sha512-28k0KYr6EItvOnVQ0cam24/D2Ky8EDFZMlyyvPMRX2KRzXIU9+FB7s6/BvSwx2+aYwxgMHCqTnBUBUDP3NZ/Hw==", "dependencies": { "@balena/es-version": "^1.0.1" }, @@ -16935,16 +16905,16 @@ } }, "node_modules/pinejs-client-request": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/pinejs-client-request/-/pinejs-client-request-7.3.6.tgz", - "integrity": "sha512-6Pr9rKXh2DcZcd/sCc7r8nLq6upL8SLWu7/VURAQ57U43bVWs/Urbib4jdyJ9CjRVvhGWm+7b52Y/Y0Q29WhqA==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/pinejs-client-request/-/pinejs-client-request-7.4.0.tgz", + "integrity": "sha512-jJupfCufljGu+ALOVqR5q52yORI1UV0/nFJyGlrXedUa13fchwzgTNY7rFvTJKDZX59L5XAuc431/hSzBmZ5TA==", "dependencies": { "@types/lodash": "^4.14.181", "@types/lru-cache": "^5.1.1", "@types/request": "^2.48.8", "lodash": "^4.17.21", "lru-cache": "^6.0.0", - "pinejs-client-core": "^6.10.2", + "pinejs-client-core": "^6.14.0", "request": "^2.88.2", "typed-error": "^3.2.1" }, @@ -17648,11 +17618,6 @@ "node": ">= 0.10" } }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" - }, "node_modules/psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -25753,18 +25718,16 @@ } }, "@balena/compose": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@balena/compose/-/compose-3.0.5.tgz", - "integrity": "sha512-5w7aCvqKOTTwuosl39Y5+7lpjjr7k4Kx6dao01T0MAomFMlXsBNSzQjw50Xv5khkW7/DaKo0zMJnLsOq3YRDcw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@balena/compose/-/compose-3.2.0.tgz", + "integrity": "sha512-euzygRQadCEzd2j8xMPAMCcPogBAbF2o+f7vcNRXAdCP3q3u/bdBk7yXgWSaLmNilGqdsgeGS8WM0RxmXS9JYA==", "requires": { "ajv": "^6.12.3", - "bluebird": "^3.7.2", - "bluebird-lru-cache": "^1.0.1", "docker-file-parser": "^1.0.7", "docker-modem": "^3.0.3", "docker-progress": "^5.1.0", "dockerfile-ast": "^0.2.1", - "dockerode": "3.3.3", + "dockerode": "^3.3.5", "duplexify": "^4.1.2", "event-stream": "^4.0.1", "fp-ts": "^2.8.1", @@ -25775,8 +25738,10 @@ "JSONStream": "^1.3.5", "klaw": "^4.0.1", "lodash": "^4.17.19", + "memoizee": "^0.4.15", "mz": "^2.7.0", "p-map": "^4.0.0", + "pinejs-client-core": "^6.13.0", "pinejs-client-request": "^7.3.5", "request": "^2.88.2", "semver": "^7.3.5", @@ -29311,39 +29276,6 @@ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, - "bluebird-lru-cache": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bluebird-lru-cache/-/bluebird-lru-cache-1.0.1.tgz", - "integrity": "sha512-HdQ6UO5vSXG5qK/pE9srZTHCsvRzmzrn7g31XTcKq9JWMK0PG+LJ0mmR6LIYuXO5DeiR+DnLWMnMsYcd0WD2Bg==", - "requires": { - "bluebird": "^3.0.6", - "lru-cache": "^3.2.0", - "typed-error": "^2.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-3.2.0.tgz", - "integrity": "sha512-91gyOKTc2k66UG6kHiH4h3S2eltcPwE1STVfMYC/NG+nZwf8IIuiamfmpGZjpbbxzSyEJaLC0tNSmhjlQUTJow==", - "requires": { - "pseudomap": "^1.0.1" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "typed-error": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/typed-error/-/typed-error-2.0.0.tgz", - "integrity": "sha512-uRrCO6P1KGYMZPykrmBaTnwuxEERymyDkO/WLfO8VcYt4MXw6RrcUoA/yYOt8T2RPZWULDtEKibR/Hlq8Hd4rA==", - "requires": { - "tslib": "^1.7.1" - } - } - } - }, "bluebird-retry": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/bluebird-retry/-/bluebird-retry-0.10.1.tgz", @@ -30937,10 +30869,11 @@ } }, "dockerode": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.3.3.tgz", - "integrity": "sha512-lvKV6/NGf2/CYLt5V4c0fd6Fl9XZSCo1Z2HBT9ioKrKLMB2o+gA62Uza8RROpzGvYv57KJx2dKu+ZwSpB//OIA==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.3.5.tgz", + "integrity": "sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA==", "requires": { + "@balena/dockerignore": "^1.0.2", "docker-modem": "^3.0.0", "tar-fs": "~2.0.1" }, @@ -37957,24 +37890,24 @@ "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==" }, "pinejs-client-core": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/pinejs-client-core/-/pinejs-client-core-6.12.3.tgz", - "integrity": "sha512-Qg9TbiaUlKDqcWCE6ARFFzOvNnJnBmovPifeyhV7ybYVDDpd8WAGrItQSJkOjuk4ZSwcaYMcHJ+FNatBUAnGWw==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/pinejs-client-core/-/pinejs-client-core-6.14.0.tgz", + "integrity": "sha512-28k0KYr6EItvOnVQ0cam24/D2Ky8EDFZMlyyvPMRX2KRzXIU9+FB7s6/BvSwx2+aYwxgMHCqTnBUBUDP3NZ/Hw==", "requires": { "@balena/es-version": "^1.0.1" } }, "pinejs-client-request": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/pinejs-client-request/-/pinejs-client-request-7.3.6.tgz", - "integrity": "sha512-6Pr9rKXh2DcZcd/sCc7r8nLq6upL8SLWu7/VURAQ57U43bVWs/Urbib4jdyJ9CjRVvhGWm+7b52Y/Y0Q29WhqA==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/pinejs-client-request/-/pinejs-client-request-7.4.0.tgz", + "integrity": "sha512-jJupfCufljGu+ALOVqR5q52yORI1UV0/nFJyGlrXedUa13fchwzgTNY7rFvTJKDZX59L5XAuc431/hSzBmZ5TA==", "requires": { "@types/lodash": "^4.14.181", "@types/lru-cache": "^5.1.1", "@types/request": "^2.48.8", "lodash": "^4.17.21", "lru-cache": "^6.0.0", - "pinejs-client-core": "^6.10.2", + "pinejs-client-core": "^6.14.0", "request": "^2.88.2", "typed-error": "^3.2.1" } @@ -38465,11 +38398,6 @@ "ipaddr.js": "1.9.1" } }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" - }, "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", diff --git a/package.json b/package.json index e456ee3c..87de9abe 100644 --- a/package.json +++ b/package.json @@ -194,7 +194,7 @@ "typescript": "^5.3.2" }, "dependencies": { - "@balena/compose": "^3.0.5", + "@balena/compose": "^3.2.0", "@balena/dockerignore": "^1.0.2", "@balena/es-version": "^1.0.1", "@oclif/core": "^3.14.1", @@ -223,7 +223,7 @@ "denymount": "^2.3.0", "docker-modem": "3.0.0", "docker-progress": "^5.1.3", - "dockerode": "3.3.3", + "dockerode": "3.3.5", "ejs": "^3.1.6", "etcher-sdk": "^8.7.0", "event-stream": "3.3.4", From 4266dc69514c2177399fc605985196a436d75740 Mon Sep 17 00:00:00 2001 From: Thodoris Greasidis Date: Mon, 11 Dec 2023 18:55:49 +0200 Subject: [PATCH 3/3] deploy: Add rate-limiting aware retries for failed requests Change-type: patch --- lib/utils/compose.ts | 47 +++++++++++++++++-- lib/utils/compose_ts.ts | 1 + npm-shrinkwrap.json | 11 +++++ package.json | 1 + tests/commands/deploy.spec.ts | 87 +++++++++++++++++++++++++++++++++++ tests/config-tests.ts | 5 ++ tests/nock/balena-api-mock.ts | 13 ++++-- tests/nock/nock-mock.ts | 39 +++++++++++----- 8 files changed, 186 insertions(+), 18 deletions(-) diff --git a/lib/utils/compose.ts b/lib/utils/compose.ts index c9c39496..4ddb992d 100644 --- a/lib/utils/compose.ts +++ b/lib/utils/compose.ts @@ -20,6 +20,7 @@ import type * as SDK from 'balena-sdk'; import type Dockerode = require('dockerode'); import * as path from 'path'; import type { Composition, ImageDescriptor } from '@balena/compose/dist/parse'; +import type { RetryParametersObj } from 'pinejs-client-core'; import type { BuiltImage, ComposeOpts, @@ -94,22 +95,62 @@ export function createProject( }; } +const getRequestRetryParameters = (): RetryParametersObj => { + if ( + process.env.BALENA_CLI_TEST_TYPE != null && + process.env.BALENA_CLI_TEST_TYPE !== '' + ) { + // We only read the test env vars when in test mode. + const { intVar } = + require('@balena/env-parsing') as typeof import('@balena/env-parsing'); + // We use the BALENARCTEST namespace and only parse the env vars while in test mode + // since we plan to switch all pinejs clients with the one of the SDK and might not + // want to have to support these env vars. + return { + minDelayMs: intVar('BALENARCTEST_API_RETRY_MIN_DELAY_MS'), + maxDelayMs: intVar('BALENARCTEST_API_RETRY_MAX_DELAY_MS'), + maxAttempts: intVar('BALENARCTEST_API_RETRY_MAX_ATTEMPTS'), + }; + } + + return { + minDelayMs: 1000, + maxDelayMs: 60000, + maxAttempts: 7, + }; +}; + export const createRelease = async function ( + logger: Logger, apiEndpoint: string, auth: string, userId: number, appId: number, composition: Composition, draft: boolean, - semver?: string, - contract?: string, + semver: string | undefined, + contract: string | undefined, ): Promise { const _ = require('lodash') as typeof import('lodash'); const crypto = require('crypto') as typeof import('crypto'); const releaseMod = require('@balena/compose/dist/release') as typeof import('@balena/compose/dist/release'); - const client = releaseMod.createClient({ apiEndpoint, auth }); + const client = releaseMod.createClient({ + apiEndpoint, + auth, + retry: { + ...getRequestRetryParameters(), + onRetry: (err, delayMs, attempt, maxAttempts) => { + const code = err?.statusCode ?? 0; + logger.logDebug( + `API call failed with code ${code}. Attempting retry ${attempt} of ${maxAttempts} in ${ + delayMs / 1000 + } seconds`, + ); + }, + }, + }); const { release, serviceImages } = await releaseMod.create({ client, diff --git a/lib/utils/compose_ts.ts b/lib/utils/compose_ts.ts index 202d85f1..7a974cff 100644 --- a/lib/utils/compose_ts.ts +++ b/lib/utils/compose_ts.ts @@ -1385,6 +1385,7 @@ export async function deployProject( `${prefix}Creating release...`, () => createRelease( + logger, apiEndpoint, auth, userId, diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 8f14e3ce..9d505bbe 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -12,6 +12,7 @@ "dependencies": { "@balena/compose": "^3.2.0", "@balena/dockerignore": "^1.0.2", + "@balena/env-parsing": "^1.1.8", "@balena/es-version": "^1.0.1", "@oclif/core": "^3.14.1", "@resin.io/valid-email": "^0.1.0", @@ -1544,6 +1545,11 @@ "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz", "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==" }, + "node_modules/@balena/env-parsing": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@balena/env-parsing/-/env-parsing-1.1.8.tgz", + "integrity": "sha512-6L9U2LJ5Akov92962+NjjvrfZ1VPVJGZwjb8DIurRXxFIWldA+D0EOgvvmmZtgiRsG3OfZnRK9oBBYVC/bDFxA==" + }, "node_modules/@balena/es-version": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@balena/es-version/-/es-version-1.0.1.tgz", @@ -25831,6 +25837,11 @@ "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz", "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==" }, + "@balena/env-parsing": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@balena/env-parsing/-/env-parsing-1.1.8.tgz", + "integrity": "sha512-6L9U2LJ5Akov92962+NjjvrfZ1VPVJGZwjb8DIurRXxFIWldA+D0EOgvvmmZtgiRsG3OfZnRK9oBBYVC/bDFxA==" + }, "@balena/es-version": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@balena/es-version/-/es-version-1.0.1.tgz", diff --git a/package.json b/package.json index 87de9abe..45f601cc 100644 --- a/package.json +++ b/package.json @@ -196,6 +196,7 @@ "dependencies": { "@balena/compose": "^3.2.0", "@balena/dockerignore": "^1.0.2", + "@balena/env-parsing": "^1.1.8", "@balena/es-version": "^1.0.1", "@oclif/core": "^3.14.1", "@resin.io/valid-email": "^0.1.0", diff --git a/tests/commands/deploy.spec.ts b/tests/commands/deploy.spec.ts index 9b03ce08..7842c9ea 100644 --- a/tests/commands/deploy.spec.ts +++ b/tests/commands/deploy.spec.ts @@ -15,6 +15,7 @@ * limitations under the License. */ +import { intVar } from '@balena/env-parsing'; import type { Request as ReleaseRequest } from '@balena/compose/dist/release'; import { expect } from 'chai'; import { promises as fs } from 'fs'; @@ -284,16 +285,25 @@ describe('balena deploy', function () { api.expectPostRelease({}); docker.expectGetManifestBusybox(); + let failedImagePatchRequests = 0; // Mock this patch HTTP request to return status code 500, in which case // the release status should be saved as "failed" rather than "success" + const maxRequestRetries = intVar('BALENARCTEST_API_RETRY_MAX_ATTEMPTS'); + expect( + maxRequestRetries, + 'BALENARCTEST_API_RETRY_MAX_ATTEMPTS must be >= 2 for this test', + ).to.be.greaterThanOrEqual(2); api.expectPatchImage({ replyBody: errMsg, statusCode: 500, + // b/c failed requests are retried + times: maxRequestRetries, inspectRequest: (_uri, requestBody) => { const imageBody = requestBody as Partial< import('@balena/compose/dist/release/models').ImageModel >; expect(imageBody.status).to.equal('success'); + failedImagePatchRequests++; }, }); // Check that the CLI patches the release with status="failed" @@ -324,6 +334,7 @@ describe('balena deploy', function () { responseCode: 200, services: ['main'], }); + expect(failedImagePatchRequests).to.equal(maxRequestRetries); } finally { await switchSentry(sentryStatus); // @ts-expect-error claims restore does not exist @@ -331,6 +342,82 @@ describe('balena deploy', function () { } }); + it('should create the expected --build tar stream after retrying failing OData requests (single container)', async () => { + const projectPath = path.join(projectsPath, 'no-docker-compose', 'basic'); + const expectedFiles: ExpectedTarStreamFiles = { + 'src/.dockerignore': { fileSize: 16, type: 'file' }, + 'src/start.sh': { fileSize: 89, type: 'file' }, + 'src/windows-crlf.sh': { + fileSize: isWindows ? 68 : 70, + testStream: isWindows ? expectStreamNoCRLF : undefined, + type: 'file', + }, + Dockerfile: { fileSize: 88, type: 'file' }, + 'Dockerfile-alt': { fileSize: 30, type: 'file' }, + }; + const responseFilename = 'build-POST.json'; + const responseBody = await fs.readFile( + path.join(dockerResponsePath, responseFilename), + 'utf8', + ); + const expectedResponseLines = [ + ...commonResponseLines[responseFilename], + `[Info] No "docker-compose.yml" file found at "${projectPath}"`, + `[Info] Creating default composition with source: "${projectPath}"`, + ...getDockerignoreWarn1( + [path.join(projectPath, 'src', '.dockerignore')], + 'deploy', + ), + ]; + if (isWindows) { + const fname = path.join(projectPath, 'src', 'windows-crlf.sh'); + expectedResponseLines.push( + `[Info] Converting line endings CRLF -> LF for file: ${fname}`, + ); + } + + api.expectPostRelease({}); + docker.expectGetManifestBusybox(); + + const maxRequestRetries = intVar('BALENARCTEST_API_RETRY_MAX_ATTEMPTS'); + expect( + maxRequestRetries, + 'BALENARCTEST_API_RETRY_MAX_ATTEMPTS must be >= 2 for this test', + ).to.be.greaterThanOrEqual(2); + let failedImagePatchRequests = 0; + let succesfullImagePatchRequests = 0; + api + .optPatch(/^\/v6\/image($|[(?])/, { times: maxRequestRetries }) + .reply((_uri, requestBody) => { + const imageBody = requestBody as Partial< + import('@balena/compose/dist/release/models').ImageModel + >; + expect(imageBody.status).to.equal('success'); + if (failedImagePatchRequests < maxRequestRetries - 1) { + failedImagePatchRequests++; + return [500, 'Patch Image Error']; + } + succesfullImagePatchRequests++; + return [200, 'OK']; + }); + api.expectPatchRelease({}); + api.expectPostImageLabel(); + + await testDockerBuildStream({ + commandLine: `deploy testApp --build --source ${projectPath}`, + dockerMock: docker, + expectedFilesByService: { main: expectedFiles }, + expectedQueryParamsByService: { main: commonQueryParams }, + expectedResponseLines, + projectPath, + responseBody, + responseCode: 200, + services: ['main'], + }); + expect(failedImagePatchRequests).to.equal(maxRequestRetries - 1); + expect(succesfullImagePatchRequests).to.equal(1); + }); + it('should create the expected tar stream (docker-compose, --multi-dockerignore)', async () => { const projectPath = path.join(projectsPath, 'docker-compose', 'basic'); const service1Dockerfile = ( diff --git a/tests/config-tests.ts b/tests/config-tests.ts index 688595d5..7e99e4fa 100644 --- a/tests/config-tests.ts +++ b/tests/config-tests.ts @@ -26,6 +26,11 @@ process.env.BALENARC_NO_SENTRY = '1'; // Like the global `--unsupported` flag process.env.BALENARC_UNSUPPORTED = '1'; +// Reduce the api request retry limits to keep the tests fast. +process.env.BALENARCTEST_API_RETRY_MIN_DELAY_MS = '100'; +process.env.BALENARCTEST_API_RETRY_MAX_DELAY_MS = '1000'; +process.env.BALENARCTEST_API_RETRY_MAX_ATTEMPTS = '2'; + import * as tmp from 'tmp'; tmp.setGracefulCleanup(); // Use a temporary dir for tests data diff --git a/tests/nock/balena-api-mock.ts b/tests/nock/balena-api-mock.ts index fd8da19c..4e321185 100644 --- a/tests/nock/balena-api-mock.ts +++ b/tests/nock/balena-api-mock.ts @@ -35,11 +35,13 @@ export class BalenaAPIMock extends NockMock { notFound = false, optional = false, persist = false, + times = undefined as number | undefined, expandArchitecture = false, } = {}) { const interceptor = this.optGet(/^\/v6\/application($|[(?])/, { optional, persist, + times, }); if (notFound) { interceptor.reply(200, { d: [] }); @@ -105,10 +107,12 @@ export class BalenaAPIMock extends NockMock { notFound = false, optional = false, persist = false, + times = undefined as number | undefined, } = {}) { const interceptor = this.optGet(/^\/v6\/release($|[(?])/, { persist, optional, + times, }); if (notFound) { interceptor.reply(200, { d: [] }); @@ -133,8 +137,9 @@ export class BalenaAPIMock extends NockMock { inspectRequest = this.inspectNoOp, optional = false, persist = false, + times = undefined as number | undefined, }) { - this.optPatch(/^\/v6\/release($|[(?])/, { optional, persist }).reply( + this.optPatch(/^\/v6\/release($|[(?])/, { optional, persist, times }).reply( statusCode, this.getInspectedReplyBodyFunction(inspectRequest, replyBody), ); @@ -148,8 +153,9 @@ export class BalenaAPIMock extends NockMock { inspectRequest = this.inspectNoOp, optional = false, persist = false, + times = undefined as number | undefined, }) { - this.optPost(/^\/v6\/release($|[(?])/, { optional, persist }).reply( + this.optPost(/^\/v6\/release($|[(?])/, { optional, persist, times }).reply( statusCode, this.getInspectedReplyFileFunction( inspectRequest, @@ -167,8 +173,9 @@ export class BalenaAPIMock extends NockMock { inspectRequest = this.inspectNoOp, optional = false, persist = false, + times = undefined as number | undefined, }) { - this.optPatch(/^\/v6\/image($|[(?])/, { optional, persist }).reply( + this.optPatch(/^\/v6\/image($|[(?])/, { optional, persist, times }).reply( statusCode, this.getInspectedReplyBodyFunction(inspectRequest, replyBody), ); diff --git a/tests/nock/nock-mock.ts b/tests/nock/nock-mock.ts index 4dd72ac2..04ce0d6d 100644 --- a/tests/nock/nock-mock.ts +++ b/tests/nock/nock-mock.ts @@ -21,6 +21,7 @@ import * as fs from 'fs'; export interface ScopeOpts { optional?: boolean; persist?: boolean; + times?: number; } /** @@ -52,36 +53,50 @@ export class NockMock { this.expect = this.scope; } + public optMethod( + method: 'get' | 'delete' | 'patch' | 'post', + uri: string | RegExp | ((uri: string) => boolean), + { optional = false, persist = false, times = undefined }: ScopeOpts, + ) { + let scope = this.scope; + if (persist) { + scope = scope.persist(); + } + let reqInterceptor = scope[method](uri); + if (times != null) { + reqInterceptor = reqInterceptor.times(times); + } else if (optional) { + reqInterceptor = reqInterceptor.optionally(); + } + return reqInterceptor; + } + public optGet( uri: string | RegExp | ((uri: string) => boolean), - { optional = false, persist = false }: ScopeOpts, + opts: ScopeOpts, ): nock.Interceptor { - const get = (persist ? this.scope.persist() : this.scope).get(uri); - return optional ? get.optionally() : get; + return this.optMethod('get', uri, opts); } public optDelete( uri: string | RegExp | ((uri: string) => boolean), - { optional = false, persist = false }: ScopeOpts, + opts: ScopeOpts, ) { - const del = (persist ? this.scope.persist() : this.scope).delete(uri); - return optional ? del.optionally() : del; + return this.optMethod('delete', uri, opts); } public optPatch( uri: string | RegExp | ((uri: string) => boolean), - { optional = false, persist = false }: ScopeOpts, + opts: ScopeOpts, ) { - const patch = (persist ? this.scope.persist() : this.scope).patch(uri); - return optional ? patch.optionally() : patch; + return this.optMethod('patch', uri, opts); } public optPost( uri: string | RegExp | ((uri: string) => boolean), - { optional = false, persist = false }: ScopeOpts, + opts: ScopeOpts, ) { - const post = (persist ? this.scope.persist() : this.scope).post(uri); - return optional ? post.optionally() : post; + return this.optMethod('post', uri, opts); } protected inspectNoOp(_uri: string, _requestBody: nock.Body): void {