diff --git a/common/changes/@azure-tools/codegen/improve-list-versions_2022-03-10-16-52.json b/common/changes/@azure-tools/codegen/improve-list-versions_2022-03-10-16-52.json new file mode 100644 index 0000000000..78746970ee --- /dev/null +++ b/common/changes/@azure-tools/codegen/improve-list-versions_2022-03-10-16-52.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@azure-tools/codegen", + "comment": "Bump `semver` dependency", + "type": "patch" + } + ], + "packageName": "@azure-tools/codegen", + "email": "tiguerin@microsoft.com" +} \ No newline at end of file diff --git a/common/changes/@azure-tools/extension/improve-list-versions_2022-03-10-16-52.json b/common/changes/@azure-tools/extension/improve-list-versions_2022-03-10-16-52.json new file mode 100644 index 0000000000..4ebc5ae4ed --- /dev/null +++ b/common/changes/@azure-tools/extension/improve-list-versions_2022-03-10-16-52.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@azure-tools/extension", + "comment": "Stop using shell call to `yarn` or `npm` to resolve list of available version and use `pactote` library. Improving reliability and performance.", + "type": "minor" + } + ], + "packageName": "@azure-tools/extension", + "email": "tiguerin@microsoft.com" +} \ No newline at end of file diff --git a/common/changes/autorest/improve-list-versions_2022-03-10-16-52.json b/common/changes/autorest/improve-list-versions_2022-03-10-16-52.json new file mode 100644 index 0000000000..1e537a1d98 --- /dev/null +++ b/common/changes/autorest/improve-list-versions_2022-03-10-16-52.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "autorest", + "comment": "Cleanup logic with using `--list-available`.", + "type": "minor" + } + ], + "packageName": "autorest", + "email": "tiguerin@microsoft.com" +} diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index fca0096532..678d41df4f 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -85,14 +85,13 @@ dependencies: morgan: 1.10.0 mustache: 4.2.0 npm: 6.14.9 - npm-normalize-package-bin: 1.0.1 - npm-package-arg: 8.1.5 - pacote: 9.2.3 + npm-package-arg: 9.0.0 + pacote: 9.5.12 prettier: 2.3.2 recursive-diff: 1.0.8 rimraf: 3.0.2 safe-buffer: 5.2.0 - semver: 5.7.1 + semver: 7.3.5 source-map: 0.7.3 source-map-support: 0.5.21 supertest: 6.2.2 @@ -115,7 +114,7 @@ dependencies: winston: 3.3.4 yaml-ast-parser: 0.0.43 yargs: 17.2.1 - yarn: 1.22.10 + yarn: 1.22.17 lockfileVersion: 5.2 packages: /@ampproject/remapping/2.1.2: @@ -2130,13 +2129,14 @@ packages: node: '>= 0.8' resolution: integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== - /cacache/11.3.3: + /cacache/12.0.4: dependencies: bluebird: 3.7.2 chownr: 1.1.4 figgy-pudding: 3.5.2 glob: 7.1.7 graceful-fs: 4.2.9 + infer-owner: 1.0.4 lru-cache: 5.1.1 mississippi: 3.0.0 mkdirp: 0.5.5 @@ -2148,7 +2148,7 @@ packages: y18n: 4.0.3 dev: false resolution: - integrity: sha512-p8WcneCytvzPxhDvYp31PD039vi77I12W+/KfR9S8AZbaiARFBCpsPJS+9uhWfeBfeAtW7o/4vt3MUqLkbY6nA== + integrity: sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== /cache-base/1.0.1: dependencies: collection-visit: 1.0.0 @@ -4350,6 +4350,10 @@ packages: node: '>=4' resolution: integrity: sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= + /infer-owner/1.0.4: + dev: false + resolution: + integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== /inflight/1.0.6: dependencies: once: 1.4.0 @@ -5739,13 +5743,6 @@ packages: node: '>=0.10.0' resolution: integrity: sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= - /lru-cache/4.1.5: - dependencies: - pseudomap: 1.0.2 - yallist: 2.1.2 - dev: false - resolution: - integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== /lru-cache/5.1.1: dependencies: yallist: 3.1.1 @@ -5781,10 +5778,10 @@ packages: dev: false resolution: integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - /make-fetch-happen/4.0.2: + /make-fetch-happen/5.0.2: dependencies: agentkeepalive: 3.5.2 - cacache: 11.3.3 + cacache: 12.0.4 http-cache-semantics: 3.8.1 http-proxy-agent: 2.1.0 https-proxy-agent: 2.2.4 @@ -5796,7 +5793,7 @@ packages: ssri: 6.0.2 dev: false resolution: - integrity: sha512-YMJrAjHSb/BordlsDEcVcPyTbiJKkzqMf48N8dAJZT9Zjctrkb6Yg4TY9Sq2AwSIQJFn5qBBKVTYt3vP5FMIHA== + integrity: sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag== /makeerror/1.0.12: dependencies: tmpl: 1.0.5 @@ -6252,16 +6249,16 @@ packages: dev: false resolution: integrity: sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg== - /npm-package-arg/8.1.5: + /npm-package-arg/9.0.0: dependencies: hosted-git-info: 4.1.0 semver: 7.3.5 validate-npm-package-name: 3.0.0 dev: false engines: - node: '>=10' + node: ^12.13.0 || ^14.15.0 || >=16 resolution: - integrity: sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q== + integrity: sha512-yhzXxeor+Zfhe5MGwPdDumz6HtNlj2pMekWB95IX3CC6uDNgde0oPKHDCLDPoJqQfd0HqAWt+y4Hs5m7CK1+9Q== /npm-packlist/1.4.8: dependencies: ignore-walk: 3.0.4 @@ -6270,25 +6267,26 @@ packages: dev: false resolution: integrity: sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== - /npm-pick-manifest/2.2.3: + /npm-pick-manifest/3.0.2: dependencies: figgy-pudding: 3.5.2 npm-package-arg: 6.1.1 semver: 5.7.1 dev: false resolution: - integrity: sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA== - /npm-registry-fetch/3.9.1: + integrity: sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw== + /npm-registry-fetch/4.0.7: dependencies: JSONStream: 1.3.5 bluebird: 3.7.2 figgy-pudding: 3.5.2 lru-cache: 5.1.1 - make-fetch-happen: 4.0.2 + make-fetch-happen: 5.0.2 npm-package-arg: 6.1.1 + safe-buffer: 5.2.1 dev: false resolution: - integrity: sha512-VQCEZlydXw4AwLROAXWUR7QDfe2Y8Id/vpAgp6TI1/H78a4SiQ1kQrKZALm5/zxM5n4HIi+aYb+idUAV/RuY0Q== + integrity: sha512-cny9v0+Mq6Tjz+e0erFAB+RYJ/AVGzkjnISiobqP8OWj9c9FLoZZu8/SPSKJWE17F1tk4018wfjV+ZbIbqC7fQ== /npm-run-path/2.0.2: dependencies: path-key: 2.0.1 @@ -6693,24 +6691,27 @@ packages: node: '>=6' resolution: integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - /pacote/9.2.3: + /pacote/9.5.12: dependencies: bluebird: 3.7.2 - cacache: 11.3.3 + cacache: 12.0.4 + chownr: 1.1.4 figgy-pudding: 3.5.2 get-stream: 4.1.0 glob: 7.1.7 - lru-cache: 4.1.5 - make-fetch-happen: 4.0.2 + infer-owner: 1.0.4 + lru-cache: 5.1.1 + make-fetch-happen: 5.0.2 minimatch: 3.1.2 minipass: 2.9.0 mississippi: 3.0.0 mkdirp: 0.5.5 normalize-package-data: 2.5.0 + npm-normalize-package-bin: 1.0.1 npm-package-arg: 6.1.1 npm-packlist: 1.4.8 - npm-pick-manifest: 2.2.3 - npm-registry-fetch: 3.9.1 + npm-pick-manifest: 3.0.2 + npm-registry-fetch: 4.0.7 osenv: 0.1.5 promise-inflight: 1.0.1 promise-retry: 1.1.1 @@ -6724,7 +6725,7 @@ packages: which: 1.3.1 dev: false resolution: - integrity: sha512-Y3+yY3nBRAxMlZWvr62XLJxOwCmG9UmkGZkFurWHoCjqF0cZL72cTOCRJTvWw8T4OhJS2RTg13x4oYYriauvEw== + integrity: sha512-BUIj/4kKbwWg4RtnBncXPJd15piFSVNpTzY0rysSr3VnMowTYgkGKcaHrbReepAkjTr8lH2CVWRi58Spg2CicQ== /parallel-transform/1.2.0: dependencies: cyclist: 1.0.1 @@ -7027,10 +7028,6 @@ packages: node: '>= 0.10' resolution: integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== - /pseudomap/1.0.2: - dev: false - resolution: - integrity: sha1-8FKijacOYYkX7wqKw0wa5aaChrM= /psl/1.8.0: dev: false resolution: @@ -8967,7 +8964,7 @@ packages: integrity: sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== /wide-align/1.1.5: dependencies: - string-width: 1.0.2 + string-width: 4.2.3 dev: false resolution: integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== @@ -9082,10 +9079,6 @@ packages: node: '>=10' resolution: integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - /yallist/2.1.2: - dev: false - resolution: - integrity: sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= /yallist/3.1.1: dev: false resolution: @@ -9184,14 +9177,14 @@ packages: node: '>=12' resolution: integrity: sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q== - /yarn/1.22.10: + /yarn/1.22.17: dev: false engines: node: '>=4.0.0' hasBin: true requiresBuild: true resolution: - integrity: sha512-IanQGI9RRPAN87VGTF7zs2uxkSyQSrSPsju0COgbsKQOOXr5LtcVPeyXWgwVa0ywG3d8dg6kSYKGBuYK021qeA== + integrity: sha512-H0p241BXaH0UN9IeH//RT82tl5PfNraVpSpEoW+ET7lmopNC61eZ+A+IDvU8FM6Go5vx162SncDL8J1ZjRBriQ== /yn/2.0.0: dev: false engines: @@ -9238,7 +9231,7 @@ packages: prettier: 2.3.2 rimraf: 3.0.2 safe-buffer: 5.2.0 - semver: 5.7.1 + semver: 7.3.5 source-map: 0.7.3 source-map-support: 0.5.21 ts-jest: 27.1.3_292c4e99862771b539f45e23f67003cc @@ -9253,7 +9246,7 @@ packages: peerDependencies: ts-node: '*' resolution: - integrity: sha512-XloMFBAw1oi4A9cythhKy4B1XH/PaEFyQI5GN/9SQo2/SYTxhDJSMMhTQ9ktB8KzmK/2R3n3/vHXCanctYn/LQ== + integrity: sha512-CgBJweMQtsK8peEGdCCHO6qNSQueEsMgsYhiHXjEN5JeyGl7wgpvNVMmSbZCdSisxxj5tVUIyvOdv8BUyq7KWw== tarball: file:projects/autorest.tgz version: 0.0.0 file:projects/cadl.tgz_ts-node@9.1.1+webpack@5.40.0: @@ -9309,7 +9302,7 @@ packages: jest: 27.5.1_ts-node@9.1.1 js-yaml: 4.0.0 rimraf: 3.0.2 - semver: 5.7.1 + semver: 7.3.5 source-map-support: 0.5.21 ts-jest: 27.1.3_292c4e99862771b539f45e23f67003cc typescript: 4.4.4 @@ -9320,7 +9313,7 @@ packages: prettier: '*' ts-node: '*' resolution: - integrity: sha512-G+bprtkqcXKoat2JELN9SYYHCsBAUhYXxUoW+4hOoxParpLTnw2xX4pt60VBE8jrfyisZrclXX9CYj21ufNzsQ== + integrity: sha512-2o1naqmqzOv9pJeeUzrkO3rXvyaUjVn/Lrc2CVhYjrYCL6y9aQMTpoJL66CHlI3u3Gy4Jsp1L341IK8MpP1+mg== tarball: file:projects/codegen.tgz version: 0.0.0 file:projects/codemodel.tgz_prettier@2.3.2: @@ -9615,14 +9608,14 @@ packages: mkdirp: 0.5.5 npm: 6.14.9 npm-normalize-package-bin: 1.0.1 - npm-package-arg: 8.1.5 - pacote: 9.2.3 + npm-package-arg: 9.0.0 + pacote: 9.5.12 rimraf: 3.0.2 safe-buffer: 5.2.0 - semver: 5.7.1 + semver: 7.3.5 source-map-support: 0.5.21 typescript: 4.4.4 - yarn: 1.22.10 + yarn: 1.22.17 dev: false id: file:projects/extension.tgz name: '@rush-temp/extension' @@ -9630,7 +9623,7 @@ packages: prettier: '*' ts-node: '*' resolution: - integrity: sha512-jpSfbjbNzvaCAXempq/JLTmHe5YLzSnB2xbnyw8MMOoKhCepybR0onj/V1X9N93yE4ebi6Vjo773JKCFQJrHPg== + integrity: sha512-G8EMr8cYd8S/lo9KjbXdwACY6TKjkqdFGth6ofn+86LqjG4+OifXT1NVLMlwghNXddsPzabcCIQp6aLm465Ccw== tarball: file:projects/extension.tgz version: 0.0.0 file:projects/fixer.tgz_ts-node@9.1.1: @@ -9935,6 +9928,7 @@ packages: integrity: sha512-qi2rEcvBtxQHnMA94NCGN35/2sAgi13XqbhkQHhVSZP8ccQObBI+HLAHNHBQToR5bRn9KvzRdqewz3HXkcW3Lw== tarball: file:projects/yaml.tgz version: 0.0.0 +registry: '' specifiers: '@azure-tools/async-io': ~3.0.0 '@azure-tools/object-comparison': ~3.0.0 @@ -10022,14 +10016,13 @@ specifiers: morgan: ^1.10.0 mustache: ^4.1.0 npm: 6.14.9 - npm-normalize-package-bin: 1.0.1 - npm-package-arg: ^8.1.0 - pacote: 9.2.3 + npm-package-arg: ^9.0.0 + pacote: ~9.5.12 prettier: ~2.3.1 recursive-diff: ~1.0.6 rimraf: ^3.0.2 safe-buffer: 5.2.0 - semver: ^5.5.1 + semver: ^7.3.5 source-map: 0.7.3 source-map-support: ^0.5.19 supertest: ^6.0.1 @@ -10052,4 +10045,4 @@ specifiers: winston: ~3.3.3 yaml-ast-parser: 0.0.43 yargs: 17.2.1 - yarn: 1.22.10 + yarn: 1.22.17 diff --git a/packages/apps/autorest/package.json b/packages/apps/autorest/package.json index ed323dd9c7..774abaa409 100644 --- a/packages/apps/autorest/package.json +++ b/packages/apps/autorest/package.json @@ -73,7 +73,7 @@ "prettier": "~2.3.1", "rimraf": "^3.0.2", "safe-buffer": "5.2.0", - "semver": "^5.5.1", + "semver": "^7.3.5", "source-map-support": "^0.5.19", "source-map": "0.7.3", "ts-jest": "^27.0.3", diff --git a/packages/apps/autorest/src/autorest-as-a-service.ts b/packages/apps/autorest/src/autorest-as-a-service.ts index 2c77c9532e..610a9991d1 100644 --- a/packages/apps/autorest/src/autorest-as-a-service.ts +++ b/packages/apps/autorest/src/autorest-as-a-service.ts @@ -57,13 +57,7 @@ export async function availableVersions() { const vers = (await (await extensionManager).getPackageVersions(newCorePackage)).sort((b, a) => semver.compare(a, b), ); - const result = new Array(); - for (const ver of vers) { - if (semver.satisfies(ver, versionRange)) { - result.push(ver); - } - } - return result; + return vers.filter((x) => semver.satisfies(x, versionRange)); } catch (e) { console.info(`No available versions of package ${newCorePackage} found.`); } diff --git a/packages/libs/codegen/package.json b/packages/libs/codegen/package.json index 194a693ec0..77a0eb846a 100644 --- a/packages/libs/codegen/package.json +++ b/packages/libs/codegen/package.json @@ -57,6 +57,6 @@ "dependencies": { "@azure-tools/async-io": "~3.0.0", "js-yaml": "~4.0.0", - "semver": "^5.5.1" + "semver": "^7.3.5" } } diff --git a/packages/libs/extension/jest.e2e.config.js b/packages/libs/extension/jest.e2e.config.js new file mode 100644 index 0000000000..2347936d25 --- /dev/null +++ b/packages/libs/extension/jest.e2e.config.js @@ -0,0 +1,10 @@ +// @ts-check + +const defaultConfig = require("./jest.config"); + +const config = { + ...defaultConfig, + testMatch: ["/test/**/*.e2e.ts"], +}; + +module.exports = config; diff --git a/packages/libs/extension/package.json b/packages/libs/extension/package.json index ff0735b942..cc305f1a6a 100644 --- a/packages/libs/extension/package.json +++ b/packages/libs/extension/package.json @@ -14,8 +14,11 @@ "lint:fix": "eslint ./src --fix --ext .ts", "lint": "eslint ./src --ext .ts --max-warnings=0", "prepare": "npm run build", - "test": "jest --watchAll --coverage=false", - "test:ci": "jest --ci", + "test": "jest --coverage=false --watch", + "test:unit:ci": "jest --ci", + "test:e2e": "jest --forceExit --runInBand --config ./jest.e2e.config.js", + "test:e2e:ci": "jest --ci --forceExit --runInBand --config ./jest.e2e.config.js", + "test:ci": "npm run test:unit:ci && npm run test:e2e:ci", "clean": "rimraf ./dist ./temp" }, "repository": { @@ -44,14 +47,12 @@ "@azure-tools/tasks": "~3.0.0", "@azure/logger": "^1.0.2", "command-exists": "~1.2.9", - "npm-normalize-package-bin": "1.0.1", - "npm-package-arg": "^8.1.0", + "npm-package-arg": "^9.0.0", "npm": "6.14.9", - "pacote": "9.2.3", + "pacote": "~9.5.12", "rimraf": "^3.0.2", - "safe-buffer": "5.2.0", - "semver": "^5.5.1", - "yarn": "1.22.10" + "semver": "^7.3.5", + "yarn": "1.22.17" }, "devDependencies": { "@types/jest": "^26.0.20", diff --git a/packages/libs/extension/src/main.ts b/packages/libs/extension/src/main.ts index 9395a8d89b..4f5cd0d2e9 100644 --- a/packages/libs/extension/src/main.ts +++ b/packages/libs/extension/src/main.ts @@ -6,7 +6,7 @@ import { ChildProcess, spawn } from "child_process"; import { homedir, tmpdir } from "os"; import { basename, delimiter, dirname, extname, isAbsolute, join, normalize, resolve } from "path"; -import { exists, isDirectory, isFile, mkdir, readdir, readFile, rmdir } from "@azure-tools/async-io"; +import { exists, isDirectory, isFile, mkdir, readdir, rmdir } from "@azure-tools/async-io"; import { CriticalSection, Delay, Exception, Mutex, shallowCopy, SharedLock } from "@azure-tools/tasks"; import { resolve as npmResolvePackage } from "npm-package-arg"; import * as pacote from "pacote"; @@ -207,9 +207,15 @@ export class ExtensionManager { private packageManager: PackageManager, ) {} - public async getPackageVersions(name: string): Promise> { - const versions = await this.packageManager.getPackageVersions(process.cwd(), name); - return versions.sort((b, a) => semver.compare(a, b)); + /** + * Return the list of version for the given package name [+ version range] + * + * @param name Name of the package with or without version range. + * @returns List of semver versions + */ + public async getPackageVersions(name: string): Promise { + const packument = await pacote.packument(name); + return Object.keys(packument.versions); } public async findPackage(name: string, version = "latest"): Promise { @@ -222,15 +228,8 @@ export class ExtensionManager { // version can be a version or any one of the formats that // npm accepts (path, targz, git repo) const resolved = resolveName(name, version); - let resolvedName = resolved.raw; + const resolvedName = resolved.raw; - // get all matching package versions for that - if (version.startsWith("~") || version.startsWith("^")) { - const vers = (await this.getPackageVersions(resolved.raw)).filter((each) => semver.satisfies(each, version)); - if (vers.length > 0) { - resolvedName = `${resolved.name}@${vers[0]}`; - } - } // get the package metadata const pm = await fetchPackageMetadata(resolvedName); return new Package(resolved, pm, this); @@ -367,14 +366,10 @@ export class ExtensionManager { // create the folder await mkdir(extension.location); - const promise = this.packageManager.install( - extension.location, - [pkg.packageMetadata._resolved], - { force }, - (progress) => { - reportProgress({ pkg, ...progress }); - }, - ); + const pkgRef = getPkgRef(pkg.packageMetadata); + const promise = this.packageManager.install(extension.location, [pkgRef], { force }, (progress) => { + reportProgress({ pkg, ...progress }); + }); await extensionRelease(); const result = await promise; @@ -528,3 +523,12 @@ function formatLogEntry(entry: PackageManagerLogEntry): string { const spacing = " ".repeat(entry.severity.length); return [`${entry.severity}: ${first}`, ...lines.map((x) => `${spacing} ${x}`)].join("\n"); } + +function getPkgRef(pkg: pacote.ManifestResult) { + // Change in pacote https://github.com/npm/pacote/issues/20 + // Issue with git+ssh in yarn + performance of git+https is much worse that github https://github.com/yarnpkg/yarn/issues/6417 + // if (pkg._from.startsWith("github:")) { + // return pkg._from; + // } + return pkg._resolved; +} diff --git a/packages/libs/extension/src/npm.ts b/packages/libs/extension/src/npm.ts index 97dd815a25..96b241be93 100644 --- a/packages/libs/extension/src/npm.ts +++ b/packages/libs/extension/src/npm.ts @@ -53,10 +53,4 @@ export class Npm implements PackageManager { public async clean(directory: string): Promise { await execNpm(directory, "cache", "clean", "--force"); } - - public async getPackageVersions(directory: string, packageName: string): Promise { - const result = await execNpm(directory, "view", packageName, "versions", "--json"); - - return JSON.parse(result.stdout).data; - } } diff --git a/packages/libs/extension/src/package-manager.ts b/packages/libs/extension/src/package-manager.ts index a138bfdadf..fc1b6bd54f 100644 --- a/packages/libs/extension/src/package-manager.ts +++ b/packages/libs/extension/src/package-manager.ts @@ -54,8 +54,6 @@ export interface PackageManager { ): Promise; clean(directory: string): Promise; - - getPackageVersions(directory: string, packageName: string): Promise; } /** diff --git a/packages/libs/extension/src/yarn.ts b/packages/libs/extension/src/yarn.ts index 529ad5cb22..e641cdfd0f 100644 --- a/packages/libs/extension/src/yarn.ts +++ b/packages/libs/extension/src/yarn.ts @@ -98,11 +98,6 @@ export class Yarn implements PackageManager { await this.execYarn(directory, ["cache", "clean", "--force"]); } - public async getPackageVersions(directory: string, packageName: string): Promise { - const result = await this.execYarn(directory, ["info", packageName, "versions", "--json"]); - return JSON.parse(result.stdout).data; - } - public async execYarn(cwd: string, args: string[], onYarnEvent?: (event: YarnEvent) => void) { const procArgs = [ this.pathToYarnCli ?? (await getPathToYarnCli()), diff --git a/packages/libs/extension/test/test-extensions.e2e.ts b/packages/libs/extension/test/test-extensions.e2e.ts new file mode 100644 index 0000000000..9ef6b2d9d1 --- /dev/null +++ b/packages/libs/extension/test/test-extensions.e2e.ts @@ -0,0 +1,183 @@ +import * as fs from "fs"; +import * as os from "os"; +import * as asyncio from "@azure-tools/async-io"; +import * as tasks from "@azure-tools/tasks"; +import { ExtensionManager, InvalidPackageIdentityException, UnresolvedPackageException } from "../src"; + +const rootTmpFolder = fs.mkdtempSync(`${os.tmpdir()}/test`); + +// Those test do install pacakge and could take a little bit of time. Increasing timeout to 50s. +const TEST_TIMEOUT = 50_000; + +describe("TestExtensions", () => { + let extensionManager: ExtensionManager; + let tmpFolder: string; + + beforeEach(async () => { + tmpFolder = fs.mkdtempSync(`${rootTmpFolder}/install-pkg`); + + extensionManager = await ExtensionManager.Create(tmpFolder); + }); + + afterEach(async () => { + await extensionManager.dispose(); + await tasks.Delay(500); + // await fs.promises.rm(tmpFolder, { force: true, recursive: true }); + }); + + it( + "reset", + async () => { + await extensionManager.reset(); + // install it once + const pkg1 = await extensionManager.findPackage("echo-cli", "*"); + const extension1 = await extensionManager.installPackage(pkg1, false, 60000, (i) => {}); + expect(await extension1.configuration).not.toEqual(""); + // install/overwrite + const dni = await extensionManager.findPackage("echo-cli", "*"); + const installing = extensionManager.installPackage(dni, true, 60000, (i) => {}); + + // install at the same time? + const dni2 = await extensionManager.findPackage("echo-cli", "*"); + const installing2 = extensionManager.installPackage(dni2, true, 60000, (i) => {}); + + // wait for it. + const extension = await installing; + expect(await extension.configuration).not.toEqual(""); + + const extension2 = await installing2; + expect(await extension2.configuration).not.toEqual(""); + + const installedExtensions = await extensionManager.getInstalledExtensions(); + expect(installedExtensions).not.toHaveLength(0); + + for (const each of installedExtensions) { + expect(each.name).toEqual("echo-cli"); + } + await tasks.Delay(5000); + }, + TEST_TIMEOUT, + ); + + /* + @test async 'FindPackage- in github'() { + // github repo style + const npmpkg = await extensionManager.findPackage('npm', 'npm/npm'); + assert.equal(npmpkg.name, 'npm'); + } + */ + + it( + "FindPackage- in npm", + async () => { + const p = await extensionManager.findPackage("autorest"); + expect(p.name).toEqual("autorest"); + }, + TEST_TIMEOUT, + ); + + it( + "FindPackage- unknown package", + async () => { + await expect(async () => { + await extensionManager.findPackage("koooopasdpasdppasdpa"); + }).rejects.toThrowError(UnresolvedPackageException); + }, + TEST_TIMEOUT, + ); + + it( + "BadPackageID- garbage name", + async () => { + await expect(async () => { + await extensionManager.findPackage("LLLLl", "$DDFOIDFJIODFJ"); + }).rejects.toThrowError(InvalidPackageIdentityException); + }, + TEST_TIMEOUT, + ); + + it("View Versions", async () => { + // gets a package + const pkg = await extensionManager.findPackage("echo-cli"); + // finds out if there are more versions + expect((await pkg.allVersions).length > 5).toBe(true); + }); + + it( + "Install Extension", + async () => { + const dni = await extensionManager.findPackage("echo-cli", "1.0.8"); + const installing = extensionManager.installPackage(dni, false, 5 * 60 * 1000, (installing) => {}); + + const extension = await installing; + + expect(await extension.configuration).not.toEqual(""); + + let done = false; + + for (const each of await extensionManager.getInstalledExtensions()) { + done = true; + // make sure we have one extension installed and that it is echo-cli (for testing) + expect(each.name).toEqual("echo-cli"); + } + + expect(done).toBe(true); + }, + TEST_TIMEOUT, + ); + + it( + "Install Extension via star", + async () => { + const dni = await extensionManager.findPackage("echo-cli", "*"); + const installing = extensionManager.installPackage(dni, false, 5 * 60 * 1000, (installing) => {}); + const extension = await installing; + + expect(await extension.configuration).not.toEqual(""); + + let done = false; + + for (const each of await extensionManager.getInstalledExtensions()) { + done = true; + // make sure we have one extension installed and that it is echo-cli (for testing) + expect(each.name).toEqual("echo-cli"); + } + + expect(done).toBe(true); + }, + TEST_TIMEOUT, + ); + + it( + "Force install", + async () => { + const dni = await extensionManager.findPackage("echo-cli", "*"); + const installing = extensionManager.installPackage(dni, false, 5 * 60 * 1000, (installing) => {}); + const extension = await installing; + expect(await extension.configuration).not.toEqual(""); + + // erase the readme.md file in the installed folder (check if force works to reinstall) + await asyncio.rmFile(await extension.configurationPath); + + // reinstall with force! + const installing2 = extensionManager.installPackage(dni, true, 5 * 60 * 1000, (installing) => {}); + const extension2 = await installing2; + + // is the file back? + expect(await extension2.configuration).not.toEqual(""); + }, + TEST_TIMEOUT, + ); + + it( + "start extension", + async () => { + const dni = await extensionManager.findPackage("none", "fearthecowboy/echo-cli"); + const extension = await extensionManager.installPackage(dni, false, 5 * 60 * 1000, (installing) => {}); + expect(await extension.configuration).not.toEqual(""); + const proc = await extension.start(); + await tasks.When(proc, "exit"); + }, + TEST_TIMEOUT, + ); +}); diff --git a/packages/libs/extension/test/test-extensions.test.ts b/packages/libs/extension/test/test-extensions.test.ts deleted file mode 100644 index a66824943a..0000000000 --- a/packages/libs/extension/test/test-extensions.test.ts +++ /dev/null @@ -1,220 +0,0 @@ -/* eslint-disable no-console */ -import assert from "assert"; -import * as fs from "fs"; -import * as os from "os"; -import * as asyncio from "@azure-tools/async-io"; -import * as tasks from "@azure-tools/tasks"; -import { ExtensionManager, InvalidPackageIdentityException, UnresolvedPackageException } from "../src"; - -const tmpFolder = fs.mkdtempSync(`${fs.mkdtempSync(`${os.tmpdir()}/test`)}/install-pkg`); - -// Those test do install pacakge and could take a little bit of time. Increasing timeout to 50s. -const TEST_TIMEOUT = 50_000; - -describe("TestExtensions", () => { - let extensionManager: ExtensionManager; - beforeEach(async () => { - extensionManager = await ExtensionManager.Create(tmpFolder); - }); - - afterEach(async () => { - try { - await extensionManager.dispose(); - try { - await tasks.Delay(500); - await asyncio.rmdir(tmpFolder); - } catch (E) { - console.error("rmdir is giving grief... [probably intermittent]"); - } - } catch (e) { - console.error("ABORTING\n"); - console.error(e); - throw "AFTER TEST ABORTED"; - } - }); - - it( - "reset", - async () => { - await extensionManager.reset(); - { - console.log("Installing Once"); - // install it once - const dni = await extensionManager.findPackage("echo-cli", "*"); - const installing = extensionManager.installPackage(dni, false, 60000, (i) => {}); - const extension = await installing; - assert.notEqual(await extension.configuration, "the configuration file isnt where it should be?"); - } - - { - console.log("Attempt Overwrite"); - // install/overwrite - const dni = await extensionManager.findPackage("echo-cli", "*"); - const installing = extensionManager.installPackage(dni, true, 60000, (i) => {}); - - // install at the same time? - const dni2 = await extensionManager.findPackage("echo-cli", "*"); - const installing2 = extensionManager.installPackage(dni2, true, 60000, (i) => {}); - - // wait for it. - const extension = await installing; - assert.notEqual(await extension.configuration, ""); - - const extension2 = await installing2; - assert.notEqual(await extension.configuration, ""); - - let done = false; - for (const each of await extensionManager.getInstalledExtensions()) { - done = true; - // make sure we have one extension installed and that it is echo-cli (for testing) - assert.equal(each.name, "echo-cli"); - } - - assert.equal(done, true, "Package is not installed"); - //await tasks.Delay(5000); - } - }, - TEST_TIMEOUT, - ); - - /* - @test async 'FindPackage- in github'() { - // github repo style - const npmpkg = await extensionManager.findPackage('npm', 'npm/npm'); - assert.equal(npmpkg.name, 'npm'); - } - */ - - it( - "FindPackage- in npm", - async () => { - const p = await extensionManager.findPackage("autorest"); - assert.equal(p.name, "autorest"); - }, - TEST_TIMEOUT, - ); - - it( - "FindPackage- unknown package", - async () => { - let threw = false; - try { - const p = await extensionManager.findPackage("koooopasdpasdppasdpa"); - } catch (e) { - if (e instanceof UnresolvedPackageException) { - threw = true; - } - } - assert.equal(threw, true, "Expected unknown package to throw UnresolvedPackageException"); - }, - TEST_TIMEOUT, - ); - - it( - "BadPackageID- garbage name", - async () => { - let threw = false; - try { - await extensionManager.findPackage("LLLLl", "$DDFOIDFJIODFJ"); - } catch (e) { - if (e instanceof InvalidPackageIdentityException) { - threw = true; - } - } - assert.equal(threw, true, "Expected bad package id to throw InvalidPackageIdentityException"); - }, - TEST_TIMEOUT, - ); - - it("View Versions", async () => { - // gets a package - const pkg = await extensionManager.findPackage("echo-cli"); - // finds out if there are more versions - assert.equal((await pkg.allVersions).length > 5, true); - }); - - it( - "Install Extension", - async () => { - const dni = await extensionManager.findPackage("echo-cli", "1.0.8"); - const installing = extensionManager.installPackage(dni, false, 5 * 60 * 1000, (installing) => {}); - - const extension = await installing; - - assert.notEqual(await extension.configuration, ""); - - let done = false; - - for (const each of await extensionManager.getInstalledExtensions()) { - done = true; - // make sure we have one extension installed and that it is echo-cli (for testing) - assert.equal(each.name, "echo-cli"); - } - - assert.equal(done, true, "Package is not installed"); - }, - TEST_TIMEOUT, - ); - - it( - "Install Extension via star", - async () => { - const dni = await extensionManager.findPackage("echo-cli", "*"); - const installing = extensionManager.installPackage(dni, false, 5 * 60 * 1000, (installing) => {}); - const extension = await installing; - - assert.notEqual(await extension.configuration, ""); - - let done = false; - - for (const each of await extensionManager.getInstalledExtensions()) { - done = true; - // make sure we have one extension installed and that it is echo-cli (for testing) - assert.equal(each.name, "echo-cli"); - } - - assert.equal(done, true, "Package is not installed"); - }, - TEST_TIMEOUT, - ); - - it( - "Force install", - async () => { - const dni = await extensionManager.findPackage("echo-cli", "*"); - const installing = extensionManager.installPackage(dni, false, 5 * 60 * 1000, (installing) => {}); - const extension = await installing; - assert.notEqual(await extension.configuration, ""); - - // erase the readme.md file in the installed folder (check if force works to reinstall) - await asyncio.rmFile(await extension.configurationPath); - - // reinstall with force! - const installing2 = extensionManager.installPackage(dni, true, 5 * 60 * 1000, (installing) => {}); - const extension2 = await installing2; - - // is the file back? - assert.notEqual(await extension2.configuration, ""); - }, - TEST_TIMEOUT, - ); - - it( - "Test Start", - async () => { - try { - const dni = await extensionManager.findPackage("none", "fearthecowboy/echo-cli"); - const installing = extensionManager.installPackage(dni, false, 5 * 60 * 1000, (installing) => {}); - const extension = await installing; - assert.notEqual(await extension.configuration, ""); - const proc = await extension.start(); - await tasks.When(proc, "exit"); - } catch (E) { - // oh well... - console.error(E); - assert(false, "FAILED DURING START TEST."); - } - }, - TEST_TIMEOUT, - ); -});