azdo-cli 0.5.0-017-pr-comments-threads.244 → 0.5.0-017-pr-comments-threads.246
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/dist/index.js +143 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2396,6 +2396,14 @@ function mapThread(thread) {
|
|
|
2396
2396
|
comments
|
|
2397
2397
|
};
|
|
2398
2398
|
}
|
|
2399
|
+
function toActiveCommentThread(thread) {
|
|
2400
|
+
return {
|
|
2401
|
+
id: thread.id,
|
|
2402
|
+
status: thread.status,
|
|
2403
|
+
threadContext: thread.threadContext?.filePath ?? null,
|
|
2404
|
+
comments: thread.comments.map(mapComment).filter((comment) => comment !== null)
|
|
2405
|
+
};
|
|
2406
|
+
}
|
|
2399
2407
|
var RESOLVED_THREAD_STATUSES = /* @__PURE__ */ new Set(["fixed", "wontFix", "closed", "byDesign"]);
|
|
2400
2408
|
function isThreadResolved(status) {
|
|
2401
2409
|
return RESOLVED_THREAD_STATUSES.has(status);
|
|
@@ -2406,6 +2414,22 @@ async function readJsonResponse(response) {
|
|
|
2406
2414
|
}
|
|
2407
2415
|
return response.json();
|
|
2408
2416
|
}
|
|
2417
|
+
async function patchThreadStatus(context, repo, pat, prId, threadId, status) {
|
|
2418
|
+
const url = new URL(
|
|
2419
|
+
`https://dev.azure.com/${encodeURIComponent(context.org)}/${encodeURIComponent(context.project)}/_apis/git/repositories/${encodeURIComponent(repo)}/pullRequests/${prId}/threads/${threadId}`
|
|
2420
|
+
);
|
|
2421
|
+
url.searchParams.set("api-version", "7.1");
|
|
2422
|
+
const response = await fetchWithErrors(url.toString(), {
|
|
2423
|
+
method: "PATCH",
|
|
2424
|
+
headers: {
|
|
2425
|
+
...authHeaders(pat),
|
|
2426
|
+
"Content-Type": "application/json"
|
|
2427
|
+
},
|
|
2428
|
+
body: JSON.stringify({ status })
|
|
2429
|
+
});
|
|
2430
|
+
const data = await readJsonResponse(response);
|
|
2431
|
+
return toActiveCommentThread(data);
|
|
2432
|
+
}
|
|
2409
2433
|
async function getPullRequestById(context, repo, pat, prId) {
|
|
2410
2434
|
const url = new URL(
|
|
2411
2435
|
`https://dev.azure.com/${encodeURIComponent(context.org)}/${encodeURIComponent(context.project)}/_apis/git/repositories/${encodeURIComponent(repo)}/pullRequests/${prId}`
|
|
@@ -2739,12 +2763,131 @@ function createPrCommentsCommand() {
|
|
|
2739
2763
|
});
|
|
2740
2764
|
return command;
|
|
2741
2765
|
}
|
|
2766
|
+
async function resolveThreadTarget(threadIdRaw, options) {
|
|
2767
|
+
validateOrgProjectPair(options);
|
|
2768
|
+
const threadId = parsePositivePrNumber(threadIdRaw);
|
|
2769
|
+
if (threadId === null) {
|
|
2770
|
+
writeError(`Invalid thread id "${threadIdRaw}"; expected a positive integer.`);
|
|
2771
|
+
return null;
|
|
2772
|
+
}
|
|
2773
|
+
let explicitPrId = null;
|
|
2774
|
+
if (options.prNumber !== void 0) {
|
|
2775
|
+
explicitPrId = parsePositivePrNumber(options.prNumber);
|
|
2776
|
+
if (explicitPrId === null) {
|
|
2777
|
+
writeError(`Invalid --pr-number "${options.prNumber}"; expected a positive integer.`);
|
|
2778
|
+
return null;
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
const resolved = await resolvePrCommandContext(options, { requireBranch: explicitPrId === null });
|
|
2782
|
+
let pullRequest;
|
|
2783
|
+
if (explicitPrId !== null) {
|
|
2784
|
+
try {
|
|
2785
|
+
pullRequest = await getPullRequestById(resolved.context, resolved.repo, resolved.pat, explicitPrId);
|
|
2786
|
+
} catch (err) {
|
|
2787
|
+
if (err instanceof Error && err.message.startsWith("NOT_FOUND")) {
|
|
2788
|
+
writeError(`Pull request #${explicitPrId} not found in ${resolved.context.org}/${resolved.context.project}/${resolved.repo}.`);
|
|
2789
|
+
return null;
|
|
2790
|
+
}
|
|
2791
|
+
throw err;
|
|
2792
|
+
}
|
|
2793
|
+
} else {
|
|
2794
|
+
const pullRequests = await listPullRequests(resolved.context, resolved.repo, resolved.pat, resolved.branch, {
|
|
2795
|
+
status: "active"
|
|
2796
|
+
});
|
|
2797
|
+
if (pullRequests.length === 0) {
|
|
2798
|
+
writeError(`No active pull request found for branch ${resolved.branch}.`);
|
|
2799
|
+
return null;
|
|
2800
|
+
}
|
|
2801
|
+
if (pullRequests.length > 1) {
|
|
2802
|
+
const ids = pullRequests.map((pr) => `#${pr.id}`).join(", ");
|
|
2803
|
+
writeError(`Multiple active pull requests found for branch ${resolved.branch}: ${ids}. Use pr status to review them.`);
|
|
2804
|
+
return null;
|
|
2805
|
+
}
|
|
2806
|
+
pullRequest = pullRequests[0];
|
|
2807
|
+
}
|
|
2808
|
+
return { context: resolved.context, repo: resolved.repo, pat: resolved.pat, pullRequest, threadId };
|
|
2809
|
+
}
|
|
2810
|
+
async function runThreadStateChange(threadIdRaw, options, direction) {
|
|
2811
|
+
let context;
|
|
2812
|
+
try {
|
|
2813
|
+
const target = await resolveThreadTarget(threadIdRaw, options);
|
|
2814
|
+
if (target === null) {
|
|
2815
|
+
return;
|
|
2816
|
+
}
|
|
2817
|
+
context = target.context;
|
|
2818
|
+
const threads = await getPullRequestThreads(target.context, target.repo, target.pat, target.pullRequest.id);
|
|
2819
|
+
const thread = threads.find((t) => t.id === target.threadId);
|
|
2820
|
+
if (!thread) {
|
|
2821
|
+
writeError(`Thread #${target.threadId} not found on pull request #${target.pullRequest.id}.`);
|
|
2822
|
+
return;
|
|
2823
|
+
}
|
|
2824
|
+
const alreadyInTargetState = direction === "resolve" ? isThreadResolved(thread.status) : !isThreadResolved(thread.status);
|
|
2825
|
+
const targetStatus = direction === "resolve" ? "fixed" : "active";
|
|
2826
|
+
if (alreadyInTargetState) {
|
|
2827
|
+
const humanLabel = direction === "resolve" ? "resolved" : "active";
|
|
2828
|
+
const noopResult = {
|
|
2829
|
+
pullRequestId: target.pullRequest.id,
|
|
2830
|
+
threadId: target.threadId,
|
|
2831
|
+
status: targetStatus,
|
|
2832
|
+
noop: true
|
|
2833
|
+
};
|
|
2834
|
+
if (options.json) {
|
|
2835
|
+
process.stdout.write(`${JSON.stringify(noopResult, null, 2)}
|
|
2836
|
+
`);
|
|
2837
|
+
return;
|
|
2838
|
+
}
|
|
2839
|
+
process.stdout.write(`Thread #${target.threadId} is already ${humanLabel} on pull request #${target.pullRequest.id}.
|
|
2840
|
+
`);
|
|
2841
|
+
return;
|
|
2842
|
+
}
|
|
2843
|
+
const updated = await patchThreadStatus(
|
|
2844
|
+
target.context,
|
|
2845
|
+
target.repo,
|
|
2846
|
+
target.pat,
|
|
2847
|
+
target.pullRequest.id,
|
|
2848
|
+
target.threadId,
|
|
2849
|
+
targetStatus
|
|
2850
|
+
);
|
|
2851
|
+
const result = {
|
|
2852
|
+
pullRequestId: target.pullRequest.id,
|
|
2853
|
+
threadId: target.threadId,
|
|
2854
|
+
status: targetStatus,
|
|
2855
|
+
noop: false
|
|
2856
|
+
};
|
|
2857
|
+
if (options.json) {
|
|
2858
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
2859
|
+
`);
|
|
2860
|
+
return;
|
|
2861
|
+
}
|
|
2862
|
+
const verb = direction === "resolve" ? "resolved" : "reopened";
|
|
2863
|
+
process.stdout.write(`Thread #${target.threadId} ${verb} on pull request #${target.pullRequest.id} (status: ${updated.status}).
|
|
2864
|
+
`);
|
|
2865
|
+
} catch (err) {
|
|
2866
|
+
handlePrCommandError(err, context, "write");
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
function createPrCommentResolveCommand() {
|
|
2870
|
+
const command = new Command12("comment-resolve");
|
|
2871
|
+
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) => {
|
|
2872
|
+
await runThreadStateChange(threadIdRaw, options, "resolve");
|
|
2873
|
+
});
|
|
2874
|
+
return command;
|
|
2875
|
+
}
|
|
2876
|
+
function createPrCommentReopenCommand() {
|
|
2877
|
+
const command = new Command12("comment-reopen");
|
|
2878
|
+
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) => {
|
|
2879
|
+
await runThreadStateChange(threadIdRaw, options, "reopen");
|
|
2880
|
+
});
|
|
2881
|
+
return command;
|
|
2882
|
+
}
|
|
2742
2883
|
function createPrCommand() {
|
|
2743
2884
|
const command = new Command12("pr");
|
|
2744
2885
|
command.description("Manage Azure DevOps pull requests");
|
|
2745
2886
|
command.addCommand(createPrStatusCommand());
|
|
2746
2887
|
command.addCommand(createPrOpenCommand());
|
|
2747
2888
|
command.addCommand(createPrCommentsCommand());
|
|
2889
|
+
command.addCommand(createPrCommentResolveCommand());
|
|
2890
|
+
command.addCommand(createPrCommentReopenCommand());
|
|
2748
2891
|
return command;
|
|
2749
2892
|
}
|
|
2750
2893
|
|