ado-npm-auth 0.0.3 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +9 -1
  2. package/dist/ado-npm-auth.cjs +117 -30
  3. package/lib/.tsbuildinfo +1 -1
  4. package/lib/azureauth/ado.d.ts.map +1 -1
  5. package/lib/azureauth/ado.js +10 -2
  6. package/lib/azureauth/ado.js.map +1 -1
  7. package/lib/azureauth/ado.test.d.ts +2 -0
  8. package/lib/azureauth/ado.test.d.ts.map +1 -0
  9. package/lib/azureauth/ado.test.js +119 -0
  10. package/lib/azureauth/ado.test.js.map +1 -0
  11. package/lib/azureauth/azureauth-command.d.ts +1 -0
  12. package/lib/azureauth/azureauth-command.d.ts.map +1 -1
  13. package/lib/azureauth/azureauth-command.js +4 -0
  14. package/lib/azureauth/azureauth-command.js.map +1 -1
  15. package/lib/azureauth/is-azureauth-installed.d.ts +1 -0
  16. package/lib/azureauth/is-azureauth-installed.d.ts.map +1 -1
  17. package/lib/azureauth/is-azureauth-installed.js +4 -1
  18. package/lib/azureauth/is-azureauth-installed.js.map +1 -1
  19. package/lib/azureauth/is-azureauth-installed.test.d.ts +2 -0
  20. package/lib/azureauth/is-azureauth-installed.test.d.ts.map +1 -0
  21. package/lib/azureauth/is-azureauth-installed.test.js +51 -0
  22. package/lib/azureauth/is-azureauth-installed.test.js.map +1 -0
  23. package/lib/index.d.ts +1 -0
  24. package/lib/index.js +1 -1
  25. package/lib/npmrc/get-repo-npmrc-ado-orgs.d.ts.map +1 -1
  26. package/lib/npmrc/get-repo-npmrc-ado-orgs.js +13 -3
  27. package/lib/npmrc/get-repo-npmrc-ado-orgs.js.map +1 -1
  28. package/lib/npmrc/index.d.ts +1 -0
  29. package/lib/npmrc/index.js +1 -1
  30. package/lib/utils/exec.d.ts +1 -1
  31. package/lib/utils/get-feed-without-protocol.d.ts.map +1 -1
  32. package/lib/utils/get-feed-without-protocol.js +3 -1
  33. package/lib/utils/get-feed-without-protocol.js.map +1 -1
  34. package/lib/utils/get-feed-without-protocol.test.d.ts +2 -0
  35. package/lib/utils/get-feed-without-protocol.test.d.ts.map +1 -0
  36. package/lib/utils/get-feed-without-protocol.test.js +31 -0
  37. package/lib/utils/get-feed-without-protocol.test.js.map +1 -0
  38. package/lib/utils/get-organization-from-feed-url.d.ts.map +1 -1
  39. package/lib/utils/get-organization-from-feed-url.js +64 -13
  40. package/lib/utils/get-organization-from-feed-url.js.map +1 -1
  41. package/lib/utils/get-organization-from-feed-url.test.d.ts +2 -0
  42. package/lib/utils/get-organization-from-feed-url.test.d.ts.map +1 -0
  43. package/lib/utils/get-organization-from-feed-url.test.js +19 -0
  44. package/lib/utils/get-organization-from-feed-url.test.js.map +1 -0
  45. package/lib/utils/request.d.ts +1 -1
  46. package/package.json +6 -3
package/README.md CHANGED
@@ -12,6 +12,14 @@ You can run the binary `"ado-npm-auth"` via `yarn ado-npm-auth` or `npm exec ado
12
12
 
13
13
  It will then shell out to the `azureauth` package on [npm](https://www.npmjs.com/package/azureauth), retrieve a token, and update your `~/.npmrc`.
14
14
 
15
+ ## Beware the chicken and egg problem
16
+
17
+ You may need to set the registry to the public NPM feed when running `npm exec` or `npx`.
18
+
19
+ If that's the case, set the environment variable `npm_config_registry=https://registry.npmjs.org`.
20
+
21
+ That will ensure that `npx` or `npm exec` grabs from the public NPM feed, bypassing the soon-to-be authenticated ADO feed.
22
+
15
23
  ## ado-npm-auth vs vsts-npm-auth
16
24
 
17
25
  The main difference between the two is how they function, and where they can run. The `vsts-npm-auth` tool is Windows only, and uses MSAL authentication.
@@ -32,4 +40,4 @@ One of the easiest ways to use the tool is to add it to your `"preinstall"` scri
32
40
 
33
41
  It will then perform a quick "pre-flight" check to assess if the token is valid, and generate a new one if it has expired.
34
42
 
35
- ![screenshot of tool running via preinstall](https://github.com/microsoft/ado-npm-auth/raw/main/packages/ado-npm-auth/static/preinstall.png)
43
+ ![screenshot of tool running via preinstall](https://github.com/microsoft/ado-npm-auth/raw/main/packages/ado-npm-auth/static/preinstall.png)
@@ -38004,6 +38004,14 @@ module.exports = require("fs");
38004
38004
 
38005
38005
  /***/ }),
38006
38006
 
38007
+ /***/ 1455:
38008
+ /***/ ((module) => {
38009
+
38010
+ "use strict";
38011
+ module.exports = require("node:fs/promises");
38012
+
38013
+ /***/ }),
38014
+
38007
38015
  /***/ 6760:
38008
38016
  /***/ ((module) => {
38009
38017
 
@@ -38098,7 +38106,7 @@ __webpack_require__.a(__webpack_module__, async (__webpack_handle_async_dependen
38098
38106
  /* harmony import */ var _utils_is_codespaces_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(3827);
38099
38107
  /* harmony import */ var _telemetry_index_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6000);
38100
38108
  /* harmony import */ var os__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(857);
38101
- /* harmony import */ var _npmrc_is_valid_pat_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5192);
38109
+ /* harmony import */ var _npmrc_is_valid_pat_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(7467);
38102
38110
  /* harmony import */ var _npmrc_set_npmrc_pat_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(4460);
38103
38111
 
38104
38112
 
@@ -38176,7 +38184,9 @@ __webpack_async_result__();
38176
38184
  /* harmony import */ var _npmcli_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7954);
38177
38185
  /* harmony import */ var workspace_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6892);
38178
38186
  /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6760);
38179
- /* harmony import */ var _utils_get_organization_from_feed_url_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(962);
38187
+ /* harmony import */ var node_fs_promises__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(1455);
38188
+ /* harmony import */ var _utils_get_organization_from_feed_url_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(962);
38189
+
38180
38190
 
38181
38191
 
38182
38192
 
@@ -38190,17 +38200,26 @@ const getRepoNpmrcAdoOrganizations = async () => {
38190
38200
  var _a;
38191
38201
  const workspaceRoot = (0,workspace_tools__WEBPACK_IMPORTED_MODULE_1__.getWorkspaceRoot)(process.cwd()) || "";
38192
38202
  let config;
38193
- // TODO: check for workspaces npmrc
38203
+ const npmrcPath = (0,node_path__WEBPACK_IMPORTED_MODULE_2__.join)(workspaceRoot, ".npmrc");
38204
+ try {
38205
+ await node_fs_promises__WEBPACK_IMPORTED_MODULE_3__.access(npmrcPath);
38206
+ }
38207
+ catch (error) {
38208
+ throw new Error("No project .npmrc file found");
38209
+ }
38194
38210
  try {
38195
38211
  config = new _npmcli_config__WEBPACK_IMPORTED_MODULE_0__({
38196
- npmPath: (0,node_path__WEBPACK_IMPORTED_MODULE_2__.join)(workspaceRoot, ".npmrc"),
38212
+ npmPath: npmrcPath,
38197
38213
  shorthands: {},
38198
38214
  definitions: {}, // needed so we can access random feed names
38199
38215
  });
38200
38216
  await config.load();
38201
38217
  }
38202
38218
  catch (error) {
38203
- throw new Error("No project .npmrc file found");
38219
+ if (error instanceof TypeError && error.message.includes("Invalid URL")) {
38220
+ throw new Error("Registry URL missing or invalid");
38221
+ }
38222
+ throw new Error("Error loading .npmrc");
38204
38223
  }
38205
38224
  // @npmcli/config does not have a normal way to display all keys
38206
38225
  // so we use this ugly access instead
@@ -38211,7 +38230,7 @@ const getRepoNpmrcAdoOrganizations = async () => {
38211
38230
  const feed = config.get(registry, "project");
38212
38231
  return {
38213
38232
  feed,
38214
- organization: (0,_utils_get_organization_from_feed_url_js__WEBPACK_IMPORTED_MODULE_3__/* .getOrganizationFromFeedUrl */ .N)(feed),
38233
+ organization: (0,_utils_get_organization_from_feed_url_js__WEBPACK_IMPORTED_MODULE_4__/* .getOrganizationFromFeedUrl */ .N)(feed),
38215
38234
  };
38216
38235
  });
38217
38236
  };
@@ -38219,7 +38238,7 @@ const getRepoNpmrcAdoOrganizations = async () => {
38219
38238
 
38220
38239
  /***/ }),
38221
38240
 
38222
- /***/ 5192:
38241
+ /***/ 7467:
38223
38242
  /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
38224
38243
 
38225
38244
  "use strict";
@@ -38391,8 +38410,8 @@ var get_organization_from_feed_url = __webpack_require__(962);
38391
38410
  const external_node_os_namespaceObject = require("node:os");
38392
38411
  // EXTERNAL MODULE: external "node:path"
38393
38412
  var external_node_path_ = __webpack_require__(6760);
38394
- ;// CONCATENATED MODULE: external "node:fs/promises"
38395
- const promises_namespaceObject = require("node:fs/promises");
38413
+ // EXTERNAL MODULE: external "node:fs/promises"
38414
+ var promises_ = __webpack_require__(1455);
38396
38415
  // EXTERNAL MODULE: ./lib/npmrc/get-repo-npmrc-ado-orgs.js
38397
38416
  var get_repo_npmrc_ado_orgs = __webpack_require__(2236);
38398
38417
  ;// CONCATENATED MODULE: ./lib/npmrc/is-valid-pat.js
@@ -38410,17 +38429,17 @@ const isValidPat = async () => {
38410
38429
  const removeUserNpmrcRegistries = async () => {
38411
38430
  const userNpmrc = (0,external_node_path_.join)((0,external_node_os_namespaceObject.homedir)(), ".npmrc");
38412
38431
  try {
38413
- const npmrcContent = await (0,promises_namespaceObject.readFile)(userNpmrc, "utf-8");
38432
+ const npmrcContent = await (0,promises_.readFile)(userNpmrc, "utf-8");
38414
38433
  // remove the entry for registries in the user-level .npmrc
38415
38434
  const updatedNpmrcContent = npmrcContent
38416
38435
  .split(external_node_os_namespaceObject.EOL)
38417
38436
  .filter((line) => !line.includes("registry="))
38418
38437
  .join(external_node_os_namespaceObject.EOL);
38419
- await (0,promises_namespaceObject.writeFile)(userNpmrc, updatedNpmrcContent);
38438
+ await (0,promises_.writeFile)(userNpmrc, updatedNpmrcContent);
38420
38439
  }
38421
38440
  catch (error) {
38422
38441
  // user npmrc does not exist so make an empty one
38423
- await (0,promises_namespaceObject.writeFile)(userNpmrc, "");
38442
+ await (0,promises_.writeFile)(userNpmrc, "");
38424
38443
  }
38425
38444
  };
38426
38445
  const getNpmrcFeeds = async () => {
@@ -38439,7 +38458,7 @@ const getNpmrcFeeds = async () => {
38439
38458
  };
38440
38459
  const getUserNpmrcFeeds = async () => {
38441
38460
  const userNpmrcFilepath = (0,external_node_path_.join)((0,external_node_os_namespaceObject.homedir)(), ".npmrc");
38442
- const userNpmrcContent = await (0,promises_namespaceObject.readFile)(userNpmrcFilepath, "utf-8");
38461
+ const userNpmrcContent = await (0,promises_.readFile)(userNpmrcFilepath, "utf-8");
38443
38462
  return userNpmrcContent
38444
38463
  .split(external_node_os_namespaceObject.EOL)
38445
38464
  .filter((line) => line.includes(":_password="))
@@ -38525,6 +38544,9 @@ var is_wsl = __webpack_require__(1891);
38525
38544
  ;// CONCATENATED MODULE: ./lib/azureauth/azureauth-command.js
38526
38545
 
38527
38546
  let memo = undefined;
38547
+ const clearMemo = () => {
38548
+ memo = void 0;
38549
+ };
38528
38550
  /**
38529
38551
  * Get the executable path of azureauth command
38530
38552
  * @returns { string } the string of the executable command to run azureauth
@@ -38532,6 +38554,7 @@ let memo = undefined;
38532
38554
  const npxAzureAuthCommand = [
38533
38555
  "npm",
38534
38556
  "exec",
38557
+ "--silent",
38535
38558
  "--yes",
38536
38559
  "azureauth",
38537
38560
  "--",
@@ -38549,13 +38572,16 @@ var external_child_process_ = __webpack_require__(5317);
38549
38572
 
38550
38573
 
38551
38574
  let is_azureauth_installed_memo = undefined;
38575
+ const is_azureauth_installed_clearMemo = () => {
38576
+ is_azureauth_installed_memo = void 0;
38577
+ };
38552
38578
  /**
38553
38579
  * Determine if a valid version (>=0.8.0.0) is installed
38554
38580
  * @returns { boolean } Whether a valid version of azureauth is installed
38555
38581
  */
38556
38582
  const isAzureAuthInstalled = async () => {
38557
38583
  if (is_azureauth_installed_memo === undefined) {
38558
- const command = `${azureAuthCommand()} --version`;
38584
+ const command = `${azureAuthCommand().join(" ")} --version`;
38559
38585
  try {
38560
38586
  const result = await exec(command);
38561
38587
  // version must be >=0.8.0.0
@@ -38629,13 +38655,21 @@ const adoPat = async (options) => {
38629
38655
  npm_config_registry: "https://registry.npmjs.org",
38630
38656
  },
38631
38657
  });
38658
+ if (result.stderr) {
38659
+ throw new Error(result.stderr);
38660
+ }
38632
38661
  }
38633
38662
  catch (error) {
38634
- throw new Error(`Failed to get Ado Pat from npx AzureAuth: ${error.message}. Adding the --skip-auth flag may fix this issue.`);
38663
+ throw new Error(`Failed to get Ado Pat from npx AzureAuth: ${error.message}`);
38635
38664
  }
38636
38665
  }
38637
38666
  if (options.output === "json") {
38638
- return JSON.parse(result.stdout);
38667
+ try {
38668
+ return JSON.parse(result.stdout);
38669
+ }
38670
+ catch (error) {
38671
+ throw new Error(`Failed to parse JSON output: ${result.stdout}`);
38672
+ }
38639
38673
  }
38640
38674
  return result.stdout;
38641
38675
  }
@@ -38838,7 +38872,9 @@ const logTelemetry = (inputProperties, flush, client) => {
38838
38872
  */
38839
38873
  const getFeedWithoutProtocol = (feed) => {
38840
38874
  const feedUrl = new URL(feed);
38841
- const feedWithoutProtocol = `${feedUrl.host}${feedUrl.pathname}`;
38875
+ const protocol = feedUrl.protocol; // will be something like "http:"
38876
+ const protocolLength = protocol.length + 2; // we want to strip out the protocol, colon, and double slash
38877
+ const feedWithoutProtocol = feedUrl.toString().slice(protocolLength);
38842
38878
  return feedWithoutProtocol;
38843
38879
  };
38844
38880
  //# sourceMappingURL=get-feed-without-protocol.js.map
@@ -38852,6 +38888,68 @@ const getFeedWithoutProtocol = (feed) => {
38852
38888
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
38853
38889
  /* harmony export */ N: () => (/* binding */ getOrganizationFromFeedUrl)
38854
38890
  /* harmony export */ });
38891
+ /**
38892
+ * Extracts the organization and project details from a given Azure DevOps URL.
38893
+ * The function differentiates between the "new style" URLs that use 'dev.azure.com'
38894
+ * and "old style" URLs that use a subdomain of 'visualstudio.com'.
38895
+ *
38896
+ * @param {string} url - The Azure DevOps URL from which to extract details.
38897
+ * @returns {Object} An object containing the `organization` and `project` extracted from the URL.
38898
+ * @throws {Error} Throws an error if the URL is invalid, not in the expected format,
38899
+ * or does not contain the necessary information for extraction.
38900
+ *
38901
+ * @example
38902
+ * // New style URL
38903
+ * extractAdoDetails("https://dev.azure.com/contoso/WebsiteRedesign");
38904
+ * // returns { organization: "contoso", project: "WebsiteRedesign" }
38905
+ *
38906
+ * // Old style URL
38907
+ * extractAdoDetails("https://contoso.visualstudio.com/WebsiteRedesign");
38908
+ * // returns { organization: "contoso", project: "WebsiteRedesign" }
38909
+ *
38910
+ * // Invalid URL
38911
+ * extractAdoDetails("https://invalid.url.com");
38912
+ * // throws Error
38913
+ */
38914
+ const extractAdoDetails = (url) => {
38915
+ try {
38916
+ const parsedUrl = new URL(url);
38917
+ const hostname = parsedUrl.hostname;
38918
+ const pathname = parsedUrl.pathname;
38919
+ // Check for new style URLs (dev.azure.com)
38920
+ if (hostname.endsWith("dev.azure.com")) {
38921
+ const pathSegments = pathname.split('/').filter(Boolean); // Remove empty strings from the split result
38922
+ if (pathSegments.length >= 2) {
38923
+ return {
38924
+ organization: pathSegments[0],
38925
+ project: pathSegments[1]
38926
+ };
38927
+ }
38928
+ else {
38929
+ throw new Error("Not enough segments in path for a valid organization and project extraction.");
38930
+ }
38931
+ }
38932
+ // Check for old style URLs (visualstudio.com)
38933
+ if (hostname.endsWith("visualstudio.com")) {
38934
+ const subdomain = hostname.split('.')[0];
38935
+ const pathSegments = pathname.split('/').filter(Boolean);
38936
+ if (subdomain && pathSegments.length >= 1) {
38937
+ return {
38938
+ organization: subdomain,
38939
+ project: pathSegments[0]
38940
+ };
38941
+ }
38942
+ else {
38943
+ throw new Error("Not enough segments in path or missing subdomain for a valid organization and project extraction.");
38944
+ }
38945
+ }
38946
+ // If the URL does not match expected formats
38947
+ throw new Error("URL format not recognized or does not contain enough information.");
38948
+ }
38949
+ catch (error) {
38950
+ throw new Error("Invalid URL or unsupported format");
38951
+ }
38952
+ };
38855
38953
  /**
38856
38954
  * Get the ADO Org for a npm feed
38857
38955
  * @param {string} feedUrl URL of the feed to get the ADO organization from
@@ -38860,19 +38958,8 @@ const getFeedWithoutProtocol = (feed) => {
38860
38958
  */
38861
38959
  const getOrganizationFromFeedUrl = (feedUrl, defaultOrg = "") => {
38862
38960
  try {
38863
- const url = new URL(feedUrl);
38864
- const packagingIndex = url.pathname.indexOf("/_packaging");
38865
- if (packagingIndex > 0) {
38866
- // sometimes org is included in the path as the first item in the path
38867
- // after the initial "/"
38868
- // ex. "https://pkgs.dev.azure.com/foo/bar/_packaging/baz/npm/registry/" -> "foo"
38869
- // these are project-scoped feeds
38870
- return url.pathname.split("/")[1];
38871
- }
38872
- // otherwise feed is the first item in the host
38873
- // ex. "https://foo.pkgs.visualstudio.com/_packaging/npm-mirror/npm/registry" -> "foo"
38874
- // these are org-wide feeds
38875
- return url.host.split(".")[0];
38961
+ const { organization } = extractAdoDetails(feedUrl);
38962
+ return organization;
38876
38963
  }
38877
38964
  catch (error) {
38878
38965
  return defaultOrg;