azdo-cli 0.19.0-develop.710 → 0.19.0-develop.724
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
|
@@ -15,11 +15,11 @@ 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`)
|
|
22
|
-
- 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`. Diagnose auth problems with `azdo auth diagnose`. See [docs/authentication.md](docs/authentication.md).
|
|
22
|
+
- 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`. Diagnose auth problems with `azdo auth diagnose`. Print the credential the CLI itself would use, for API calls the CLI does not yet wrap, with `azdo auth token`. See [docs/authentication.md](docs/authentication.md).
|
|
23
23
|
- Trace all HTTP requests to a local file with `--trace <filepath>` (sensitive headers and tokens are automatically redacted).
|
|
24
24
|
|
|
25
25
|
## Installation
|
|
@@ -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
|
|
@@ -825,10 +825,18 @@ function entryFor(account) {
|
|
|
825
825
|
return wrapUnavailable(() => new Entry(SERVICE, account));
|
|
826
826
|
}
|
|
827
827
|
var legacyUnsetNoticeEmitted = false;
|
|
828
|
+
var noticesSuppressed = false;
|
|
829
|
+
function suppressCredentialStoreNotices(suppressed) {
|
|
830
|
+
noticesSuppressed = suppressed;
|
|
831
|
+
}
|
|
832
|
+
function writeNotice(message) {
|
|
833
|
+
if (noticesSuppressed) return;
|
|
834
|
+
process.stderr.write(message);
|
|
835
|
+
}
|
|
828
836
|
function emitLegacyUnsetNoticeOnce() {
|
|
829
837
|
if (legacyUnsetNoticeEmitted) return;
|
|
830
838
|
legacyUnsetNoticeEmitted = true;
|
|
831
|
-
|
|
839
|
+
writeNotice(
|
|
832
840
|
'A legacy PAT exists in the OS vault from a previous azdo-cli version, but no "org" is set in config. Run `azdo auth --org <name>` to re-store it under the per-org key, then `azdo clear-pat` to remove the legacy slot.\n'
|
|
833
841
|
);
|
|
834
842
|
}
|
|
@@ -925,7 +933,7 @@ async function maybeMigrateLegacy(targetOrg) {
|
|
|
925
933
|
backend: probeBackend(),
|
|
926
934
|
masked_pat: maskedDisplay(legacy)
|
|
927
935
|
});
|
|
928
|
-
|
|
936
|
+
writeNotice(`Migrated legacy PAT to org ${targetOrg}.
|
|
929
937
|
`);
|
|
930
938
|
return legacy;
|
|
931
939
|
}
|
|
@@ -990,7 +998,7 @@ async function deletePat(org) {
|
|
|
990
998
|
}
|
|
991
999
|
try {
|
|
992
1000
|
const { unlinkSync: unlinkSync2 } = await import("fs");
|
|
993
|
-
const { lockPath: lockPath2 } = await import("./oauth-token-refresh-
|
|
1001
|
+
const { lockPath: lockPath2 } = await import("./oauth-token-refresh-PXUQE5JE.js");
|
|
994
1002
|
unlinkSync2(lockPath2(org));
|
|
995
1003
|
} catch {
|
|
996
1004
|
}
|
|
@@ -1180,6 +1188,7 @@ function _resetInFlight() {
|
|
|
1180
1188
|
export {
|
|
1181
1189
|
CredentialStoreUnavailableError,
|
|
1182
1190
|
CredentialMissingError,
|
|
1191
|
+
CredentialRefreshError,
|
|
1183
1192
|
appendAuthAuditEvent,
|
|
1184
1193
|
readAuditEvents,
|
|
1185
1194
|
SETTINGS,
|
|
@@ -1211,6 +1220,7 @@ export {
|
|
|
1211
1220
|
refreshIfNeeded,
|
|
1212
1221
|
_resetInFlight,
|
|
1213
1222
|
probeBackend,
|
|
1223
|
+
suppressCredentialStoreNotices,
|
|
1214
1224
|
getPat,
|
|
1215
1225
|
getStoredCredential,
|
|
1216
1226
|
storePat,
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
AZDO_RESOURCE_ID,
|
|
4
4
|
CredentialMissingError,
|
|
5
|
+
CredentialRefreshError,
|
|
5
6
|
CredentialStoreUnavailableError,
|
|
6
7
|
SETTINGS,
|
|
7
8
|
appendAuthAuditEvent,
|
|
@@ -32,12 +33,13 @@ import {
|
|
|
32
33
|
setOrgScopedValue,
|
|
33
34
|
storeOAuthCredential,
|
|
34
35
|
storePat,
|
|
36
|
+
suppressCredentialStoreNotices,
|
|
35
37
|
tokenResponseToCredential,
|
|
36
38
|
unsetConfigValue,
|
|
37
39
|
unsetOrgScopedValue
|
|
38
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-K2CKMY72.js";
|
|
39
41
|
|
|
40
|
-
// src/
|
|
42
|
+
// src/program.ts
|
|
41
43
|
import { Command as Command19 } from "commander";
|
|
42
44
|
|
|
43
45
|
// src/version.ts
|
|
@@ -1165,28 +1167,28 @@ function findDotEnvPat(startDir = process.cwd()) {
|
|
|
1165
1167
|
return null;
|
|
1166
1168
|
}
|
|
1167
1169
|
async function resolveAuthCredential(org) {
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
if (stored.kind === "pat") {
|
|
1175
|
-
return { pat: stored.token, source: "credential-store", kind: "pat" };
|
|
1170
|
+
let cred;
|
|
1171
|
+
try {
|
|
1172
|
+
cred = await exportCredential(org);
|
|
1173
|
+
} catch (err) {
|
|
1174
|
+
if (err instanceof CredentialMissingError) {
|
|
1175
|
+
return null;
|
|
1176
1176
|
}
|
|
1177
|
-
|
|
1177
|
+
throw err;
|
|
1178
|
+
}
|
|
1179
|
+
if (cred.kind === "oauth") {
|
|
1178
1180
|
return {
|
|
1179
|
-
pat:
|
|
1181
|
+
pat: cred.token,
|
|
1180
1182
|
source: "credential-store",
|
|
1181
1183
|
kind: "oauth",
|
|
1182
|
-
accountId:
|
|
1184
|
+
accountId: cred.accountId
|
|
1183
1185
|
};
|
|
1184
1186
|
}
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1187
|
+
return {
|
|
1188
|
+
pat: cred.token,
|
|
1189
|
+
source: cred.source === "credential-store" ? "credential-store" : "env",
|
|
1190
|
+
kind: "pat"
|
|
1191
|
+
};
|
|
1190
1192
|
}
|
|
1191
1193
|
var lastResolvedCredential = null;
|
|
1192
1194
|
async function requireAuthCredential(org) {
|
|
@@ -1351,6 +1353,32 @@ async function status() {
|
|
|
1351
1353
|
}
|
|
1352
1354
|
return { orgs: out };
|
|
1353
1355
|
}
|
|
1356
|
+
async function exportCredential(org) {
|
|
1357
|
+
const envPat = process.env.AZDO_PAT;
|
|
1358
|
+
if (envPat && envPat.length > 0) {
|
|
1359
|
+
return { kind: "pat", token: envPat, source: "env" };
|
|
1360
|
+
}
|
|
1361
|
+
const stored = await getStoredCredential(org);
|
|
1362
|
+
if (stored === null) {
|
|
1363
|
+
const dotEnvPat = findDotEnvPat();
|
|
1364
|
+
if (dotEnvPat !== null) {
|
|
1365
|
+
return { kind: "pat", token: dotEnvPat, source: "dotenv" };
|
|
1366
|
+
}
|
|
1367
|
+
throw new CredentialMissingError(org);
|
|
1368
|
+
}
|
|
1369
|
+
if (stored.kind === "pat") {
|
|
1370
|
+
return { kind: "pat", token: stored.token, source: "credential-store" };
|
|
1371
|
+
}
|
|
1372
|
+
const fresh = await refreshIfNeeded(org, stored);
|
|
1373
|
+
return {
|
|
1374
|
+
kind: "oauth",
|
|
1375
|
+
token: fresh.accessToken,
|
|
1376
|
+
source: "credential-store",
|
|
1377
|
+
accountId: fresh.accountId,
|
|
1378
|
+
expiresAt: fresh.expiresAt,
|
|
1379
|
+
scope: fresh.scope
|
|
1380
|
+
};
|
|
1381
|
+
}
|
|
1354
1382
|
|
|
1355
1383
|
// src/services/git-remote.ts
|
|
1356
1384
|
import { execFileSync } from "child_process";
|
|
@@ -2364,6 +2392,73 @@ async function handleLogout(options, orgFromGlobal) {
|
|
|
2364
2392
|
throw err;
|
|
2365
2393
|
}
|
|
2366
2394
|
}
|
|
2395
|
+
function describeCredentialSource(cred) {
|
|
2396
|
+
switch (cred.source) {
|
|
2397
|
+
case "env":
|
|
2398
|
+
return "the AZDO_PAT environment variable";
|
|
2399
|
+
case "dotenv":
|
|
2400
|
+
return "the AZDO_PAT entry in a .env file";
|
|
2401
|
+
default:
|
|
2402
|
+
return `the OS credential store (${probeBackend()})`;
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
function describeExportedCredential(cred, org) {
|
|
2406
|
+
const source = describeCredentialSource(cred);
|
|
2407
|
+
if (cred.kind === "oauth") {
|
|
2408
|
+
const expires = new Date(cred.expiresAt * 1e3).toISOString();
|
|
2409
|
+
return `OAuth access token for org ${org} from ${source}; account ${cred.accountId}, expires ${expires}.
|
|
2410
|
+
Send it as: Authorization: Bearer <token>
|
|
2411
|
+
`;
|
|
2412
|
+
}
|
|
2413
|
+
return `PAT for org ${org} from ${source}.
|
|
2414
|
+
Send it as: Authorization: Basic base64(":<token>") (curl: -u :<token>)
|
|
2415
|
+
`;
|
|
2416
|
+
}
|
|
2417
|
+
function reportTokenFailure(err) {
|
|
2418
|
+
if (err instanceof CredentialStoreUnavailableError) {
|
|
2419
|
+
process.stderr.write(`${err.message}
|
|
2420
|
+
`);
|
|
2421
|
+
process.exitCode = 4;
|
|
2422
|
+
return;
|
|
2423
|
+
}
|
|
2424
|
+
if (err instanceof CredentialMissingError || err instanceof CredentialRefreshError) {
|
|
2425
|
+
process.stderr.write(`${err.message}
|
|
2426
|
+
`);
|
|
2427
|
+
process.exitCode = 1;
|
|
2428
|
+
return;
|
|
2429
|
+
}
|
|
2430
|
+
throw err;
|
|
2431
|
+
}
|
|
2432
|
+
async function handleToken(orgFromGlobal) {
|
|
2433
|
+
const resolved = resolveOrg({ org: orgFromGlobal });
|
|
2434
|
+
if (!resolved) {
|
|
2435
|
+
process.stderr.write(`${formatResolutionError()}
|
|
2436
|
+
`);
|
|
2437
|
+
process.exitCode = 3;
|
|
2438
|
+
return;
|
|
2439
|
+
}
|
|
2440
|
+
const org = resolved.org;
|
|
2441
|
+
const stderrIsTty = Boolean(process.stderr.isTTY);
|
|
2442
|
+
suppressCredentialStoreNotices(!stderrIsTty);
|
|
2443
|
+
let cred;
|
|
2444
|
+
try {
|
|
2445
|
+
cred = await exportCredential(org);
|
|
2446
|
+
} catch (err) {
|
|
2447
|
+
reportTokenFailure(err);
|
|
2448
|
+
return;
|
|
2449
|
+
}
|
|
2450
|
+
appendAuthAuditEvent({
|
|
2451
|
+
event: "auth.token",
|
|
2452
|
+
org,
|
|
2453
|
+
backend: probeBackend(),
|
|
2454
|
+
...cred.kind === "oauth" ? { accountId: cred.accountId } : {}
|
|
2455
|
+
});
|
|
2456
|
+
if (stderrIsTty) {
|
|
2457
|
+
process.stderr.write(describeExportedCredential(cred, org));
|
|
2458
|
+
}
|
|
2459
|
+
process.stdout.write(`${cred.token}
|
|
2460
|
+
`);
|
|
2461
|
+
}
|
|
2367
2462
|
function createAuthCommand() {
|
|
2368
2463
|
const command = new Command3("auth");
|
|
2369
2464
|
command.description(
|
|
@@ -2448,6 +2543,38 @@ Note: \`azdo auth\` (no subcommand) preserves the legacy PAT-prompt entry point;
|
|
|
2448
2543
|
const globals = logoutCmd.optsWithGlobals();
|
|
2449
2544
|
await handleLogout(options, globals.org);
|
|
2450
2545
|
});
|
|
2546
|
+
const tokenCmd = command.command("token").description(
|
|
2547
|
+
"Print the credential the CLI uses for an org \u2014 a PAT or an OAuth access token \u2014 to stdout (for scripted API calls)"
|
|
2548
|
+
).option("--org <name>", "Azure DevOps organization (defaults: git remote -> config)");
|
|
2549
|
+
tokenCmd.action(async () => {
|
|
2550
|
+
const globals = tokenCmd.optsWithGlobals();
|
|
2551
|
+
await handleToken(globals.org);
|
|
2552
|
+
});
|
|
2553
|
+
tokenCmd.addHelpText(
|
|
2554
|
+
"after",
|
|
2555
|
+
`
|
|
2556
|
+
stdout carries the token and nothing else, so it is safe to capture:
|
|
2557
|
+
|
|
2558
|
+
TOKEN=$(azdo auth token --org myorg)
|
|
2559
|
+
|
|
2560
|
+
The token is whichever credential the CLI would use itself (AZDO_PAT, then the
|
|
2561
|
+
stored credential for the org, then a .env AZDO_PAT); an expired OAuth access
|
|
2562
|
+
token is refreshed first. Treat it as a password \u2014 it carries your full access.
|
|
2563
|
+
|
|
2564
|
+
Azure DevOps needs a different header for each kind:
|
|
2565
|
+
|
|
2566
|
+
PAT curl -u :"$TOKEN" https://dev.azure.com/myorg/_apis/projects
|
|
2567
|
+
OAuth access tok curl -H "Authorization: Bearer $TOKEN" https://dev.azure.com/myorg/_apis/projects
|
|
2568
|
+
|
|
2569
|
+
When stderr is a terminal the command names the kind, source and expiry there;
|
|
2570
|
+
redirected or piped, stderr stays silent. There is deliberately no --json: use
|
|
2571
|
+
\`azdo auth status --json\` for machine-readable credential metadata, which never
|
|
2572
|
+
includes token material.
|
|
2573
|
+
|
|
2574
|
+
Exit codes: 1 no stored credential (or a rejected OAuth refresh), 3 org could
|
|
2575
|
+
not be resolved, 4 OS credential store unavailable. stdout stays empty on all.
|
|
2576
|
+
`
|
|
2577
|
+
);
|
|
2451
2578
|
const diagnoseCmd = command.command("diagnose").description("Show auth type, credential source, org, and live connectivity test result").option("--org <name>", "Azure DevOps organization (overrides context resolution)").option("--project <name>", "Azure DevOps project (optional context)").option("--json", "emit JSON instead of human-readable text", false);
|
|
2452
2579
|
diagnoseCmd.action(async (options) => {
|
|
2453
2580
|
const globals = diagnoseCmd.optsWithGlobals();
|
|
@@ -4105,7 +4232,12 @@ function parsePositivePrNumber(raw) {
|
|
|
4105
4232
|
const n = Number.parseInt(raw, 10);
|
|
4106
4233
|
return Number.isFinite(n) && n > 0 ? n : null;
|
|
4107
4234
|
}
|
|
4108
|
-
|
|
4235
|
+
function prNumberHelp(branchStatus = "active") {
|
|
4236
|
+
const matched = branchStatus === "abandoned" ? "abandoned" : "open";
|
|
4237
|
+
return `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 ${matched} PR matches, the command fails with a message naming the searched branch.`;
|
|
4238
|
+
}
|
|
4239
|
+
var PR_NUMBER_HELP = prNumberHelp();
|
|
4240
|
+
var PR_NUMBER_HELP_ABANDONED = prNumberHelp("abandoned");
|
|
4109
4241
|
var PR_REPO_HELP = 'Azure DevOps repository name; defaults to the repository of the git "origin" remote';
|
|
4110
4242
|
function configureUnwrappedHelp(command) {
|
|
4111
4243
|
return command.configureHelp({ helpWidth: 1e3 });
|
|
@@ -4123,6 +4255,13 @@ function autoDetectMultiMatch(branch, ids) {
|
|
|
4123
4255
|
const idList = ids.map((id) => `#${id}`).join(", ");
|
|
4124
4256
|
return `Multiple open pull requests match branch ${branch}: ${idList}. Re-run with --pr-number to choose.`;
|
|
4125
4257
|
}
|
|
4258
|
+
function abandonedAutoDetectZeroMatch(branch) {
|
|
4259
|
+
return `No abandoned pull request matches branch ${branch}. Pass --pr-number to target a specific PR.`;
|
|
4260
|
+
}
|
|
4261
|
+
function abandonedAutoDetectMultiMatch(branch, ids) {
|
|
4262
|
+
const idList = ids.map((id) => `#${id}`).join(", ");
|
|
4263
|
+
return `Multiple abandoned pull requests match branch ${branch}: ${idList}. Re-run with --pr-number to choose.`;
|
|
4264
|
+
}
|
|
4126
4265
|
function writeContractError(line) {
|
|
4127
4266
|
process.stderr.write(`${line}
|
|
4128
4267
|
`);
|
|
@@ -4595,11 +4734,14 @@ async function runPrUpdate(options) {
|
|
|
4595
4734
|
}
|
|
4596
4735
|
let context;
|
|
4597
4736
|
try {
|
|
4598
|
-
const target = await resolvePullRequestTarget(options
|
|
4737
|
+
const target = await resolvePullRequestTarget(options, {
|
|
4738
|
+
onContextResolved: (resolved) => {
|
|
4739
|
+
context = resolved;
|
|
4740
|
+
}
|
|
4741
|
+
});
|
|
4599
4742
|
if (target === null) {
|
|
4600
4743
|
return;
|
|
4601
4744
|
}
|
|
4602
|
-
context = target.context;
|
|
4603
4745
|
const current = target.pullRequest;
|
|
4604
4746
|
const { fields, updatedFields, requestedFields } = diffRequestedFields(requested, current);
|
|
4605
4747
|
if (updatedFields.length === 0) {
|
|
@@ -4655,6 +4797,102 @@ function createPrUpdateCommand() {
|
|
|
4655
4797
|
});
|
|
4656
4798
|
return command;
|
|
4657
4799
|
}
|
|
4800
|
+
var PR_STATUS_CHANGES = {
|
|
4801
|
+
abandon: {
|
|
4802
|
+
target: "abandoned",
|
|
4803
|
+
search: "active",
|
|
4804
|
+
verb: "abandoned",
|
|
4805
|
+
applied: "Abandoned",
|
|
4806
|
+
refusal: "abandoned"
|
|
4807
|
+
},
|
|
4808
|
+
reactivate: {
|
|
4809
|
+
target: "active",
|
|
4810
|
+
search: "abandoned",
|
|
4811
|
+
verb: "active",
|
|
4812
|
+
applied: "Reactivated",
|
|
4813
|
+
refusal: "reactivated"
|
|
4814
|
+
}
|
|
4815
|
+
};
|
|
4816
|
+
var COMPLETED_STATUS = "completed";
|
|
4817
|
+
async function runPrStatusChange(options, direction) {
|
|
4818
|
+
const change = PR_STATUS_CHANGES[direction];
|
|
4819
|
+
let context;
|
|
4820
|
+
try {
|
|
4821
|
+
const target = await resolvePullRequestTarget(options, {
|
|
4822
|
+
branchStatus: change.search,
|
|
4823
|
+
onContextResolved: (resolved) => {
|
|
4824
|
+
context = resolved;
|
|
4825
|
+
}
|
|
4826
|
+
});
|
|
4827
|
+
if (target === null) {
|
|
4828
|
+
return;
|
|
4829
|
+
}
|
|
4830
|
+
const current = target.pullRequest;
|
|
4831
|
+
if (current.status === COMPLETED_STATUS) {
|
|
4832
|
+
writeError(
|
|
4833
|
+
`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.`
|
|
4834
|
+
);
|
|
4835
|
+
return;
|
|
4836
|
+
}
|
|
4837
|
+
if (current.status === change.target) {
|
|
4838
|
+
const noopResult = {
|
|
4839
|
+
pullRequestId: current.id,
|
|
4840
|
+
title: current.title,
|
|
4841
|
+
status: current.status,
|
|
4842
|
+
previousStatus: current.status,
|
|
4843
|
+
url: current.url,
|
|
4844
|
+
noop: true
|
|
4845
|
+
};
|
|
4846
|
+
if (options.json) {
|
|
4847
|
+
process.stdout.write(`${JSON.stringify(noopResult, null, 2)}
|
|
4848
|
+
`);
|
|
4849
|
+
return;
|
|
4850
|
+
}
|
|
4851
|
+
process.stdout.write(`Pull request #${current.id} is already ${change.verb}; nothing to do.
|
|
4852
|
+
`);
|
|
4853
|
+
return;
|
|
4854
|
+
}
|
|
4855
|
+
const updated = await updatePullRequest(target.context, target.repo, target.pat, current.id, {
|
|
4856
|
+
status: change.target
|
|
4857
|
+
});
|
|
4858
|
+
const result = {
|
|
4859
|
+
pullRequestId: updated.id,
|
|
4860
|
+
title: updated.title,
|
|
4861
|
+
status: updated.status,
|
|
4862
|
+
previousStatus: current.status,
|
|
4863
|
+
url: updated.url,
|
|
4864
|
+
noop: false
|
|
4865
|
+
};
|
|
4866
|
+
if (options.json) {
|
|
4867
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
4868
|
+
`);
|
|
4869
|
+
return;
|
|
4870
|
+
}
|
|
4871
|
+
process.stdout.write(`${change.applied} pull request #${updated.id} (${updated.title}).
|
|
4872
|
+
${updated.url ?? "\u2014"}
|
|
4873
|
+
`);
|
|
4874
|
+
} catch (err) {
|
|
4875
|
+
handlePrCommandError(err, context, "write");
|
|
4876
|
+
}
|
|
4877
|
+
}
|
|
4878
|
+
function createPrAbandonCommand() {
|
|
4879
|
+
const command = new Command12("abandon");
|
|
4880
|
+
withCommonPrOptions(configureUnwrappedHelp(command)).alias("close").description(
|
|
4881
|
+
'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.'
|
|
4882
|
+
).option("--pr-number <N>", PR_NUMBER_HELP).option("--json", "output JSON").action(async (_options, command2) => {
|
|
4883
|
+
await runPrStatusChange(mergedPrOptions(command2), "abandon");
|
|
4884
|
+
});
|
|
4885
|
+
return command;
|
|
4886
|
+
}
|
|
4887
|
+
function createPrReactivateCommand() {
|
|
4888
|
+
const command = new Command12("reactivate");
|
|
4889
|
+
withCommonPrOptions(configureUnwrappedHelp(command)).description(
|
|
4890
|
+
"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."
|
|
4891
|
+
).option("--pr-number <N>", PR_NUMBER_HELP_ABANDONED).option("--json", "output JSON").action(async (_options, command2) => {
|
|
4892
|
+
await runPrStatusChange(mergedPrOptions(command2), "reactivate");
|
|
4893
|
+
});
|
|
4894
|
+
return command;
|
|
4895
|
+
}
|
|
4658
4896
|
function createPrCommentsCommand() {
|
|
4659
4897
|
const command = new Command12("comments");
|
|
4660
4898
|
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,52 +5008,75 @@ function createPrCommentsCommand() {
|
|
|
4770
5008
|
command.addCommand(createPrCommentsEditCommand());
|
|
4771
5009
|
return command;
|
|
4772
5010
|
}
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
if (options.prNumber !== void 0) {
|
|
4777
|
-
explicitPrId = parsePositivePrNumber(options.prNumber);
|
|
4778
|
-
if (explicitPrId === null) {
|
|
4779
|
-
writeError(`Invalid --pr-number "${options.prNumber}"; expected a positive integer.`);
|
|
4780
|
-
return null;
|
|
4781
|
-
}
|
|
5011
|
+
function parseTargetPrNumber(options) {
|
|
5012
|
+
if (options.prNumber === void 0) {
|
|
5013
|
+
return "none";
|
|
4782
5014
|
}
|
|
4783
|
-
const
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
if (pullRequests.length === 0) {
|
|
4800
|
-
writeContractError(autoDetectZeroMatch(resolved.branch));
|
|
4801
|
-
return null;
|
|
4802
|
-
}
|
|
4803
|
-
if (pullRequests.length > 1) {
|
|
4804
|
-
writeContractError(autoDetectMultiMatch(resolved.branch, pullRequests.map((pr) => pr.id)));
|
|
5015
|
+
const parsed = parsePositivePrNumber(options.prNumber);
|
|
5016
|
+
if (parsed === null) {
|
|
5017
|
+
writeError(`Invalid --pr-number "${options.prNumber}"; expected a positive integer.`);
|
|
5018
|
+
return "invalid";
|
|
5019
|
+
}
|
|
5020
|
+
return parsed;
|
|
5021
|
+
}
|
|
5022
|
+
async function fetchTargetById(resolved, prId) {
|
|
5023
|
+
try {
|
|
5024
|
+
return await getPullRequestById(resolved.context, resolved.repo, resolved.pat, prId);
|
|
5025
|
+
} catch (err) {
|
|
5026
|
+
if (err instanceof Error && err.message.startsWith("NOT_FOUND")) {
|
|
5027
|
+
writeError(
|
|
5028
|
+
`Pull request #${prId} not found in ${resolved.context.org}/${resolved.context.project}/${resolved.repo}.`,
|
|
5029
|
+
EXIT_NOT_FOUND
|
|
5030
|
+
);
|
|
4805
5031
|
return null;
|
|
4806
5032
|
}
|
|
4807
|
-
|
|
5033
|
+
throw err;
|
|
5034
|
+
}
|
|
5035
|
+
}
|
|
5036
|
+
async function findBranchPullRequest(resolved, branch, branchStatus) {
|
|
5037
|
+
const pullRequests = await listPullRequests(resolved.context, resolved.repo, resolved.pat, branch, {
|
|
5038
|
+
status: branchStatus
|
|
5039
|
+
});
|
|
5040
|
+
const abandoned = branchStatus === "abandoned";
|
|
5041
|
+
if (pullRequests.length === 0) {
|
|
5042
|
+
writeContractError(abandoned ? abandonedAutoDetectZeroMatch(branch) : autoDetectZeroMatch(branch));
|
|
5043
|
+
return null;
|
|
5044
|
+
}
|
|
5045
|
+
if (pullRequests.length > 1) {
|
|
5046
|
+
const ids = pullRequests.map((pr) => pr.id);
|
|
5047
|
+
writeContractError(
|
|
5048
|
+
abandoned ? abandonedAutoDetectMultiMatch(branch, ids) : autoDetectMultiMatch(branch, ids)
|
|
5049
|
+
);
|
|
5050
|
+
return null;
|
|
5051
|
+
}
|
|
5052
|
+
return pullRequests[0];
|
|
5053
|
+
}
|
|
5054
|
+
async function resolvePullRequestTarget(options, {
|
|
5055
|
+
branchStatus = "active",
|
|
5056
|
+
onContextResolved
|
|
5057
|
+
} = {}) {
|
|
5058
|
+
validateOrgProjectPair(options);
|
|
5059
|
+
const explicitPrId = parseTargetPrNumber(options);
|
|
5060
|
+
if (explicitPrId === "invalid") {
|
|
5061
|
+
return null;
|
|
5062
|
+
}
|
|
5063
|
+
const byBranch = explicitPrId === "none";
|
|
5064
|
+
const resolved = await resolvePrCommandContext(options, { requireBranch: byBranch });
|
|
5065
|
+
onContextResolved?.(resolved.context);
|
|
5066
|
+
const pullRequest = byBranch ? await findBranchPullRequest(resolved, resolved.branch, branchStatus) : await fetchTargetById(resolved, explicitPrId);
|
|
5067
|
+
if (pullRequest === null) {
|
|
5068
|
+
return null;
|
|
4808
5069
|
}
|
|
4809
5070
|
return { context: resolved.context, repo: resolved.repo, pat: resolved.pat, pullRequest };
|
|
4810
5071
|
}
|
|
4811
|
-
async function resolveThreadTarget(threadIdRaw, options) {
|
|
5072
|
+
async function resolveThreadTarget(threadIdRaw, options, { onContextResolved } = {}) {
|
|
4812
5073
|
const threadId = parsePositivePrNumber(threadIdRaw);
|
|
4813
5074
|
if (threadId === null) {
|
|
4814
5075
|
validateOrgProjectPair(options);
|
|
4815
5076
|
writeError(`Invalid thread id "${threadIdRaw}"; expected a positive integer.`);
|
|
4816
5077
|
return null;
|
|
4817
5078
|
}
|
|
4818
|
-
const target = await resolvePullRequestTarget(options);
|
|
5079
|
+
const target = await resolvePullRequestTarget(options, { onContextResolved });
|
|
4819
5080
|
if (target === null) {
|
|
4820
5081
|
return null;
|
|
4821
5082
|
}
|
|
@@ -4824,11 +5085,14 @@ async function resolveThreadTarget(threadIdRaw, options) {
|
|
|
4824
5085
|
async function runThreadStateChange(threadIdRaw, options, direction) {
|
|
4825
5086
|
let context;
|
|
4826
5087
|
try {
|
|
4827
|
-
const target = await resolveThreadTarget(threadIdRaw, options
|
|
5088
|
+
const target = await resolveThreadTarget(threadIdRaw, options, {
|
|
5089
|
+
onContextResolved: (resolved) => {
|
|
5090
|
+
context = resolved;
|
|
5091
|
+
}
|
|
5092
|
+
});
|
|
4828
5093
|
if (target === null) {
|
|
4829
5094
|
return;
|
|
4830
5095
|
}
|
|
4831
|
-
context = target.context;
|
|
4832
5096
|
const threads = await getPullRequestThreads(target.context, target.repo, target.pat, target.pullRequest.id);
|
|
4833
5097
|
const thread = threads.find((t) => t.id === target.threadId);
|
|
4834
5098
|
if (!thread) {
|
|
@@ -4901,11 +5165,14 @@ async function runCommentReply(threadIdRaw, text, options) {
|
|
|
4901
5165
|
if (trimmedText === null) {
|
|
4902
5166
|
return;
|
|
4903
5167
|
}
|
|
4904
|
-
const target = await resolveThreadTarget(threadIdRaw, options
|
|
5168
|
+
const target = await resolveThreadTarget(threadIdRaw, options, {
|
|
5169
|
+
onContextResolved: (resolved) => {
|
|
5170
|
+
context = resolved;
|
|
5171
|
+
}
|
|
5172
|
+
});
|
|
4905
5173
|
if (target === null) {
|
|
4906
5174
|
return;
|
|
4907
5175
|
}
|
|
4908
|
-
context = target.context;
|
|
4909
5176
|
const threads = await getPullRequestThreads(target.context, target.repo, target.pat, target.pullRequest.id);
|
|
4910
5177
|
const thread = threads.find((t) => t.id === target.threadId);
|
|
4911
5178
|
if (!thread) {
|
|
@@ -4979,11 +5246,14 @@ async function runCommentAdd(text, options) {
|
|
|
4979
5246
|
}
|
|
4980
5247
|
status2 = match;
|
|
4981
5248
|
}
|
|
4982
|
-
const target = await resolvePullRequestTarget(options
|
|
5249
|
+
const target = await resolvePullRequestTarget(options, {
|
|
5250
|
+
onContextResolved: (resolved) => {
|
|
5251
|
+
context = resolved;
|
|
5252
|
+
}
|
|
5253
|
+
});
|
|
4983
5254
|
if (target === null) {
|
|
4984
5255
|
return;
|
|
4985
5256
|
}
|
|
4986
|
-
context = target.context;
|
|
4987
5257
|
if (options.dryRun === true) {
|
|
4988
5258
|
const dryResult = {
|
|
4989
5259
|
pullRequestId: target.pullRequest.id,
|
|
@@ -5122,11 +5392,14 @@ async function runCommentEdit(threadIdRaw, text, options) {
|
|
|
5122
5392
|
return;
|
|
5123
5393
|
}
|
|
5124
5394
|
}
|
|
5125
|
-
const target = await resolveThreadTarget(threadIdRaw, options
|
|
5395
|
+
const target = await resolveThreadTarget(threadIdRaw, options, {
|
|
5396
|
+
onContextResolved: (resolved) => {
|
|
5397
|
+
context = resolved;
|
|
5398
|
+
}
|
|
5399
|
+
});
|
|
5126
5400
|
if (target === null) {
|
|
5127
5401
|
return;
|
|
5128
5402
|
}
|
|
5129
|
-
context = target.context;
|
|
5130
5403
|
const thread = await fetchThreadForEdit(target);
|
|
5131
5404
|
if (thread === null) {
|
|
5132
5405
|
return;
|
|
@@ -5261,11 +5534,14 @@ async function runWorkItemLinkChange(workItemIdRaw, options, direction) {
|
|
|
5261
5534
|
writeError(`Invalid work item id "${workItemIdRaw}"; expected a positive integer.`);
|
|
5262
5535
|
return;
|
|
5263
5536
|
}
|
|
5264
|
-
const target = await resolvePullRequestTarget(options
|
|
5537
|
+
const target = await resolvePullRequestTarget(options, {
|
|
5538
|
+
onContextResolved: (resolved) => {
|
|
5539
|
+
context = resolved;
|
|
5540
|
+
}
|
|
5541
|
+
});
|
|
5265
5542
|
if (target === null) {
|
|
5266
5543
|
return;
|
|
5267
5544
|
}
|
|
5268
|
-
context = target.context;
|
|
5269
5545
|
let outcome;
|
|
5270
5546
|
try {
|
|
5271
5547
|
outcome = direction === "link" ? await linkWorkItemToPullRequest(target.context, target.repo, target.pat, target.pullRequest.id, workItemId) : await unlinkWorkItemFromPullRequest(target.context, target.repo, target.pat, target.pullRequest.id, workItemId);
|
|
@@ -5320,11 +5596,14 @@ function createPrWorkItemsCommand() {
|
|
|
5320
5596
|
async function runReviewerAdd(reviewer, options) {
|
|
5321
5597
|
let context;
|
|
5322
5598
|
try {
|
|
5323
|
-
const target = await resolvePullRequestTarget(options
|
|
5599
|
+
const target = await resolvePullRequestTarget(options, {
|
|
5600
|
+
onContextResolved: (resolved) => {
|
|
5601
|
+
context = resolved;
|
|
5602
|
+
}
|
|
5603
|
+
});
|
|
5324
5604
|
if (target === null) {
|
|
5325
5605
|
return;
|
|
5326
5606
|
}
|
|
5327
|
-
context = target.context;
|
|
5328
5607
|
let identity;
|
|
5329
5608
|
try {
|
|
5330
5609
|
identity = await resolveReviewerIdentity(target.context.org, target.pat, reviewer);
|
|
@@ -5375,11 +5654,14 @@ async function runReviewerAdd(reviewer, options) {
|
|
|
5375
5654
|
async function runReviewerRemove(reviewer, options) {
|
|
5376
5655
|
let context;
|
|
5377
5656
|
try {
|
|
5378
|
-
const target = await resolvePullRequestTarget(options
|
|
5657
|
+
const target = await resolvePullRequestTarget(options, {
|
|
5658
|
+
onContextResolved: (resolved) => {
|
|
5659
|
+
context = resolved;
|
|
5660
|
+
}
|
|
5661
|
+
});
|
|
5379
5662
|
if (target === null) {
|
|
5380
5663
|
return;
|
|
5381
5664
|
}
|
|
5382
|
-
context = target.context;
|
|
5383
5665
|
let identity;
|
|
5384
5666
|
try {
|
|
5385
5667
|
identity = await resolveReviewerIdentity(target.context.org, target.pat, reviewer);
|
|
@@ -5435,6 +5717,8 @@ function createPrCommand() {
|
|
|
5435
5717
|
command.addCommand(createPrStatusCommand());
|
|
5436
5718
|
command.addCommand(createPrOpenCommand());
|
|
5437
5719
|
command.addCommand(createPrUpdateCommand());
|
|
5720
|
+
command.addCommand(createPrAbandonCommand());
|
|
5721
|
+
command.addCommand(createPrReactivateCommand());
|
|
5438
5722
|
command.addCommand(createPrCommentsCommand());
|
|
5439
5723
|
command.addCommand(createPrCommentResolveCommand());
|
|
5440
5724
|
command.addCommand(createPrCommentReopenCommand());
|
|
@@ -6930,6 +7214,18 @@ import os from "os";
|
|
|
6930
7214
|
var THROTTLE_MS = 10 * 60 * 1e3;
|
|
6931
7215
|
var FETCH_TIMEOUT_MS = 1500;
|
|
6932
7216
|
var REGISTRY_URL = "https://registry.npmjs.org/azdo-cli/latest";
|
|
7217
|
+
var UPDATE_CHECK_EXEMPT_COMMANDS = /* @__PURE__ */ new Set(["auth token"]);
|
|
7218
|
+
function commandPathOf(command) {
|
|
7219
|
+
const names = [];
|
|
7220
|
+
for (let node = command; node !== null; node = node.parent) {
|
|
7221
|
+
names.unshift(node.name());
|
|
7222
|
+
}
|
|
7223
|
+
return names;
|
|
7224
|
+
}
|
|
7225
|
+
function skipsUpdateCheck(commandPath) {
|
|
7226
|
+
const withoutRoot = commandPath.slice(1);
|
|
7227
|
+
return UPDATE_CHECK_EXEMPT_COMMANDS.has(withoutRoot.join(" "));
|
|
7228
|
+
}
|
|
6933
7229
|
function getCachePath() {
|
|
6934
7230
|
return path2.join(os.homedir(), ".azdo", "update-check.json");
|
|
6935
7231
|
}
|
|
@@ -7027,6 +7323,49 @@ async function getUpdateNotice(opts) {
|
|
|
7027
7323
|
}
|
|
7028
7324
|
}
|
|
7029
7325
|
|
|
7326
|
+
// src/program.ts
|
|
7327
|
+
function createProgram() {
|
|
7328
|
+
const program2 = new Command19();
|
|
7329
|
+
program2.name("azdo").description("Azure DevOps CLI tool").version(version, "-v, --version");
|
|
7330
|
+
program2.option("--no-update-check", "Skip the check for a newer published version");
|
|
7331
|
+
program2.option("--trace <filepath>", "Append redacted HTTP request/response trace to a file (owner-read-only permissions)");
|
|
7332
|
+
program2.addCommand(createGetItemCommand());
|
|
7333
|
+
program2.addCommand(createAuthCommand());
|
|
7334
|
+
program2.addCommand(createClearPatCommand());
|
|
7335
|
+
program2.addCommand(createConfigCommand());
|
|
7336
|
+
program2.addCommand(createSetStateCommand());
|
|
7337
|
+
program2.addCommand(createAssignCommand());
|
|
7338
|
+
program2.addCommand(createSetFieldCommand());
|
|
7339
|
+
program2.addCommand(createGetMdFieldCommand());
|
|
7340
|
+
program2.addCommand(createSetMdFieldCommand());
|
|
7341
|
+
program2.addCommand(createUpsertCommand());
|
|
7342
|
+
program2.addCommand(createListFieldsCommand());
|
|
7343
|
+
program2.addCommand(createPrCommand());
|
|
7344
|
+
program2.addCommand(createPipelineCommand());
|
|
7345
|
+
program2.addCommand(createCommentsCommand());
|
|
7346
|
+
program2.addCommand(createDownloadAttachmentCommand());
|
|
7347
|
+
program2.addCommand(createAddAttachmentCommand());
|
|
7348
|
+
program2.addCommand(createDeleteAttachmentCommand());
|
|
7349
|
+
program2.addCommand(createRelationsCommand());
|
|
7350
|
+
program2.showHelpAfterError();
|
|
7351
|
+
program2.hook("preAction", () => {
|
|
7352
|
+
const { trace } = program2.opts();
|
|
7353
|
+
if (trace) {
|
|
7354
|
+
initTraceWriter(trace);
|
|
7355
|
+
}
|
|
7356
|
+
});
|
|
7357
|
+
program2.hook("postAction", async (_thisCommand, actionCommand) => {
|
|
7358
|
+
if (skipsUpdateCheck(commandPathOf(actionCommand))) {
|
|
7359
|
+
return;
|
|
7360
|
+
}
|
|
7361
|
+
const notice = await getUpdateNotice({ enabled: program2.opts().updateCheck });
|
|
7362
|
+
if (notice) {
|
|
7363
|
+
process.stderr.write(notice + "\n");
|
|
7364
|
+
}
|
|
7365
|
+
});
|
|
7366
|
+
return program2;
|
|
7367
|
+
}
|
|
7368
|
+
|
|
7030
7369
|
// src/index.ts
|
|
7031
7370
|
function exitOnEpipe(err) {
|
|
7032
7371
|
if (err.code === "EPIPE") {
|
|
@@ -7036,41 +7375,7 @@ function exitOnEpipe(err) {
|
|
|
7036
7375
|
}
|
|
7037
7376
|
process.stdout.on("error", exitOnEpipe);
|
|
7038
7377
|
process.stderr.on("error", exitOnEpipe);
|
|
7039
|
-
var program =
|
|
7040
|
-
program.name("azdo").description("Azure DevOps CLI tool").version(version, "-v, --version");
|
|
7041
|
-
program.option("--no-update-check", "Skip the check for a newer published version");
|
|
7042
|
-
program.option("--trace <filepath>", "Append redacted HTTP request/response trace to a file (owner-read-only permissions)");
|
|
7043
|
-
program.addCommand(createGetItemCommand());
|
|
7044
|
-
program.addCommand(createAuthCommand());
|
|
7045
|
-
program.addCommand(createClearPatCommand());
|
|
7046
|
-
program.addCommand(createConfigCommand());
|
|
7047
|
-
program.addCommand(createSetStateCommand());
|
|
7048
|
-
program.addCommand(createAssignCommand());
|
|
7049
|
-
program.addCommand(createSetFieldCommand());
|
|
7050
|
-
program.addCommand(createGetMdFieldCommand());
|
|
7051
|
-
program.addCommand(createSetMdFieldCommand());
|
|
7052
|
-
program.addCommand(createUpsertCommand());
|
|
7053
|
-
program.addCommand(createListFieldsCommand());
|
|
7054
|
-
program.addCommand(createPrCommand());
|
|
7055
|
-
program.addCommand(createPipelineCommand());
|
|
7056
|
-
program.addCommand(createCommentsCommand());
|
|
7057
|
-
program.addCommand(createDownloadAttachmentCommand());
|
|
7058
|
-
program.addCommand(createAddAttachmentCommand());
|
|
7059
|
-
program.addCommand(createDeleteAttachmentCommand());
|
|
7060
|
-
program.addCommand(createRelationsCommand());
|
|
7061
|
-
program.showHelpAfterError();
|
|
7062
|
-
program.hook("preAction", () => {
|
|
7063
|
-
const { trace } = program.opts();
|
|
7064
|
-
if (trace) {
|
|
7065
|
-
initTraceWriter(trace);
|
|
7066
|
-
}
|
|
7067
|
-
});
|
|
7068
|
-
program.hook("postAction", async () => {
|
|
7069
|
-
const notice = await getUpdateNotice({ enabled: program.opts().updateCheck });
|
|
7070
|
-
if (notice) {
|
|
7071
|
-
process.stderr.write(notice + "\n");
|
|
7072
|
-
}
|
|
7073
|
-
});
|
|
7378
|
+
var program = createProgram();
|
|
7074
7379
|
await program.parseAsync();
|
|
7075
7380
|
if (process.argv.length <= 2) {
|
|
7076
7381
|
program.help();
|