@thanaen/ado-cli 0.3.0 → 0.5.0

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.
package/README.md CHANGED
@@ -72,8 +72,8 @@ ado smoke
72
72
  - `workitem-comment-update <id> <commentId> --text="..." [--file=path]`
73
73
  - `prs [status] [top] [repo]`
74
74
  - `pr-get <id> [repo]`
75
- - `pr-create --title=... --source=... --target=... [--description=...] [--repo=...] [--work-items=123,456]`
76
- - `pr-update <id> [--title=...] [--description=...] [--repo=...] [--work-items=123,456]`
75
+ - `pr-create --title=... --source=... --target=... [--description=...] [--repo=...] [--work-items=123,456] [--tags=tag-a,tag-b]`
76
+ - `pr-update <id> [--title=...] [--description=...] [--repo=...] [--work-items=123,456] [--tags=tag-a,tag-b]`
77
77
  - `pr-cherry-pick <id> --target=... [--topic=branch-name] [--repo=...]`
78
78
  - `pr-approve <id> [repo]`
79
79
  - `pr-autocomplete <id> [repo]`
package/dist/cli.js CHANGED
@@ -57273,6 +57273,12 @@ function parseWorkItemIds(rawValue) {
57273
57273
  const ids = rawValue.split(",").map((part) => Number(part.trim())).filter((id) => Number.isFinite(id) && id > 0);
57274
57274
  return [...new Set(ids)];
57275
57275
  }
57276
+ function parsePrTags(rawValue) {
57277
+ if (!rawValue)
57278
+ return [];
57279
+ const tags = rawValue.split(",").map((part) => part.trim()).filter((tag) => tag.length > 0);
57280
+ return [...new Set(tags)];
57281
+ }
57276
57282
  function buildPullRequestArtifactUrl(pr) {
57277
57283
  const projectId = pr?.repository?.project?.id;
57278
57284
  const repoId = pr?.repository?.id;
@@ -57406,6 +57412,7 @@ var import_GitInterfaces = __toESM(require_GitInterfaces(), 1);
57406
57412
  var import_WorkItemTrackingInterfaces = __toESM(require_WorkItemTrackingInterfaces(), 1);
57407
57413
  var import_BuildInterfaces = __toESM(require_BuildInterfaces(), 1);
57408
57414
  var import_VSSInterfaces = __toESM(require_VSSInterfaces(), 1);
57415
+ process.removeAllListeners("warning");
57409
57416
  function pickRepo(config, value) {
57410
57417
  return value || config.repo;
57411
57418
  }
@@ -57458,6 +57465,40 @@ async function cmdSmoke(config) {
57458
57465
  console.log("Pull request: none found");
57459
57466
  }
57460
57467
  }
57468
+ async function cmdStatus() {
57469
+ const localConfig = loadLocalConfig();
57470
+ const fileConfig = loadFileConfig();
57471
+ const pat = process.env.DEVOPS_PAT ?? localConfig.pat ?? fileConfig.pat;
57472
+ if (!pat) {
57473
+ console.error("Not ready: DEVOPS_PAT is not set");
57474
+ process.exit(1);
57475
+ }
57476
+ const collectionUrl = process.env.ADO_COLLECTION_URL ?? localConfig.collectionUrl ?? fileConfig.collectionUrl ?? "";
57477
+ const project = process.env.ADO_PROJECT ?? localConfig.project ?? fileConfig.project ?? "";
57478
+ const repo = process.env.ADO_REPO ?? localConfig.repo ?? fileConfig.repo ?? "";
57479
+ const hasPlaceholder = (v) => v.includes("<your-");
57480
+ if (!collectionUrl || hasPlaceholder(collectionUrl)) {
57481
+ console.error("Not ready: ADO_COLLECTION_URL is not configured");
57482
+ process.exit(1);
57483
+ }
57484
+ if (!project || hasPlaceholder(project)) {
57485
+ console.error("Not ready: ADO_PROJECT is not configured");
57486
+ process.exit(1);
57487
+ }
57488
+ if (!repo || hasPlaceholder(repo)) {
57489
+ console.error("Not ready: ADO_REPO is not configured");
57490
+ process.exit(1);
57491
+ }
57492
+ try {
57493
+ const config = getConfig();
57494
+ const witApi = await config.connection.getWorkItemTrackingApi();
57495
+ await witApi.queryByWiql({ query: "SELECT [System.Id] FROM WorkItems ORDER BY [System.ChangedDate] DESC" }, { project: config.project }, undefined, 1);
57496
+ console.log("Ready");
57497
+ } catch (err) {
57498
+ console.error(`Not ready: ${err instanceof Error ? err.message : String(err)}`);
57499
+ process.exit(1);
57500
+ }
57501
+ }
57461
57502
  async function cmdRepos(config) {
57462
57503
  const gitApi = await config.connection.getGitApi();
57463
57504
  const repos = await gitApi.getRepositories(config.project);
@@ -57765,6 +57806,24 @@ async function linkWorkItemsToPr(config, _repo, pr, workItemIds) {
57765
57806
  console.log(`Linked work item #${workItemId} to PR #${pr?.pullRequestId}`);
57766
57807
  }
57767
57808
  }
57809
+ async function addTagsToPr(config, repoName, pullRequestId, tags) {
57810
+ if (tags.length === 0)
57811
+ return;
57812
+ const gitApi = await config.connection.getGitApi();
57813
+ const repository = await gitApi.getRepository(repoName, config.project);
57814
+ const repositoryId = repository?.id;
57815
+ if (!repositoryId) {
57816
+ throw new Error(`Repository "${repoName}" not found.`);
57817
+ }
57818
+ const existing = await gitApi.getPullRequestLabels(repositoryId, pullRequestId, config.project);
57819
+ const existingTags = new Set(existing.map((label) => label.name).filter(Boolean));
57820
+ for (const tag of tags) {
57821
+ if (existingTags.has(tag))
57822
+ continue;
57823
+ await gitApi.createPullRequestLabel({ name: tag }, repositoryId, pullRequestId, config.project);
57824
+ console.log(`Added tag "${tag}" to PR #${pullRequestId}`);
57825
+ }
57826
+ }
57768
57827
  async function cmdPrCreate(config, args) {
57769
57828
  const kv = Object.fromEntries(args.map((arg) => {
57770
57829
  const [k, ...rest] = arg.split("=");
@@ -57776,8 +57835,9 @@ async function cmdPrCreate(config, args) {
57776
57835
  const description = kv.description ?? "";
57777
57836
  const repo = pickRepo(config, kv.repo);
57778
57837
  const workItemIds = parseWorkItemIds(kv["work-items"]);
57838
+ const tags = parsePrTags(kv.tags);
57779
57839
  if (!title || !source || !target) {
57780
- console.error("Usage: pr-create --title=... --source=feature/x --target=develop [--description=...] [--repo=...] [--work-items=123,456]");
57840
+ console.error("Usage: pr-create --title=... --source=feature/x --target=develop [--description=...] [--repo=...] [--work-items=123,456] [--tags=tag-a,tag-b]");
57781
57841
  process.exit(1);
57782
57842
  }
57783
57843
  const gitApi = await config.connection.getGitApi();
@@ -57792,11 +57852,14 @@ async function cmdPrCreate(config, args) {
57792
57852
  const createdPr = await gitApi.getPullRequestById(created.pullRequestId, config.project);
57793
57853
  await linkWorkItemsToPr(config, repo, createdPr, workItemIds);
57794
57854
  }
57855
+ if (created.pullRequestId && tags.length > 0) {
57856
+ await addTagsToPr(config, repo, created.pullRequestId, tags);
57857
+ }
57795
57858
  }
57796
57859
  async function cmdPrUpdate(config, idRaw, args) {
57797
57860
  const id = Number(idRaw);
57798
57861
  if (!Number.isFinite(id)) {
57799
- console.error("Usage: pr-update <id> [--title=...] [--description=...] [--repo=...] [--work-items=123,456]");
57862
+ console.error("Usage: pr-update <id> [--title=...] [--description=...] [--repo=...] [--work-items=123,456] [--tags=tag-a,tag-b]");
57800
57863
  process.exit(1);
57801
57864
  }
57802
57865
  const kv = Object.fromEntries(args.map((arg) => {
@@ -57806,12 +57869,13 @@ async function cmdPrUpdate(config, idRaw, args) {
57806
57869
  const repo = pickRepo(config, kv.repo);
57807
57870
  const body = {};
57808
57871
  const workItemIds = parseWorkItemIds(kv["work-items"]);
57872
+ const tags = parsePrTags(kv.tags);
57809
57873
  if (kv.title !== undefined)
57810
57874
  body.title = kv.title;
57811
57875
  if (kv.description !== undefined)
57812
57876
  body.description = kv.description;
57813
- if (Object.keys(body).length === 0 && workItemIds.length === 0) {
57814
- console.error("Usage: pr-update <id> [--title=...] [--description=...] [--repo=...] [--work-items=123,456]");
57877
+ if (Object.keys(body).length === 0 && workItemIds.length === 0 && tags.length === 0) {
57878
+ console.error("Usage: pr-update <id> [--title=...] [--description=...] [--repo=...] [--work-items=123,456] [--tags=tag-a,tag-b]");
57815
57879
  process.exit(1);
57816
57880
  }
57817
57881
  const gitApi = await config.connection.getGitApi();
@@ -57825,6 +57889,9 @@ async function cmdPrUpdate(config, idRaw, args) {
57825
57889
  if (workItemIds.length > 0) {
57826
57890
  await linkWorkItemsToPr(config, repo, updated, workItemIds);
57827
57891
  }
57892
+ if (tags.length > 0) {
57893
+ await addTagsToPr(config, repo, id, tags);
57894
+ }
57828
57895
  }
57829
57896
  async function cmdPrCherryPick(config, args) {
57830
57897
  const usage = "Usage: pr-cherry-pick <id> --target=main [--topic=branch-name] [--repo=...]";
@@ -57957,6 +58024,7 @@ Commands:
57957
58024
  -v, --version
57958
58025
  init [--local]
57959
58026
  config
58027
+ status
57960
58028
  smoke
57961
58029
  repos
57962
58030
  branches [repo]
@@ -57967,8 +58035,8 @@ Commands:
57967
58035
  workitem-comment-update <id> <commentId> --text="..." [--file=path]
57968
58036
  prs [status] [top] [repo]
57969
58037
  pr-get <id> [repo]
57970
- pr-create --title=... --source=... --target=... [--description=...] [--repo=...] [--work-items=123,456]
57971
- pr-update <id> [--title=...] [--description=...] [--repo=...] [--work-items=123,456]
58038
+ pr-create --title=... --source=... --target=... [--description=...] [--repo=...] [--work-items=123,456] [--tags=tag-a,tag-b]
58039
+ pr-update <id> [--title=...] [--description=...] [--repo=...] [--work-items=123,456] [--tags=tag-a,tag-b]
57972
58040
  pr-cherry-pick <id> --target=... [--topic=branch-name] [--repo=...]
57973
58041
  pr-approve <id> [repo]
57974
58042
  pr-autocomplete <id> [repo]
@@ -57998,6 +58066,10 @@ async function main() {
57998
58066
  cmdConfig();
57999
58067
  return;
58000
58068
  }
58069
+ if (command === "status") {
58070
+ await cmdStatus();
58071
+ return;
58072
+ }
58001
58073
  const config = getConfig();
58002
58074
  switch (command) {
58003
58075
  case "smoke":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thanaen/ado-cli",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Lightweight Azure DevOps CLI for repos, work items, pull requests, and builds",
5
5
  "repository": {
6
6
  "type": "git",
@@ -9,13 +9,8 @@ Use the `ado` CLI instead of ad-hoc curl commands.
9
9
 
10
10
  ## Preflight
11
11
 
12
- 1. Verify auth is available:
13
- - `test -n "$DEVOPS_PAT" && echo OK || echo MISSING`
14
- 2. Verify connectivity:
15
- - `ado smoke`
16
- - If needed, run an independent host check (outside the CLI) to distinguish network issues from ADO config issues.
17
-
18
- If auth is missing, stop and ask for `DEVOPS_PAT`.
12
+ Run `ado status` before any other command. It checks token, configuration, and connectivity in one step.
13
+ If it reports an issue, guide the user to run `ado init` (or `ado init --local` for per-project setup) to configure the CLI.
19
14
 
20
15
  ## Core commands
21
16
 
@@ -33,8 +28,8 @@ If auth is missing, stop and ask for `DEVOPS_PAT`.
33
28
  - Update an existing comment on a work item: `ado workitem-comment-update <id> <commentId> --text="..."`
34
29
  - List PRs: `ado prs active 10 "MyRepo"`
35
30
  - Get PR: `ado pr-get <id> "MyRepo"`
36
- - Create PR: `ado pr-create --title="..." --source="feature/x" --target="develop" --description="..." --repo="MyRepo" --work-items=123,456`
37
- - Update PR: `ado pr-update <id> --title="..." --description="..." --repo="MyRepo" --work-items=123,456`
31
+ - Create PR: `ado pr-create --title="..." --source="feature/x" --target="develop" --description="..." --repo="MyRepo" --work-items=123,456 --tags=backend,release-1`
32
+ - Update PR: `ado pr-update <id> --title="..." --description="..." --repo="MyRepo" --work-items=123,456 --tags=backend,release-1`
38
33
  - Cherry-pick PR onto another branch: `ado pr-cherry-pick <id> --target="main" --topic="cherry-pick-branch" --repo="MyRepo"`
39
34
  - Approve PR: `ado pr-approve <id> "MyRepo"`
40
35
  - Enable auto-complete: `ado pr-autocomplete <id> "MyRepo"`