azdo-cli 0.19.0-feature-038-pr-update.708 → 0.19.0-feature-039-pr-abandon.711
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 +7 -1
- package/dist/index.js +111 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ Azure DevOps CLI focused on work item read/write workflows.
|
|
|
15
15
|
- Attach a local file to a work item, or remove a named attachment (`add-attachment`, `delete-attachment`)
|
|
16
16
|
- Read/write rich-text fields as markdown (`get-md-field`, `set-md-field`)
|
|
17
17
|
- Download images embedded in rich-text fields, optionally resized for LLM use (`get-item`/`get-md-field` `--download-images`, `--resize-images`)
|
|
18
|
-
- Check branch pull request status, open PRs to `develop` (optionally pre-filled from a repository-defined template), update an existing PR's title or description (`pr update`), list PR comment threads for any PR (`--pr-number`), resolve/reopen threads, link/unlink work items, and add/remove required or optional reviewers — all from the CLI (`pr`)
|
|
18
|
+
- Check branch pull request status, open PRs to `develop` (optionally pre-filled from a repository-defined template), update an existing PR's title or description (`pr update`), abandon or reactivate one (`pr abandon` / `pr reactivate`), list PR comment threads for any PR (`--pr-number`), resolve/reopen threads, link/unlink work items, and add/remove required or optional reviewers — all from the CLI (`pr`)
|
|
19
19
|
- Feed long PR titles, descriptions and comment bodies from a file or a pipe instead of the shell (`--title-file`, `--description-file`, `--file`; `-` reads standard input)
|
|
20
20
|
- Persist org/project/default fields in local config (`config`)
|
|
21
21
|
- List all fields of a work item (`list-fields`)
|
|
@@ -102,6 +102,12 @@ cat body.md | azdo pr edit --pr-number 64 --description-file - # "edit" is an
|
|
|
102
102
|
azdo pr update --pr-number 64 --title "A real title" --json # { pullRequestId, title, description, url, noop, updatedFields }
|
|
103
103
|
# Re-running with the values it already has writes nothing and reports "noop": true.
|
|
104
104
|
|
|
105
|
+
# Abandon a PR you opened by mistake — reversible, never prompts, nothing is deleted
|
|
106
|
+
azdo pr abandon --pr-number 64 # "close" is an alias; it does NOT complete/merge the PR
|
|
107
|
+
azdo pr reactivate --pr-number 64 # bring it back; --pr-number-less lookup searches abandoned PRs
|
|
108
|
+
azdo pr abandon --pr-number 64 --json # { pullRequestId, title, status, previousStatus, url, noop }
|
|
109
|
+
# Already abandoned? noop, exit 0, no write. Already completed? exit 1, no write.
|
|
110
|
+
|
|
105
111
|
# Link/unlink a work item, add/remove reviewers
|
|
106
112
|
azdo pr work-items link 1234 --pr-number 64
|
|
107
113
|
azdo pr work-items unlink 1234 --pr-number 64
|
package/dist/index.js
CHANGED
|
@@ -4123,6 +4123,13 @@ function autoDetectMultiMatch(branch, ids) {
|
|
|
4123
4123
|
const idList = ids.map((id) => `#${id}`).join(", ");
|
|
4124
4124
|
return `Multiple open pull requests match branch ${branch}: ${idList}. Re-run with --pr-number to choose.`;
|
|
4125
4125
|
}
|
|
4126
|
+
function abandonedAutoDetectZeroMatch(branch) {
|
|
4127
|
+
return `No abandoned pull request matches branch ${branch}. Pass --pr-number to target a specific PR.`;
|
|
4128
|
+
}
|
|
4129
|
+
function abandonedAutoDetectMultiMatch(branch, ids) {
|
|
4130
|
+
const idList = ids.map((id) => `#${id}`).join(", ");
|
|
4131
|
+
return `Multiple abandoned pull requests match branch ${branch}: ${idList}. Re-run with --pr-number to choose.`;
|
|
4132
|
+
}
|
|
4126
4133
|
function writeContractError(line) {
|
|
4127
4134
|
process.stderr.write(`${line}
|
|
4128
4135
|
`);
|
|
@@ -4655,6 +4662,98 @@ function createPrUpdateCommand() {
|
|
|
4655
4662
|
});
|
|
4656
4663
|
return command;
|
|
4657
4664
|
}
|
|
4665
|
+
var PR_STATUS_CHANGES = {
|
|
4666
|
+
abandon: {
|
|
4667
|
+
target: "abandoned",
|
|
4668
|
+
search: "active",
|
|
4669
|
+
verb: "abandoned",
|
|
4670
|
+
applied: "Abandoned",
|
|
4671
|
+
refusal: "abandoned"
|
|
4672
|
+
},
|
|
4673
|
+
reactivate: {
|
|
4674
|
+
target: "active",
|
|
4675
|
+
search: "abandoned",
|
|
4676
|
+
verb: "active",
|
|
4677
|
+
applied: "Reactivated",
|
|
4678
|
+
refusal: "reactivated"
|
|
4679
|
+
}
|
|
4680
|
+
};
|
|
4681
|
+
var COMPLETED_STATUS = "completed";
|
|
4682
|
+
async function runPrStatusChange(options, direction) {
|
|
4683
|
+
const change = PR_STATUS_CHANGES[direction];
|
|
4684
|
+
let context;
|
|
4685
|
+
try {
|
|
4686
|
+
const target = await resolvePullRequestTarget(options, { branchStatus: change.search });
|
|
4687
|
+
if (target === null) {
|
|
4688
|
+
return;
|
|
4689
|
+
}
|
|
4690
|
+
context = target.context;
|
|
4691
|
+
const current = target.pullRequest;
|
|
4692
|
+
if (current.status === COMPLETED_STATUS) {
|
|
4693
|
+
writeError(
|
|
4694
|
+
`Pull request #${current.id} is completed and cannot be ${change.refusal}. A completed pull request is final; revert it with a new pull request instead.`
|
|
4695
|
+
);
|
|
4696
|
+
return;
|
|
4697
|
+
}
|
|
4698
|
+
if (current.status === change.target) {
|
|
4699
|
+
const noopResult = {
|
|
4700
|
+
pullRequestId: current.id,
|
|
4701
|
+
title: current.title,
|
|
4702
|
+
status: current.status,
|
|
4703
|
+
previousStatus: current.status,
|
|
4704
|
+
url: current.url,
|
|
4705
|
+
noop: true
|
|
4706
|
+
};
|
|
4707
|
+
if (options.json) {
|
|
4708
|
+
process.stdout.write(`${JSON.stringify(noopResult, null, 2)}
|
|
4709
|
+
`);
|
|
4710
|
+
return;
|
|
4711
|
+
}
|
|
4712
|
+
process.stdout.write(`Pull request #${current.id} is already ${change.verb}; nothing to do.
|
|
4713
|
+
`);
|
|
4714
|
+
return;
|
|
4715
|
+
}
|
|
4716
|
+
const updated = await updatePullRequest(target.context, target.repo, target.pat, current.id, {
|
|
4717
|
+
status: change.target
|
|
4718
|
+
});
|
|
4719
|
+
const result = {
|
|
4720
|
+
pullRequestId: updated.id,
|
|
4721
|
+
title: updated.title,
|
|
4722
|
+
status: updated.status,
|
|
4723
|
+
previousStatus: current.status,
|
|
4724
|
+
url: updated.url,
|
|
4725
|
+
noop: false
|
|
4726
|
+
};
|
|
4727
|
+
if (options.json) {
|
|
4728
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
4729
|
+
`);
|
|
4730
|
+
return;
|
|
4731
|
+
}
|
|
4732
|
+
process.stdout.write(`${change.applied} pull request #${updated.id} (${updated.title}).
|
|
4733
|
+
${updated.url ?? "\u2014"}
|
|
4734
|
+
`);
|
|
4735
|
+
} catch (err) {
|
|
4736
|
+
handlePrCommandError(err, context, "write");
|
|
4737
|
+
}
|
|
4738
|
+
}
|
|
4739
|
+
function createPrAbandonCommand() {
|
|
4740
|
+
const command = new Command12("abandon");
|
|
4741
|
+
withCommonPrOptions(configureUnwrappedHelp(command)).alias("close").description(
|
|
4742
|
+
'Abandon a pull request (status: abandoned) \u2014 reversible with "azdo pr reactivate". This does NOT complete or merge the pull request; nothing is deleted and no confirmation is asked.'
|
|
4743
|
+
).option("--pr-number <N>", PR_NUMBER_HELP).option("--json", "output JSON").action(async (_options, command2) => {
|
|
4744
|
+
await runPrStatusChange(mergedPrOptions(command2), "abandon");
|
|
4745
|
+
});
|
|
4746
|
+
return command;
|
|
4747
|
+
}
|
|
4748
|
+
function createPrReactivateCommand() {
|
|
4749
|
+
const command = new Command12("reactivate");
|
|
4750
|
+
withCommonPrOptions(configureUnwrappedHelp(command)).description(
|
|
4751
|
+
"Reactivate an abandoned pull request (status: active). When --pr-number is omitted the current branch's single ABANDONED pull request is used, not the active one."
|
|
4752
|
+
).option("--pr-number <N>", PR_NUMBER_HELP).option("--json", "output JSON").action(async (_options, command2) => {
|
|
4753
|
+
await runPrStatusChange(mergedPrOptions(command2), "reactivate");
|
|
4754
|
+
});
|
|
4755
|
+
return command;
|
|
4756
|
+
}
|
|
4658
4757
|
function createPrCommentsCommand() {
|
|
4659
4758
|
const command = new Command12("comments");
|
|
4660
4759
|
withCommonPrOptions(configureUnwrappedHelp(command)).description("List pull request comment threads for the current branch").option("--pr-number <N>", PR_NUMBER_HELP).option("--hide-resolved", "hide threads whose status is resolved / won't fix / closed / by design").option("--exclude-resolved", "alias of --hide-resolved: exclude resolved / won't fix / closed / by design threads").option("--code-related-only", "show only threads anchored to a file/line; omit general discussion threads").option("--exclude-system", "omit Azure DevOps system comments (branch updates, reviewer votes, build events)").option("--max-chars <N>", "truncate each comment body to N characters (0 = no limit, the default)").option("--thread <id>", "show only the thread with this numeric id; fails when the pull request has no such thread").option("--contains <text>", "show only threads holding a comment that contains this literal, case-sensitive substring (matched before --max-chars truncates)").option("--json", "output JSON").action(async (options) => {
|
|
@@ -4770,7 +4869,7 @@ function createPrCommentsCommand() {
|
|
|
4770
4869
|
command.addCommand(createPrCommentsEditCommand());
|
|
4771
4870
|
return command;
|
|
4772
4871
|
}
|
|
4773
|
-
async function resolvePullRequestTarget(options) {
|
|
4872
|
+
async function resolvePullRequestTarget(options, { branchStatus = "active" } = {}) {
|
|
4774
4873
|
validateOrgProjectPair(options);
|
|
4775
4874
|
let explicitPrId = null;
|
|
4776
4875
|
if (options.prNumber !== void 0) {
|
|
@@ -4794,14 +4893,20 @@ async function resolvePullRequestTarget(options) {
|
|
|
4794
4893
|
}
|
|
4795
4894
|
} else {
|
|
4796
4895
|
const pullRequests = await listPullRequests(resolved.context, resolved.repo, resolved.pat, resolved.branch, {
|
|
4797
|
-
status:
|
|
4896
|
+
status: branchStatus
|
|
4798
4897
|
});
|
|
4898
|
+
const abandoned = branchStatus === "abandoned";
|
|
4799
4899
|
if (pullRequests.length === 0) {
|
|
4800
|
-
writeContractError(
|
|
4900
|
+
writeContractError(
|
|
4901
|
+
abandoned ? abandonedAutoDetectZeroMatch(resolved.branch) : autoDetectZeroMatch(resolved.branch)
|
|
4902
|
+
);
|
|
4801
4903
|
return null;
|
|
4802
4904
|
}
|
|
4803
4905
|
if (pullRequests.length > 1) {
|
|
4804
|
-
|
|
4906
|
+
const ids = pullRequests.map((pr) => pr.id);
|
|
4907
|
+
writeContractError(
|
|
4908
|
+
abandoned ? abandonedAutoDetectMultiMatch(resolved.branch, ids) : autoDetectMultiMatch(resolved.branch, ids)
|
|
4909
|
+
);
|
|
4805
4910
|
return null;
|
|
4806
4911
|
}
|
|
4807
4912
|
pullRequest = pullRequests[0];
|
|
@@ -5435,6 +5540,8 @@ function createPrCommand() {
|
|
|
5435
5540
|
command.addCommand(createPrStatusCommand());
|
|
5436
5541
|
command.addCommand(createPrOpenCommand());
|
|
5437
5542
|
command.addCommand(createPrUpdateCommand());
|
|
5543
|
+
command.addCommand(createPrAbandonCommand());
|
|
5544
|
+
command.addCommand(createPrReactivateCommand());
|
|
5438
5545
|
command.addCommand(createPrCommentsCommand());
|
|
5439
5546
|
command.addCommand(createPrCommentResolveCommand());
|
|
5440
5547
|
command.addCommand(createPrCommentReopenCommand());
|