@thanaen/ado-cli 0.3.0 → 0.4.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;
@@ -57458,6 +57464,40 @@ async function cmdSmoke(config) {
57458
57464
  console.log("Pull request: none found");
57459
57465
  }
57460
57466
  }
57467
+ async function cmdStatus() {
57468
+ const localConfig = loadLocalConfig();
57469
+ const fileConfig = loadFileConfig();
57470
+ const pat = process.env.DEVOPS_PAT ?? localConfig.pat ?? fileConfig.pat;
57471
+ if (!pat) {
57472
+ console.error("Not ready: DEVOPS_PAT is not set");
57473
+ process.exit(1);
57474
+ }
57475
+ const collectionUrl = process.env.ADO_COLLECTION_URL ?? localConfig.collectionUrl ?? fileConfig.collectionUrl ?? "";
57476
+ const project = process.env.ADO_PROJECT ?? localConfig.project ?? fileConfig.project ?? "";
57477
+ const repo = process.env.ADO_REPO ?? localConfig.repo ?? fileConfig.repo ?? "";
57478
+ const hasPlaceholder = (v) => v.includes("<your-");
57479
+ if (!collectionUrl || hasPlaceholder(collectionUrl)) {
57480
+ console.error("Not ready: ADO_COLLECTION_URL is not configured");
57481
+ process.exit(1);
57482
+ }
57483
+ if (!project || hasPlaceholder(project)) {
57484
+ console.error("Not ready: ADO_PROJECT is not configured");
57485
+ process.exit(1);
57486
+ }
57487
+ if (!repo || hasPlaceholder(repo)) {
57488
+ console.error("Not ready: ADO_REPO is not configured");
57489
+ process.exit(1);
57490
+ }
57491
+ try {
57492
+ const config = getConfig();
57493
+ const witApi = await config.connection.getWorkItemTrackingApi();
57494
+ await witApi.queryByWiql({ query: "SELECT [System.Id] FROM WorkItems ORDER BY [System.ChangedDate] DESC" }, { project: config.project }, undefined, 1);
57495
+ console.log("Ready");
57496
+ } catch (err) {
57497
+ console.error(`Not ready: ${err instanceof Error ? err.message : String(err)}`);
57498
+ process.exit(1);
57499
+ }
57500
+ }
57461
57501
  async function cmdRepos(config) {
57462
57502
  const gitApi = await config.connection.getGitApi();
57463
57503
  const repos = await gitApi.getRepositories(config.project);
@@ -57765,6 +57805,24 @@ async function linkWorkItemsToPr(config, _repo, pr, workItemIds) {
57765
57805
  console.log(`Linked work item #${workItemId} to PR #${pr?.pullRequestId}`);
57766
57806
  }
57767
57807
  }
57808
+ async function addTagsToPr(config, repoName, pullRequestId, tags) {
57809
+ if (tags.length === 0)
57810
+ return;
57811
+ const gitApi = await config.connection.getGitApi();
57812
+ const repository = await gitApi.getRepository(repoName, config.project);
57813
+ const repositoryId = repository?.id;
57814
+ if (!repositoryId) {
57815
+ throw new Error(`Repository "${repoName}" not found.`);
57816
+ }
57817
+ const existing = await gitApi.getPullRequestLabels(repositoryId, pullRequestId, config.project);
57818
+ const existingTags = new Set(existing.map((label) => label.name).filter(Boolean));
57819
+ for (const tag of tags) {
57820
+ if (existingTags.has(tag))
57821
+ continue;
57822
+ await gitApi.createPullRequestLabel({ name: tag }, repositoryId, pullRequestId, config.project);
57823
+ console.log(`Added tag "${tag}" to PR #${pullRequestId}`);
57824
+ }
57825
+ }
57768
57826
  async function cmdPrCreate(config, args) {
57769
57827
  const kv = Object.fromEntries(args.map((arg) => {
57770
57828
  const [k, ...rest] = arg.split("=");
@@ -57776,8 +57834,9 @@ async function cmdPrCreate(config, args) {
57776
57834
  const description = kv.description ?? "";
57777
57835
  const repo = pickRepo(config, kv.repo);
57778
57836
  const workItemIds = parseWorkItemIds(kv["work-items"]);
57837
+ const tags = parsePrTags(kv.tags);
57779
57838
  if (!title || !source || !target) {
57780
- console.error("Usage: pr-create --title=... --source=feature/x --target=develop [--description=...] [--repo=...] [--work-items=123,456]");
57839
+ console.error("Usage: pr-create --title=... --source=feature/x --target=develop [--description=...] [--repo=...] [--work-items=123,456] [--tags=tag-a,tag-b]");
57781
57840
  process.exit(1);
57782
57841
  }
57783
57842
  const gitApi = await config.connection.getGitApi();
@@ -57792,11 +57851,14 @@ async function cmdPrCreate(config, args) {
57792
57851
  const createdPr = await gitApi.getPullRequestById(created.pullRequestId, config.project);
57793
57852
  await linkWorkItemsToPr(config, repo, createdPr, workItemIds);
57794
57853
  }
57854
+ if (created.pullRequestId && tags.length > 0) {
57855
+ await addTagsToPr(config, repo, created.pullRequestId, tags);
57856
+ }
57795
57857
  }
57796
57858
  async function cmdPrUpdate(config, idRaw, args) {
57797
57859
  const id = Number(idRaw);
57798
57860
  if (!Number.isFinite(id)) {
57799
- console.error("Usage: pr-update <id> [--title=...] [--description=...] [--repo=...] [--work-items=123,456]");
57861
+ console.error("Usage: pr-update <id> [--title=...] [--description=...] [--repo=...] [--work-items=123,456] [--tags=tag-a,tag-b]");
57800
57862
  process.exit(1);
57801
57863
  }
57802
57864
  const kv = Object.fromEntries(args.map((arg) => {
@@ -57806,12 +57868,13 @@ async function cmdPrUpdate(config, idRaw, args) {
57806
57868
  const repo = pickRepo(config, kv.repo);
57807
57869
  const body = {};
57808
57870
  const workItemIds = parseWorkItemIds(kv["work-items"]);
57871
+ const tags = parsePrTags(kv.tags);
57809
57872
  if (kv.title !== undefined)
57810
57873
  body.title = kv.title;
57811
57874
  if (kv.description !== undefined)
57812
57875
  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]");
57876
+ if (Object.keys(body).length === 0 && workItemIds.length === 0 && tags.length === 0) {
57877
+ console.error("Usage: pr-update <id> [--title=...] [--description=...] [--repo=...] [--work-items=123,456] [--tags=tag-a,tag-b]");
57815
57878
  process.exit(1);
57816
57879
  }
57817
57880
  const gitApi = await config.connection.getGitApi();
@@ -57825,6 +57888,9 @@ async function cmdPrUpdate(config, idRaw, args) {
57825
57888
  if (workItemIds.length > 0) {
57826
57889
  await linkWorkItemsToPr(config, repo, updated, workItemIds);
57827
57890
  }
57891
+ if (tags.length > 0) {
57892
+ await addTagsToPr(config, repo, id, tags);
57893
+ }
57828
57894
  }
57829
57895
  async function cmdPrCherryPick(config, args) {
57830
57896
  const usage = "Usage: pr-cherry-pick <id> --target=main [--topic=branch-name] [--repo=...]";
@@ -57957,6 +58023,7 @@ Commands:
57957
58023
  -v, --version
57958
58024
  init [--local]
57959
58025
  config
58026
+ status
57960
58027
  smoke
57961
58028
  repos
57962
58029
  branches [repo]
@@ -57967,8 +58034,8 @@ Commands:
57967
58034
  workitem-comment-update <id> <commentId> --text="..." [--file=path]
57968
58035
  prs [status] [top] [repo]
57969
58036
  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]
58037
+ pr-create --title=... --source=... --target=... [--description=...] [--repo=...] [--work-items=123,456] [--tags=tag-a,tag-b]
58038
+ pr-update <id> [--title=...] [--description=...] [--repo=...] [--work-items=123,456] [--tags=tag-a,tag-b]
57972
58039
  pr-cherry-pick <id> --target=... [--topic=branch-name] [--repo=...]
57973
58040
  pr-approve <id> [repo]
57974
58041
  pr-autocomplete <id> [repo]
@@ -57998,6 +58065,10 @@ async function main() {
57998
58065
  cmdConfig();
57999
58066
  return;
58000
58067
  }
58068
+ if (command === "status") {
58069
+ await cmdStatus();
58070
+ return;
58071
+ }
58001
58072
  const config = getConfig();
58002
58073
  switch (command) {
58003
58074
  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.4.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"`