@kolatts/pncli 1.23.0 → 1.25.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/dist/{chunk-XYHZMXJU.js → chunk-ABHLES5M.js} +35 -1
- package/dist/chunk-ABHLES5M.js.map +1 -0
- package/dist/{chunk-KRDKHGYS.js → chunk-JOAWUILY.js} +12 -5
- package/dist/chunk-JOAWUILY.js.map +1 -0
- package/dist/cli.js +198 -8
- package/dist/cli.js.map +1 -1
- package/dist/{config-QN5FFAZY.js → config-SQ3YHJHI.js} +2 -2
- package/dist/{http-MCIU2IV6.js → http-WCK6AZW6.js} +2 -2
- package/package.json +2 -2
- package/skills/pncli/ado.md +2 -0
- package/skills/pncli/github.md +19 -0
- package/skills/pncli/sonarqube.md +2 -0
- package/dist/chunk-KRDKHGYS.js.map +0 -1
- package/dist/chunk-XYHZMXJU.js.map +0 -1
- /package/dist/{config-QN5FFAZY.js.map → config-SQ3YHJHI.js.map} +0 -0
- /package/dist/{http-MCIU2IV6.js.map → http-WCK6AZW6.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
setRepoConfigValue,
|
|
9
9
|
writeGlobalConfig,
|
|
10
10
|
writeRepoConfig
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-JOAWUILY.js";
|
|
12
12
|
import {
|
|
13
13
|
ExitCode,
|
|
14
14
|
PncliError,
|
|
@@ -20,13 +20,29 @@ import {
|
|
|
20
20
|
success,
|
|
21
21
|
warn,
|
|
22
22
|
writeRawOutput
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-ABHLES5M.js";
|
|
24
24
|
import {
|
|
25
25
|
AdoCoreClient
|
|
26
26
|
} from "./chunk-VDL32PSG.js";
|
|
27
27
|
|
|
28
28
|
// src/cli.ts
|
|
29
29
|
import { Command as Command3 } from "commander";
|
|
30
|
+
|
|
31
|
+
// src/lib/proxyFetch.ts
|
|
32
|
+
async function configureProxy() {
|
|
33
|
+
try {
|
|
34
|
+
const { setGlobalDispatcher, EnvHttpProxyAgent } = await import("undici");
|
|
35
|
+
setGlobalDispatcher(new EnvHttpProxyAgent());
|
|
36
|
+
} catch (err) {
|
|
37
|
+
const isModuleNotFound = err instanceof Error && "code" in err && err.code === "ERR_MODULE_NOT_FOUND";
|
|
38
|
+
if (!isModuleNotFound) {
|
|
39
|
+
process.stderr.write(`[pncli] Warning: failed to configure proxy: ${String(err)}
|
|
40
|
+
`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// src/cli.ts
|
|
30
46
|
import { createRequire } from "module";
|
|
31
47
|
|
|
32
48
|
// src/services/git/client.ts
|
|
@@ -778,6 +794,17 @@ var BitbucketClient = class {
|
|
|
778
794
|
}
|
|
779
795
|
);
|
|
780
796
|
}
|
|
797
|
+
/**
|
|
798
|
+
* Returns true if `from` has commits not yet in `to` (i.e. there is something
|
|
799
|
+
* to promote), false if the branches are at the same point.
|
|
800
|
+
*/
|
|
801
|
+
async compareCommits(project, repo, from, to) {
|
|
802
|
+
const result = await this.http.bitbucket(
|
|
803
|
+
`${API}/projects/${project}/repos/${repo}/commits`,
|
|
804
|
+
{ params: { until: from, since: to, limit: 1 } }
|
|
805
|
+
);
|
|
806
|
+
return (result.values?.length ?? 0) > 0;
|
|
807
|
+
}
|
|
781
808
|
};
|
|
782
809
|
|
|
783
810
|
// src/services/git/commands.ts
|
|
@@ -1830,6 +1857,50 @@ function registerBitbucketCommands(program2) {
|
|
|
1830
1857
|
fail(err, "bitbucket", "create-pr", start);
|
|
1831
1858
|
}
|
|
1832
1859
|
});
|
|
1860
|
+
bb.command("create-prs").description("Create pull requests for multiple repositories in a project").requiredOption("--from <branch>", "Source branch").requiredOption("--to <branch>", "Target branch").requiredOption("--repos <slugs>", "Comma-separated repository slugs").option("--title <title>", 'PR title (defaults to "Promote <from> to <to>")').option("--description <desc>", "PR description").option("--reviewers <users>", "Comma-separated reviewer usernames").option("--project <key>", "Bitbucket project key (overrides parent --project)").action(async (opts) => {
|
|
1861
|
+
const start = Date.now();
|
|
1862
|
+
try {
|
|
1863
|
+
const globalOpts = bb.optsWithGlobals();
|
|
1864
|
+
const config = loadConfig({ configPath: globalOpts.config });
|
|
1865
|
+
const http = createHttpClient(config, Boolean(globalOpts.dryRun));
|
|
1866
|
+
const client = new BitbucketClient(http);
|
|
1867
|
+
const ctx = getGitContext(config);
|
|
1868
|
+
const project = opts.project ?? globalOpts.project ?? ctx?.project ?? config.defaults.bitbucket?.project ?? "";
|
|
1869
|
+
if (!project) {
|
|
1870
|
+
throw new PncliError("Could not determine Bitbucket project. Pass --project, or run pncli config init.", 1);
|
|
1871
|
+
}
|
|
1872
|
+
const repos = opts.repos.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1873
|
+
if (repos.length === 0) {
|
|
1874
|
+
throw new PncliError("--repos must contain at least one repository slug", 1);
|
|
1875
|
+
}
|
|
1876
|
+
const title = opts.title ?? `Promote ${opts.from} to ${opts.to}`;
|
|
1877
|
+
const reviewers = opts.reviewers ? opts.reviewers.split(",").map((s) => s.trim()) : [];
|
|
1878
|
+
const results = [];
|
|
1879
|
+
let created = 0;
|
|
1880
|
+
let nothingToPromote = 0;
|
|
1881
|
+
let errors = 0;
|
|
1882
|
+
for (const repo of repos) {
|
|
1883
|
+
try {
|
|
1884
|
+
const hasCommits = await client.compareCommits(project, repo, opts.from, opts.to);
|
|
1885
|
+
if (!hasCommits) {
|
|
1886
|
+
results.push({ repo, status: "nothing_to_promote" });
|
|
1887
|
+
nothingToPromote++;
|
|
1888
|
+
continue;
|
|
1889
|
+
}
|
|
1890
|
+
const pr = await client.createPR({ project, repo, title, source: opts.from, target: opts.to, description: opts.description, reviewers });
|
|
1891
|
+
results.push({ repo, status: "created", pr });
|
|
1892
|
+
created++;
|
|
1893
|
+
} catch (err) {
|
|
1894
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1895
|
+
results.push({ repo, status: "error", error: message });
|
|
1896
|
+
errors++;
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
success({ results, summary: { created, nothing_to_promote: nothingToPromote, error: errors } }, "bitbucket", "create-prs", start);
|
|
1900
|
+
} catch (err) {
|
|
1901
|
+
fail(err, "bitbucket", "create-prs", start);
|
|
1902
|
+
}
|
|
1903
|
+
});
|
|
1833
1904
|
bb.command("update-pr").description("Update a pull request").requiredOption("--id <pr-id>", "Pull request ID").option("--title <title>", "New title").option("--description <desc>", "New description").option("--reviewers <users>", "Comma-separated reviewer usernames").action(async (opts) => {
|
|
1834
1905
|
const start = Date.now();
|
|
1835
1906
|
try {
|
|
@@ -2235,12 +2306,84 @@ var GitHubClient = class {
|
|
|
2235
2306
|
}
|
|
2236
2307
|
});
|
|
2237
2308
|
}
|
|
2309
|
+
async listReviewThreads(owner, repo, pullNumber) {
|
|
2310
|
+
const query = `
|
|
2311
|
+
query($owner: String!, $repo: String!, $number: Int!) {
|
|
2312
|
+
repository(owner: $owner, name: $repo) {
|
|
2313
|
+
pullRequest(number: $number) {
|
|
2314
|
+
reviewThreads(first: 100) {
|
|
2315
|
+
pageInfo { hasNextPage }
|
|
2316
|
+
nodes {
|
|
2317
|
+
id
|
|
2318
|
+
isResolved
|
|
2319
|
+
isOutdated
|
|
2320
|
+
path
|
|
2321
|
+
line
|
|
2322
|
+
comments(first: 10) {
|
|
2323
|
+
nodes {
|
|
2324
|
+
id
|
|
2325
|
+
databaseId
|
|
2326
|
+
body
|
|
2327
|
+
author { login }
|
|
2328
|
+
createdAt
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
`;
|
|
2337
|
+
const data = await this.http.githubGraphQL(query, { owner, repo, number: pullNumber });
|
|
2338
|
+
const threads = data.repository.pullRequest.reviewThreads;
|
|
2339
|
+
if (threads.pageInfo?.hasNextPage) {
|
|
2340
|
+
process.stderr.write(`warning: more than ${threads.nodes.length} review threads on PR #${pullNumber}; only showing the first ${threads.nodes.length}
|
|
2341
|
+
`);
|
|
2342
|
+
}
|
|
2343
|
+
return threads.nodes;
|
|
2344
|
+
}
|
|
2345
|
+
async resolveReviewThread(threadId) {
|
|
2346
|
+
const mutation = `
|
|
2347
|
+
mutation($threadId: ID!) {
|
|
2348
|
+
resolveReviewThread(input: { threadId: $threadId }) {
|
|
2349
|
+
thread {
|
|
2350
|
+
id
|
|
2351
|
+
isResolved
|
|
2352
|
+
isOutdated
|
|
2353
|
+
path
|
|
2354
|
+
line
|
|
2355
|
+
comments(first: 10) {
|
|
2356
|
+
nodes {
|
|
2357
|
+
id
|
|
2358
|
+
databaseId
|
|
2359
|
+
body
|
|
2360
|
+
author { login }
|
|
2361
|
+
createdAt
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
`;
|
|
2368
|
+
const data = await this.http.githubGraphQL(mutation, { threadId });
|
|
2369
|
+
return data.resolveReviewThread.thread;
|
|
2370
|
+
}
|
|
2238
2371
|
};
|
|
2239
2372
|
|
|
2240
2373
|
// src/services/github/commands.ts
|
|
2241
2374
|
function getClient3(program2, overrides) {
|
|
2242
2375
|
const opts = program2.optsWithGlobals();
|
|
2243
2376
|
const config = loadConfig({ configPath: opts.config });
|
|
2377
|
+
const missing = [];
|
|
2378
|
+
if (!config.github.baseUrl) missing.push("github.baseUrl (e.g. pncli config set github.baseUrl https://api.github.com)");
|
|
2379
|
+
if (!config.github.token) missing.push("github.token (e.g. pncli config set github.token <your-PAT>)");
|
|
2380
|
+
if (missing.length) {
|
|
2381
|
+
throw new PncliError(
|
|
2382
|
+
`GitHub not configured. Missing required settings:
|
|
2383
|
+
${missing.join("\n ")}`,
|
|
2384
|
+
1
|
|
2385
|
+
);
|
|
2386
|
+
}
|
|
2244
2387
|
const http = createHttpClient(config, Boolean(opts.dryRun));
|
|
2245
2388
|
const client = new GitHubClient(http);
|
|
2246
2389
|
const ctx = getGitContext(config);
|
|
@@ -2541,6 +2684,26 @@ function registerGitHubCommands(program2) {
|
|
|
2541
2684
|
fail(err, "github", "list-reviewers", start);
|
|
2542
2685
|
}
|
|
2543
2686
|
});
|
|
2687
|
+
gh.command("list-review-threads").description("List review threads on a pull request (includes thread node IDs for resolve-thread)").requiredOption("--number <n>", "Pull request number").action(async (opts) => {
|
|
2688
|
+
const start = Date.now();
|
|
2689
|
+
try {
|
|
2690
|
+
const { client, owner, repo } = getClient3(gh);
|
|
2691
|
+
const data = await client.listReviewThreads(owner, repo, parseInt(opts.number, 10));
|
|
2692
|
+
success(data, "github", "list-review-threads", start);
|
|
2693
|
+
} catch (err) {
|
|
2694
|
+
fail(err, "github", "list-review-threads", start);
|
|
2695
|
+
}
|
|
2696
|
+
});
|
|
2697
|
+
gh.command("resolve-thread").description("Mark a review thread as resolved (use list-review-threads to get the thread id)").requiredOption("--thread-id <id>", "GraphQL node ID of the review thread (e.g. PRT_kwDO...)").action(async (opts) => {
|
|
2698
|
+
const start = Date.now();
|
|
2699
|
+
try {
|
|
2700
|
+
const { client } = getClient3(gh);
|
|
2701
|
+
const data = await client.resolveReviewThread(opts.threadId);
|
|
2702
|
+
success(data, "github", "resolve-thread", start);
|
|
2703
|
+
} catch (err) {
|
|
2704
|
+
fail(err, "github", "resolve-thread", start);
|
|
2705
|
+
}
|
|
2706
|
+
});
|
|
2544
2707
|
gh.command("get-status").description("Get combined commit status for a ref (branch, tag, or SHA)").requiredOption("--ref <ref>", "Git ref (branch, tag, or commit SHA)").action(async (opts) => {
|
|
2545
2708
|
const start = Date.now();
|
|
2546
2709
|
try {
|
|
@@ -2778,6 +2941,28 @@ function xmlParseHint(err, bodyContent, convertedFromMarkdown = false) {
|
|
|
2778
2941
|
return `Offending line ${row}${suffix}: ${line}
|
|
2779
2942
|
${pointer}`;
|
|
2780
2943
|
}
|
|
2944
|
+
function storageFormatHint(err, bodyContent) {
|
|
2945
|
+
if (!(err instanceof PncliError) || err.status !== 500) return null;
|
|
2946
|
+
const hints = [];
|
|
2947
|
+
if (/<ac:parameter[^>]*(?<![:\w])name\s*=/i.test(bodyContent)) {
|
|
2948
|
+
hints.push(
|
|
2949
|
+
'<ac:parameter name="..."> found \u2014 use ac:name="..." instead (e.g. <ac:parameter ac:name="language">text</ac:parameter>).'
|
|
2950
|
+
);
|
|
2951
|
+
}
|
|
2952
|
+
const macroTagRe = /<ac:structured-macro(?:\s[^>]*)?\s*>/gi;
|
|
2953
|
+
let macroMatch;
|
|
2954
|
+
while ((macroMatch = macroTagRe.exec(bodyContent)) !== null) {
|
|
2955
|
+
if (!/\bac:macro-id\b/.test(macroMatch[0])) {
|
|
2956
|
+
hints.push(
|
|
2957
|
+
'<ac:structured-macro> without ac:macro-id found \u2014 add a unique UUID: ac:macro-id="<uuid>" (e.g. ac:macro-id="a1b2c3d4-...").'
|
|
2958
|
+
);
|
|
2959
|
+
break;
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
if (hints.length === 0) return null;
|
|
2963
|
+
return `HTTP 500 \u2014 possible storage-format causes:
|
|
2964
|
+
${hints.map((h) => ` \u2022 ${h}`).join("\n")}`;
|
|
2965
|
+
}
|
|
2781
2966
|
function getClient4(program2) {
|
|
2782
2967
|
const opts = program2.optsWithGlobals();
|
|
2783
2968
|
const config = loadConfig({ configPath: opts.config });
|
|
@@ -2873,7 +3058,9 @@ function registerConfluenceCommands(program2) {
|
|
|
2873
3058
|
});
|
|
2874
3059
|
success(data, "confluence", "create-page", start);
|
|
2875
3060
|
} catch (err) {
|
|
2876
|
-
const
|
|
3061
|
+
const xmlHint = bodyContent ? xmlParseHint(err, bodyContent, Boolean(opts.markdown)) : null;
|
|
3062
|
+
const sfHint = bodyContent ? storageFormatHint(err, bodyContent) : null;
|
|
3063
|
+
const hint = [xmlHint, sfHint].filter(Boolean).join("\n\n") || null;
|
|
2877
3064
|
if (hint) {
|
|
2878
3065
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2879
3066
|
const status = err instanceof PncliError ? err.status : 1;
|
|
@@ -2905,7 +3092,9 @@ ${hint}`, status, url), "confluence", "create-page", start);
|
|
|
2905
3092
|
});
|
|
2906
3093
|
success(data, "confluence", "update-page", start);
|
|
2907
3094
|
} catch (err) {
|
|
2908
|
-
const
|
|
3095
|
+
const xmlHint = bodyContent ? xmlParseHint(err, bodyContent, Boolean(opts.markdown)) : null;
|
|
3096
|
+
const sfHint = bodyContent ? storageFormatHint(err, bodyContent) : null;
|
|
3097
|
+
const hint = [xmlHint, sfHint].filter(Boolean).join("\n\n") || null;
|
|
2909
3098
|
if (hint) {
|
|
2910
3099
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2911
3100
|
const status = err instanceof PncliError ? err.status : 1;
|
|
@@ -5724,7 +5913,7 @@ function registerConfigCommands(program2) {
|
|
|
5724
5913
|
}
|
|
5725
5914
|
if (cfg.checkmarx.baseUrl && cfg.checkmarx.tenantName && (cfg.checkmarx.apiKey || cfg.checkmarx.clientId && cfg.checkmarx.clientSecret)) {
|
|
5726
5915
|
try {
|
|
5727
|
-
await http.checkmarx("
|
|
5916
|
+
await http.checkmarx("projects", { params: { limit: 1 } });
|
|
5728
5917
|
results.checkmarx = { ok: true, message: "connected" };
|
|
5729
5918
|
} catch (err) {
|
|
5730
5919
|
results.checkmarx = { ok: false, message: err instanceof Error ? err.message : String(err) };
|
|
@@ -5966,7 +6155,7 @@ function registerConfigCommands(program2) {
|
|
|
5966
6155
|
results.checkmarx = { status: "error", message: "baseUrl not configured" };
|
|
5967
6156
|
} else {
|
|
5968
6157
|
try {
|
|
5969
|
-
await http.checkmarx("
|
|
6158
|
+
await http.checkmarx("projects", { params: { limit: 1 }, timeoutMs: 1e4 });
|
|
5970
6159
|
results.checkmarx = { status: "valid", message: "ok" };
|
|
5971
6160
|
} catch (err) {
|
|
5972
6161
|
results.checkmarx = categorize2(err);
|
|
@@ -7979,11 +8168,11 @@ function registerAdoRepoCommands(ado) {
|
|
|
7979
8168
|
const start = Date.now();
|
|
7980
8169
|
try {
|
|
7981
8170
|
const globalOpts = ado.optsWithGlobals();
|
|
7982
|
-
const { loadConfig: lc } = await import("./config-
|
|
8171
|
+
const { loadConfig: lc } = await import("./config-SQ3YHJHI.js");
|
|
7983
8172
|
const config = lc({ configPath: globalOpts.config });
|
|
7984
8173
|
const { collection, project, repo: repo2, gitClient } = getAdoContext(ado, true);
|
|
7985
8174
|
const { AdoCoreClient: AdoCoreClient2 } = await import("./core-SWYVXOQ4.js");
|
|
7986
|
-
const { createHttpClient: createHttpClient2 } = await import("./http-
|
|
8175
|
+
const { createHttpClient: createHttpClient2 } = await import("./http-WCK6AZW6.js");
|
|
7987
8176
|
const http = createHttpClient2(config, Boolean(globalOpts.dryRun));
|
|
7988
8177
|
const coreClient = new AdoCoreClient2(http);
|
|
7989
8178
|
const me = await coreClient.getConnectionData(collection);
|
|
@@ -10752,6 +10941,7 @@ function registerLogscaleCommands(program2) {
|
|
|
10752
10941
|
if (!process.env.PNCLI_VERIFY_TLS) {
|
|
10753
10942
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
10754
10943
|
}
|
|
10944
|
+
await configureProxy();
|
|
10755
10945
|
var require2 = createRequire(import.meta.url);
|
|
10756
10946
|
var pkg = require2("../package.json");
|
|
10757
10947
|
var TAGLINE = "One command does what three meetings couldn't.";
|