azdo-cli 0.10.0-develop.317 → 0.10.0-develop.348

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 (3) hide show
  1. package/README.md +5 -2
  2. package/dist/index.js +55 -19
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -16,7 +16,7 @@ Azure DevOps CLI focused on work item read/write workflows.
16
16
  - Check branch pull request status, open PRs to `develop`, list PR comment threads for any PR (`--pr-number`), and resolve/reopen threads from the CLI (`pr`)
17
17
  - Persist org/project/default fields in local config (`config`)
18
18
  - List all fields of a work item (`list-fields`)
19
- - Store a PAT per Azure DevOps organization in the OS credential store via `azdo auth` (or use `AZDO_PAT`). Inspect with `azdo auth status`, remove with `azdo auth logout`. See [docs/authentication.md](docs/authentication.md).
19
+ - Authenticate per Azure DevOps organization with `azdo auth login` OAuth (Microsoft Entra) by default, or a Personal Access Token via `--use-pat` (or the `AZDO_PAT` env var). Credentials are stored in the OS credential store. Inspect with `azdo auth status`, remove with `azdo auth logout`. See [docs/authentication.md](docs/authentication.md).
20
20
 
21
21
  ## Installation
22
22
 
@@ -27,6 +27,9 @@ npm install -g azdo-cli
27
27
  ## Quick Start
28
28
 
29
29
  ```bash
30
+ # Sign in to an organization (OAuth by default; add --use-pat for a PAT)
31
+ azdo auth login --org myorg
32
+
30
33
  # Configure defaults once
31
34
  azdo config set org myorg
32
35
  azdo config set project myproject
@@ -56,7 +59,7 @@ azdo pr comment-reopen 17 --pr-number 64
56
59
 
57
60
  | Topic | File |
58
61
  |-------|------|
59
- | Authentication & PAT storage | [docs/authentication.md](docs/authentication.md) |
62
+ | Authentication (OAuth & PAT) | [docs/authentication.md](docs/authentication.md) |
60
63
  | Linux credential store setup | [docs/linux-credential-store.md](docs/linux-credential-store.md) |
61
64
  | Full command reference | [docs/commands.md](docs/commands.md) |
62
65
  | Development setup | [docs/development.md](docs/development.md) |
package/dist/index.js CHANGED
@@ -766,22 +766,42 @@ async function status() {
766
766
 
767
767
  // src/services/git-remote.ts
768
768
  import { execSync } from "child_process";
769
+
770
+ // src/services/remote-warning.ts
771
+ var WARNING = "azdo: warning: origin includes embedded credentials; consider removing them with 'git remote set-url origin <clean-url>'\n";
772
+ var warned = false;
773
+ function noticeCredentialBearingRemote() {
774
+ if (warned) {
775
+ return;
776
+ }
777
+ warned = true;
778
+ try {
779
+ process.stderr.write(WARNING);
780
+ } catch {
781
+ }
782
+ }
783
+
784
+ // src/services/git-remote.ts
769
785
  var patterns = [
770
- // HTTPS (current): https://dev.azure.com/{org}/{project}/_git/{repo}
771
- /^https?:\/\/dev\.azure\.com\/([^/]+)\/([^/]+)\/_git\/([^/]+)$/,
772
- // HTTPS (legacy + DefaultCollection): https://{org}.visualstudio.com/DefaultCollection/{project}/_git/{repo}
773
- /^https?:\/\/([^.]+)\.visualstudio\.com\/DefaultCollection\/([^/]+)\/_git\/([^/]+)$/,
774
- // HTTPS (legacy): https://{org}.visualstudio.com/{project}/_git/{repo}
775
- /^https?:\/\/([^.]+)\.visualstudio\.com\/([^/]+)\/_git\/([^/]+)$/,
776
- // SSH (current): git@ssh.dev.azure.com:v3/{org}/{project}/{repo}
777
- /^git@ssh\.dev\.azure\.com:v3\/([^/]+)\/([^/]+)\/([^/]+)$/,
778
- // SSH (legacy): {org}@vs-ssh.visualstudio.com:v3/{org}/{project}/{repo}
779
- /^[^@]+@vs-ssh\.visualstudio\.com:v3\/([^/]+)\/([^/]+)\/([^/]+)$/
786
+ // HTTPS (current): https://[user[:token]@]dev.azure.com/{org}/{project}/_git/{repo}[.git]
787
+ /^https?:\/\/(?:[^@/]+@)?dev\.azure\.com\/([^/]+)\/([^/]+)\/_git\/([^/]+?)(?:\.git)?$/,
788
+ // HTTPS (legacy + DefaultCollection): https://[user[:token]@]{org}.visualstudio.com/DefaultCollection/{project}/_git/{repo}[.git]
789
+ /^https?:\/\/(?:[^@/]+@)?([^.]+)\.visualstudio\.com\/DefaultCollection\/([^/]+)\/_git\/([^/]+?)(?:\.git)?$/,
790
+ // HTTPS (legacy): https://[user[:token]@]{org}.visualstudio.com/{project}/_git/{repo}[.git]
791
+ /^https?:\/\/(?:[^@/]+@)?([^.]+)\.visualstudio\.com\/([^/]+)\/_git\/([^/]+?)(?:\.git)?$/,
792
+ // SSH (current): git@ssh.dev.azure.com:v3/{org}/{project}/{repo}[.git]
793
+ /^git@ssh\.dev\.azure\.com:v3\/([^/]+)\/([^/]+)\/([^/]+?)(?:\.git)?$/,
794
+ // SSH (legacy): {org}@vs-ssh.visualstudio.com:v3/{org}/{project}/{repo}[.git]
795
+ /^[^@]+@vs-ssh\.visualstudio\.com:v3\/([^/]+)\/([^/]+)\/([^/]+?)(?:\.git)?$/
780
796
  ];
797
+ var httpsUserinfo = /^https?:\/\/[^@/]+@/;
781
798
  function parseAzdoRemote(url) {
782
799
  for (const pattern of patterns) {
783
800
  const match = pattern.exec(url);
784
801
  if (match) {
802
+ if (httpsUserinfo.test(url)) {
803
+ noticeCredentialBearingRemote();
804
+ }
785
805
  const project = match[2];
786
806
  if (/^DefaultCollection$/i.test(project)) {
787
807
  return { org: match[1], project: "" };
@@ -808,6 +828,9 @@ function parseRepoName(url) {
808
828
  for (const pattern of patterns) {
809
829
  const match = pattern.exec(url);
810
830
  if (match) {
831
+ if (httpsUserinfo.test(url)) {
832
+ noticeCredentialBearingRemote();
833
+ }
811
834
  return match[3];
812
835
  }
813
836
  }
@@ -2624,6 +2647,21 @@ function parsePositivePrNumber(raw) {
2624
2647
  const n = Number.parseInt(raw, 10);
2625
2648
  return Number.isFinite(n) && n > 0 ? n : null;
2626
2649
  }
2650
+ var PR_NUMBER_HELP = "target the pull request with this numeric id, instead of the current branch's PR. When omitted, the CLI auto-detects the pull request whose source branch equals refs/heads/<current branch> in the Azure DevOps repository identified by the origin remote; if zero or more than one open PR matches, the command fails with a message naming the searched branch.";
2651
+ function configureUnwrappedHelp(command) {
2652
+ return command.configureHelp({ helpWidth: 1e3 });
2653
+ }
2654
+ function autoDetectZeroMatch(branch) {
2655
+ return `No open pull request matches branch ${branch}. Pass --pr-number to target a specific PR, or push the branch and open a pull request.`;
2656
+ }
2657
+ function autoDetectMultiMatch(branch, ids) {
2658
+ return `Multiple open pull requests match branch ${branch}: ${ids.map((id) => `#${id}`).join(", ")}. Re-run with --pr-number to choose.`;
2659
+ }
2660
+ function writeContractError(line) {
2661
+ process.stderr.write(`${line}
2662
+ `);
2663
+ process.exitCode = 1;
2664
+ }
2627
2665
  function formatBranchName(refName) {
2628
2666
  return refName.startsWith("refs/heads/") ? refName.slice("refs/heads/".length) : refName;
2629
2667
  }
@@ -2799,7 +2837,7 @@ ${result.pullRequest.url ?? "\u2014"}
2799
2837
  }
2800
2838
  function createPrCommentsCommand() {
2801
2839
  const command = new Command12("comments");
2802
- command.description("List pull request comment threads for the current branch").option("--org <org>", "Azure DevOps organization").option("--project <project>", "Azure DevOps project").option("--pr-number <N>", "target the pull request with this numeric id, instead of the current branch's PR").option("--hide-resolved", "hide threads whose status is resolved / won't fix / closed / by design").option("--json", "output JSON").action(async (options) => {
2840
+ configureUnwrappedHelp(command).description("List pull request comment threads for the current branch").option("--org <org>", "Azure DevOps organization").option("--project <project>", "Azure DevOps project").option("--pr-number <N>", PR_NUMBER_HELP).option("--hide-resolved", "hide threads whose status is resolved / won't fix / closed / by design").option("--json", "output JSON").action(async (options) => {
2803
2841
  validateOrgProjectPair(options);
2804
2842
  let context;
2805
2843
  let explicitPrId = null;
@@ -2831,12 +2869,11 @@ function createPrCommentsCommand() {
2831
2869
  status: "active"
2832
2870
  });
2833
2871
  if (pullRequests.length === 0) {
2834
- writeError(`No active pull request found for branch ${resolved.branch}.`);
2872
+ writeContractError(autoDetectZeroMatch(resolved.branch));
2835
2873
  return;
2836
2874
  }
2837
2875
  if (pullRequests.length > 1) {
2838
- const ids = pullRequests.map((pr) => `#${pr.id}`).join(", ");
2839
- writeError(`Multiple active pull requests found for branch ${resolved.branch}: ${ids}. Use pr status to review them.`);
2876
+ writeContractError(autoDetectMultiMatch(resolved.branch, pullRequests.map((pr) => pr.id)));
2840
2877
  return;
2841
2878
  }
2842
2879
  pullRequest = pullRequests[0];
@@ -2900,12 +2937,11 @@ async function resolveThreadTarget(threadIdRaw, options) {
2900
2937
  status: "active"
2901
2938
  });
2902
2939
  if (pullRequests.length === 0) {
2903
- writeError(`No active pull request found for branch ${resolved.branch}.`);
2940
+ writeContractError(autoDetectZeroMatch(resolved.branch));
2904
2941
  return null;
2905
2942
  }
2906
2943
  if (pullRequests.length > 1) {
2907
- const ids = pullRequests.map((pr) => `#${pr.id}`).join(", ");
2908
- writeError(`Multiple active pull requests found for branch ${resolved.branch}: ${ids}. Use pr status to review them.`);
2944
+ writeContractError(autoDetectMultiMatch(resolved.branch, pullRequests.map((pr) => pr.id)));
2909
2945
  return null;
2910
2946
  }
2911
2947
  pullRequest = pullRequests[0];
@@ -2973,14 +3009,14 @@ async function runThreadStateChange(threadIdRaw, options, direction) {
2973
3009
  }
2974
3010
  function createPrCommentResolveCommand() {
2975
3011
  const command = new Command12("comment-resolve");
2976
- command.description("Mark a pull request comment thread as resolved").argument("<threadId>", "numeric id of the thread to resolve").option("--org <org>", "Azure DevOps organization").option("--project <project>", "Azure DevOps project").option("--pr-number <N>", "target the pull request with this numeric id, instead of the current branch's PR").option("--json", "output JSON").action(async (threadIdRaw, options) => {
3012
+ configureUnwrappedHelp(command).description("Mark a pull request comment thread as resolved").argument("<threadId>", "numeric id of the thread to resolve").option("--org <org>", "Azure DevOps organization").option("--project <project>", "Azure DevOps project").option("--pr-number <N>", PR_NUMBER_HELP).option("--json", "output JSON").action(async (threadIdRaw, options) => {
2977
3013
  await runThreadStateChange(threadIdRaw, options, "resolve");
2978
3014
  });
2979
3015
  return command;
2980
3016
  }
2981
3017
  function createPrCommentReopenCommand() {
2982
3018
  const command = new Command12("comment-reopen");
2983
- command.description("Reopen (set to active) a previously resolved pull request comment thread").argument("<threadId>", "numeric id of the thread to reopen").option("--org <org>", "Azure DevOps organization").option("--project <project>", "Azure DevOps project").option("--pr-number <N>", "target the pull request with this numeric id, instead of the current branch's PR").option("--json", "output JSON").action(async (threadIdRaw, options) => {
3019
+ configureUnwrappedHelp(command).description("Reopen (set to active) a previously resolved pull request comment thread").argument("<threadId>", "numeric id of the thread to reopen").option("--org <org>", "Azure DevOps organization").option("--project <project>", "Azure DevOps project").option("--pr-number <N>", PR_NUMBER_HELP).option("--json", "output JSON").action(async (threadIdRaw, options) => {
2984
3020
  await runThreadStateChange(threadIdRaw, options, "reopen");
2985
3021
  });
2986
3022
  return command;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "azdo-cli",
3
- "version": "0.10.0-develop.317",
3
+ "version": "0.10.0-develop.348",
4
4
  "description": "Azure DevOps CLI tool",
5
5
  "type": "module",
6
6
  "bin": {