From 810d940e578c160e7c6e6da6a03d951c00114f1e Mon Sep 17 00:00:00 2001 From: Gal Zahavi <38544478+galz10@users.noreply.github.com> Date: Fri, 24 Oct 2025 14:23:39 -0700 Subject: [PATCH] fix(update): replace update-notifier with latest-version (#11989) --- package-lock.json | 358 +++--------------- package.json | 2 +- packages/cli/package.json | 2 +- packages/cli/src/config/config.ts | 4 + packages/cli/src/ui/utils/updateCheck.test.ts | 69 +--- packages/cli/src/ui/utils/updateCheck.ts | 75 ++-- packages/cli/src/utils/handleAutoUpdate.ts | 7 + 7 files changed, 114 insertions(+), 403 deletions(-) diff --git a/package-lock.json b/package-lock.json index f30a484e63..a0e554676c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ ], "dependencies": { "@testing-library/dom": "^10.4.1", + "latest-version": "^9.0.0", "simple-git": "^3.28.0" }, "bin": { @@ -3891,6 +3892,16 @@ "text-table": "^0.2.0" } }, + "node_modules/@textlint/linter-formatter/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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@textlint/linter-formatter/node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -5481,6 +5492,15 @@ "string-width": "^4.1.0" } }, + "node_modules/ansi-align/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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-align/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -5967,15 +5987,6 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, - "node_modules/atomically": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/atomically/-/atomically-2.0.3.tgz", - "integrity": "sha512-kU6FmrwZ3Lx7/7y3hPS5QnbJfaohcIul5fGqf7ok+4KklIEk9tJ0C2IQPdacSbVUWv6zVHXEBWoWd6NrVMT7Cw==", - "dependencies": { - "stubborn-fs": "^1.2.5", - "when-exit": "^2.1.1" - } - }, "node_modules/auto-bind": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-5.0.1.tgz", @@ -6987,30 +6998,6 @@ "proto-list": "~1.2.1" } }, - "node_modules/config-chain/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, - "node_modules/configstore": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-7.0.0.tgz", - "integrity": "sha512-yk7/5PN5im4qwz0WFZW3PXnzHgPu9mX29Y8uZ3aefe2lBPC1FYttWZRcaW9fKkT0pBCJyuQ2HfbmPVaODi9jcQ==", - "license": "BSD-2-Clause", - "dependencies": { - "atomically": "^2.0.3", - "dot-prop": "^9.0.0", - "graceful-fs": "^4.2.11", - "xdg-basedir": "^5.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/yeoman/configstore?sponsor=1" - } - }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -7630,33 +7617,6 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/dot-prop": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz", - "integrity": "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^4.18.2" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dot-prop/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/dotenv": { "version": "17.1.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.1.0.tgz", @@ -8066,18 +8026,6 @@ "node": ">=6" } }, - "node_modules/escape-goat": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", - "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -9477,21 +9425,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/global-directory": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", - "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", - "license": "MIT", - "dependencies": { - "ini": "4.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/globals": { "version": "16.3.0", "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", @@ -10200,13 +10133,10 @@ "license": "ISC" }, "node_modules/ini": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", - "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" }, "node_modules/ink": { "version": "6.2.3", @@ -10652,21 +10582,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-in-ci": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-in-ci/-/is-in-ci-1.0.0.tgz", - "integrity": "sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==", - "license": "MIT", - "bin": { - "is-in-ci": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-inside-container": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", @@ -10685,22 +10600,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-installed-globally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-1.0.0.tgz", - "integrity": "sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==", - "license": "MIT", - "dependencies": { - "global-directory": "^4.0.1", - "is-path-inside": "^4.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -10734,18 +10633,6 @@ "dev": true, "license": "MIT" }, - "node_modules/is-npm": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.0.0.tgz", - "integrity": "sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -10782,18 +10669,6 @@ "node": ">=8" } }, - "node_modules/is-path-inside": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", - "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-plain-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", @@ -11413,9 +11288,9 @@ "license": "MIT" }, "node_modules/ky": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/ky/-/ky-1.8.1.tgz", - "integrity": "sha512-7Bp3TpsE+L+TARSnnDpk3xg8Idi8RwSLdj6CMbNWoOARIrGrbuLGusV0dYwbZOm4bB3jHNxSw8Wk/ByDqJEnDw==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/ky/-/ky-1.13.0.tgz", + "integrity": "sha512-JeNNGs44hVUp2XxO3FY9WV28ymG7LgO4wju4HL/dCq1A8eKDcFgVrdCn1ssn+3Q/5OQilv5aYsL0DMt5mmAV9w==", "license": "MIT", "engines": { "node": ">=18" @@ -13805,21 +13680,6 @@ "node": ">=6" } }, - "node_modules/pupa": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz", - "integrity": "sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==", - "license": "MIT", - "dependencies": { - "escape-goat": "^4.0.0" - }, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -13940,12 +13800,6 @@ "node": ">=6" } }, - "node_modules/rc/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, "node_modules/rc/node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -15481,15 +15335,6 @@ "node": ">=8" } }, - "node_modules/strip-ansi/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==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -15559,11 +15404,6 @@ "boundary": "^2.0.0" } }, - "node_modules/stubborn-fs": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-1.2.5.tgz", - "integrity": "sha512-H2N9c26eXjzL/S/K+i/RHHcFanE74dptvvjM8iwzwbVcWY/zjBbgRqF3K0DY4+OD+uTTASTBvDoxPDaPN02D7g==" - }, "node_modules/stubs": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", @@ -15724,6 +15564,16 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/table/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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/table/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -16589,126 +16439,6 @@ "node": ">= 0.8" } }, - "node_modules/update-notifier": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-7.3.1.tgz", - "integrity": "sha512-+dwUY4L35XFYEzE+OAL3sarJdUioVovq+8f7lcIJ7wnmnYQV5UD1Y/lcwaMSyaQ6Bj3JMj1XSTjZbNLHn/19yA==", - "license": "BSD-2-Clause", - "dependencies": { - "boxen": "^8.0.1", - "chalk": "^5.3.0", - "configstore": "^7.0.0", - "is-in-ci": "^1.0.0", - "is-installed-globally": "^1.0.0", - "is-npm": "^6.0.0", - "latest-version": "^9.0.0", - "pupa": "^3.1.0", - "semver": "^7.6.3", - "xdg-basedir": "^5.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/yeoman/update-notifier?sponsor=1" - } - }, - "node_modules/update-notifier/node_modules/boxen": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz", - "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==", - "license": "MIT", - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^8.0.0", - "chalk": "^5.3.0", - "cli-boxes": "^3.0.0", - "string-width": "^7.2.0", - "type-fest": "^4.21.0", - "widest-line": "^5.0.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/update-notifier/node_modules/camelcase": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", - "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/update-notifier/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/update-notifier/node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", - "license": "MIT" - }, - "node_modules/update-notifier/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/update-notifier/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/update-notifier/node_modules/widest-line": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", - "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", - "license": "MIT", - "dependencies": { - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -17092,12 +16822,6 @@ "node": ">=18" } }, - "node_modules/when-exit": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.4.tgz", - "integrity": "sha512-4rnvd3A1t16PWzrBUcSDZqcAmsUIy4minDXT/CZ8F2mVDgd65i4Aalimgz1aQkRGU0iH5eT5+6Rx2TK8o443Pg==", - "license": "MIT" - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -18003,6 +17727,7 @@ "ink": "^6.2.3", "ink-gradient": "^3.0.0", "ink-spinner": "^5.0.0", + "latest-version": "^9.0.0", "lowlight": "^3.3.0", "mnemonist": "^0.40.3", "open": "^10.1.2", @@ -18016,7 +17741,6 @@ "strip-json-comments": "^3.1.1", "tar": "^7.5.1", "undici": "^7.10.0", - "update-notifier": "^7.3.1", "wrap-ansi": "9.0.2", "yargs": "^17.7.2", "zod": "^3.23.8" @@ -18079,6 +17803,12 @@ } } }, + "packages/cli/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, "packages/cli/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", diff --git a/package.json b/package.json index c0a3885231..ae3bdfa852 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,6 @@ }, "overrides": { "wrap-ansi": "9.0.2", - "ansi-regex": "5.0.1", "cliui": { "wrap-ansi": "7.0.0" } @@ -113,6 +112,7 @@ }, "dependencies": { "@testing-library/dom": "^10.4.1", + "latest-version": "^9.0.0", "simple-git": "^3.28.0" }, "optionalDependencies": { diff --git a/packages/cli/package.json b/packages/cli/package.json index a2e62e4a33..df73c1496b 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -45,6 +45,7 @@ "ink": "^6.2.3", "ink-gradient": "^3.0.0", "ink-spinner": "^5.0.0", + "latest-version": "^9.0.0", "lowlight": "^3.3.0", "mnemonist": "^0.40.3", "open": "^10.1.2", @@ -58,7 +59,6 @@ "strip-json-comments": "^3.1.1", "tar": "^7.5.1", "undici": "^7.10.0", - "update-notifier": "^7.3.1", "wrap-ansi": "9.0.2", "yargs": "^17.7.2", "zod": "^3.23.8" diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts index 760b8c4097..7617770b79 100755 --- a/packages/cli/src/config/config.ts +++ b/packages/cli/src/config/config.ts @@ -375,6 +375,10 @@ export async function loadCliConfig( ): Promise { const debugMode = isDebugMode(argv); + if (argv.sandbox) { + process.env['GEMINI_SANDBOX'] = 'true'; + } + const memoryImportFormat = settings.context?.importFormat || 'tree'; const ideMode = settings.ide?.enabled ?? false; diff --git a/packages/cli/src/ui/utils/updateCheck.test.ts b/packages/cli/src/ui/utils/updateCheck.test.ts index 4a2a74c83a..085fd2ea28 100644 --- a/packages/cli/src/ui/utils/updateCheck.test.ts +++ b/packages/cli/src/ui/utils/updateCheck.test.ts @@ -13,9 +13,9 @@ vi.mock('../../utils/package.js', () => ({ getPackageJson, })); -const updateNotifier = vi.hoisted(() => vi.fn()); -vi.mock('update-notifier', () => ({ - default: updateNotifier, +const latestVersion = vi.hoisted(() => vi.fn()); +vi.mock('latest-version', () => ({ + default: latestVersion, })); describe('checkForUpdates', () => { @@ -46,7 +46,7 @@ describe('checkForUpdates', () => { const result = await checkForUpdates(mockSettings); expect(result).toBeNull(); expect(getPackageJson).not.toHaveBeenCalled(); - expect(updateNotifier).not.toHaveBeenCalled(); + expect(latestVersion).not.toHaveBeenCalled(); }); it('should return null when running from source (DEV=true)', async () => { @@ -55,15 +55,11 @@ describe('checkForUpdates', () => { name: 'test-package', version: '1.0.0', }); - updateNotifier.mockReturnValue({ - fetchInfo: vi - .fn() - .mockResolvedValue({ current: '1.0.0', latest: '1.1.0' }), - }); + latestVersion.mockResolvedValue('1.1.0'); const result = await checkForUpdates(mockSettings); expect(result).toBeNull(); expect(getPackageJson).not.toHaveBeenCalled(); - expect(updateNotifier).not.toHaveBeenCalled(); + expect(latestVersion).not.toHaveBeenCalled(); }); it('should return null if package.json is missing', async () => { @@ -77,9 +73,7 @@ describe('checkForUpdates', () => { name: 'test-package', version: '1.0.0', }); - updateNotifier.mockReturnValue({ - fetchInfo: vi.fn().mockResolvedValue(null), - }); + latestVersion.mockResolvedValue('1.0.0'); const result = await checkForUpdates(mockSettings); expect(result).toBeNull(); }); @@ -89,15 +83,13 @@ describe('checkForUpdates', () => { name: 'test-package', version: '1.0.0', }); - updateNotifier.mockReturnValue({ - fetchInfo: vi - .fn() - .mockResolvedValue({ current: '1.0.0', latest: '1.1.0' }), - }); + latestVersion.mockResolvedValue('1.1.0'); const result = await checkForUpdates(mockSettings); expect(result?.message).toContain('1.0.0 → 1.1.0'); - expect(result?.update).toEqual({ current: '1.0.0', latest: '1.1.0' }); + expect(result?.update.current).toEqual('1.0.0'); + expect(result?.update.latest).toEqual('1.1.0'); + expect(result?.update.name).toEqual('test-package'); }); it('should return null if the latest version is the same as the current version', async () => { @@ -105,11 +97,7 @@ describe('checkForUpdates', () => { name: 'test-package', version: '1.0.0', }); - updateNotifier.mockReturnValue({ - fetchInfo: vi - .fn() - .mockResolvedValue({ current: '1.0.0', latest: '1.0.0' }), - }); + latestVersion.mockResolvedValue('1.0.0'); const result = await checkForUpdates(mockSettings); expect(result).toBeNull(); }); @@ -119,23 +107,17 @@ describe('checkForUpdates', () => { name: 'test-package', version: '1.1.0', }); - updateNotifier.mockReturnValue({ - fetchInfo: vi - .fn() - .mockResolvedValue({ current: '1.1.0', latest: '1.0.0' }), - }); + latestVersion.mockResolvedValue('1.0.0'); const result = await checkForUpdates(mockSettings); expect(result).toBeNull(); }); - it('should return null if fetchInfo rejects', async () => { + it('should return null if latestVersion rejects', async () => { getPackageJson.mockResolvedValue({ name: 'test-package', version: '1.0.0', }); - updateNotifier.mockReturnValue({ - fetchInfo: vi.fn().mockRejectedValue(new Error('Timeout')), - }); + latestVersion.mockRejectedValue(new Error('Timeout')); const result = await checkForUpdates(mockSettings); expect(result).toBeNull(); @@ -154,26 +136,13 @@ describe('checkForUpdates', () => { version: '1.2.3-nightly.1', }); - const fetchInfoMock = vi.fn().mockImplementation(({ distTag }) => { - if (distTag === 'nightly') { - return Promise.resolve({ - latest: '1.2.3-nightly.2', - current: '1.2.3-nightly.1', - }); + latestVersion.mockImplementation(async (name, options) => { + if (options?.version === 'nightly') { + return '1.2.3-nightly.2'; } - if (distTag === 'latest') { - return Promise.resolve({ - latest: '1.2.3', - current: '1.2.3-nightly.1', - }); - } - return Promise.resolve(null); + return '1.2.3'; }); - updateNotifier.mockImplementation(({ pkg, distTag }) => ({ - fetchInfo: () => fetchInfoMock({ pkg, distTag }), - })); - const result = await checkForUpdates(mockSettings); expect(result?.message).toContain('1.2.3-nightly.1 → 1.2.3-nightly.2'); expect(result?.update.latest).toBe('1.2.3-nightly.2'); diff --git a/packages/cli/src/ui/utils/updateCheck.ts b/packages/cli/src/ui/utils/updateCheck.ts index f924964370..6a6de8518d 100644 --- a/packages/cli/src/ui/utils/updateCheck.ts +++ b/packages/cli/src/ui/utils/updateCheck.ts @@ -4,8 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { UpdateInfo } from 'update-notifier'; -import updateNotifier from 'update-notifier'; +import latestVersion from 'latest-version'; import semver from 'semver'; import { getPackageJson } from '../../utils/package.js'; import type { LoadedSettings } from '../../config/settings.js'; @@ -13,32 +12,35 @@ import { debugLogger } from '@google/gemini-cli-core'; export const FETCH_TIMEOUT_MS = 2000; +// Replicating the bits of UpdateInfo we need from update-notifier +export interface UpdateInfo { + latest: string; + current: string; + name: string; + type?: semver.ReleaseType; +} + export interface UpdateObject { message: string; update: UpdateInfo; } /** - * From a nightly and stable update, determines which is the "best" one to offer. + * From a nightly and stable version, determines which is the "best" one to offer. * The rule is to always prefer nightly if the base versions are the same. */ function getBestAvailableUpdate( - nightly?: UpdateInfo, - stable?: UpdateInfo, -): UpdateInfo | null { + nightly?: string, + stable?: string, +): string | null { if (!nightly) return stable || null; if (!stable) return nightly || null; - const nightlyVer = nightly.latest; - const stableVer = stable.latest; - - if ( - semver.coerce(stableVer)?.version === semver.coerce(nightlyVer)?.version - ) { + if (semver.coerce(stable)?.version === semver.coerce(nightly)?.version) { return nightly; } - return semver.gt(stableVer, nightlyVer) ? stable : nightly; + return semver.gt(stable, nightly) ? stable : nightly; } export async function checkForUpdates( @@ -59,43 +61,42 @@ export async function checkForUpdates( const { name, version: currentVersion } = packageJson; const isNightly = currentVersion.includes('nightly'); - const createNotifier = (distTag: 'latest' | 'nightly') => - updateNotifier({ - pkg: { - name, - version: currentVersion, - }, - updateCheckInterval: 0, - shouldNotifyInNpmScript: true, - distTag, - }); if (isNightly) { - const [nightlyUpdateInfo, latestUpdateInfo] = await Promise.all([ - createNotifier('nightly').fetchInfo(), - createNotifier('latest').fetchInfo(), + const [nightlyUpdate, latestUpdate] = await Promise.all([ + latestVersion(name, { version: 'nightly' }), + latestVersion(name), ]); - const bestUpdate = getBestAvailableUpdate( - nightlyUpdateInfo, - latestUpdateInfo, - ); + const bestUpdate = getBestAvailableUpdate(nightlyUpdate, latestUpdate); - if (bestUpdate && semver.gt(bestUpdate.latest, currentVersion)) { - const message = `A new version of Gemini CLI is available! ${currentVersion} → ${bestUpdate.latest}`; + if (bestUpdate && semver.gt(bestUpdate, currentVersion)) { + const message = `A new version of Gemini CLI is available! ${currentVersion} → ${bestUpdate}`; + const type = semver.diff(bestUpdate, currentVersion) || undefined; return { message, - update: { ...bestUpdate, current: currentVersion }, + update: { + latest: bestUpdate, + current: currentVersion, + name, + type, + }, }; } } else { - const updateInfo = await createNotifier('latest').fetchInfo(); + const latestUpdate = await latestVersion(name); - if (updateInfo && semver.gt(updateInfo.latest, currentVersion)) { - const message = `Gemini CLI update available! ${currentVersion} → ${updateInfo.latest}`; + if (latestUpdate && semver.gt(latestUpdate, currentVersion)) { + const message = `Gemini CLI update available! ${currentVersion} → ${latestUpdate}`; + const type = semver.diff(latestUpdate, currentVersion) || undefined; return { message, - update: { ...updateInfo, current: currentVersion }, + update: { + latest: latestUpdate, + current: currentVersion, + name, + type, + }, }; } } diff --git a/packages/cli/src/utils/handleAutoUpdate.ts b/packages/cli/src/utils/handleAutoUpdate.ts index a41ddc3592..e546b0c6fc 100644 --- a/packages/cli/src/utils/handleAutoUpdate.ts +++ b/packages/cli/src/utils/handleAutoUpdate.ts @@ -23,6 +23,13 @@ export function handleAutoUpdate( return; } + if (settings.merged.tools?.sandbox || process.env['GEMINI_SANDBOX']) { + updateEventEmitter.emit('update-info', { + message: `${info.message}\nAutomatic update is not available in sandbox mode.`, + }); + return; + } + if (settings.merged.general?.disableUpdateNag) { return; }