@kolatts/pncli 1.6.1 → 1.8.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/NOTICE +22 -398
- package/README.md +40 -17
- package/copilot-instructions.md +118 -16
- package/dist/{chunk-HWXXNMGE.js → chunk-QQ7UKWTU.js} +282 -9
- package/dist/chunk-QQ7UKWTU.js.map +1 -0
- package/dist/{chunk-VEHCNGUP.js → chunk-S4SJLY4M.js} +42 -2
- package/dist/chunk-S4SJLY4M.js.map +1 -0
- package/dist/{chunk-KT3XINLJ.js → chunk-VDL32PSG.js} +3 -2
- package/dist/chunk-VDL32PSG.js.map +1 -0
- package/dist/cli.js +1036 -81
- package/dist/cli.js.map +1 -1
- package/dist/{config-IJ57IIQA.js → config-PXL632ZX.js} +2 -2
- package/dist/{core-AJYPJ2Z7.js → core-SWYVXOQ4.js} +2 -2
- package/dist/{http-FC24JSK7.js → http-TID3W235.js} +2 -2
- package/package.json +5 -2
- package/dist/chunk-HWXXNMGE.js.map +0 -1
- package/dist/chunk-KT3XINLJ.js.map +0 -1
- package/dist/chunk-VEHCNGUP.js.map +0 -1
- /package/dist/{config-IJ57IIQA.js.map → config-PXL632ZX.js.map} +0 -0
- /package/dist/{core-AJYPJ2Z7.js.map → core-SWYVXOQ4.js.map} +0 -0
- /package/dist/{http-FC24JSK7.js.map → http-TID3W235.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
setRepoConfigValue,
|
|
8
8
|
writeGlobalConfig,
|
|
9
9
|
writeRepoConfig
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-S4SJLY4M.js";
|
|
11
11
|
import {
|
|
12
12
|
ExitCode,
|
|
13
13
|
PncliError,
|
|
@@ -18,10 +18,10 @@ import {
|
|
|
18
18
|
setGlobalUser,
|
|
19
19
|
success,
|
|
20
20
|
warn
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-QQ7UKWTU.js";
|
|
22
22
|
import {
|
|
23
23
|
AdoCoreClient
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-VDL32PSG.js";
|
|
25
25
|
|
|
26
26
|
// src/cli.ts
|
|
27
27
|
import { Command } from "commander";
|
|
@@ -847,7 +847,7 @@ function registerJiraCommands(program2) {
|
|
|
847
847
|
fail(err, "jira", "list-comments", start);
|
|
848
848
|
}
|
|
849
849
|
});
|
|
850
|
-
jira.command("search").description("Search Jira issues with JQL").requiredOption("--jql <query>", "JQL query string").option("--max-results <n>", "Maximum number of results").action(async (opts) => {
|
|
850
|
+
jira.command("search").description("Search Jira issues with JQL (consider --output-file for large results)").requiredOption("--jql <query>", "JQL query string").option("--max-results <n>", "Maximum number of results").action(async (opts) => {
|
|
851
851
|
const start = Date.now();
|
|
852
852
|
try {
|
|
853
853
|
const { client, fieldMap, customFields } = getClientAndFields(program2);
|
|
@@ -883,7 +883,7 @@ function registerJiraCommands(program2) {
|
|
|
883
883
|
fail(err, "jira", "link-issue", start);
|
|
884
884
|
}
|
|
885
885
|
});
|
|
886
|
-
jira.command("fields").description("List custom fields (configured or discovered from Jira API)").option("--discover", "Fetch field metadata from Jira API").option("--custom-only", "Show only custom fields (requires --discover)").action(async (opts) => {
|
|
886
|
+
jira.command("fields").description("List custom fields (configured or discovered from Jira API) (consider --output-file for large results)").option("--discover", "Fetch field metadata from Jira API").option("--custom-only", "Show only custom fields (requires --discover)").action(async (opts) => {
|
|
887
887
|
const start = Date.now();
|
|
888
888
|
try {
|
|
889
889
|
if (opts.discover) {
|
|
@@ -936,7 +936,7 @@ function registerBitbucketCommands(program2) {
|
|
|
936
936
|
bb.command("list-prs").description("List pull requests").option("--state <state>", "PR state: OPEN|MERGED|DECLINED|ALL", "OPEN").option("--author <username>", "Filter by author username").option("--reviewer <username>", "Filter by reviewer username").action(async (opts) => {
|
|
937
937
|
const start = Date.now();
|
|
938
938
|
try {
|
|
939
|
-
const { client, project, repo } = getClient2(
|
|
939
|
+
const { client, project, repo } = getClient2(bb);
|
|
940
940
|
const data = await client.listPRs({ project, repo, state: opts.state, author: opts.author, reviewer: opts.reviewer });
|
|
941
941
|
success(data, "bitbucket", "list-prs", start);
|
|
942
942
|
} catch (err) {
|
|
@@ -946,7 +946,7 @@ function registerBitbucketCommands(program2) {
|
|
|
946
946
|
bb.command("get-pr").description("Get a pull request by ID").requiredOption("--id <pr-id>", "Pull request ID").action(async (opts) => {
|
|
947
947
|
const start = Date.now();
|
|
948
948
|
try {
|
|
949
|
-
const { client, project, repo } = getClient2(
|
|
949
|
+
const { client, project, repo } = getClient2(bb);
|
|
950
950
|
const data = await client.getPR(project, repo, parseInt(opts.id, 10));
|
|
951
951
|
success(data, "bitbucket", "get-pr", start);
|
|
952
952
|
} catch (err) {
|
|
@@ -956,8 +956,8 @@ function registerBitbucketCommands(program2) {
|
|
|
956
956
|
bb.command("create-pr").description("Create a pull request").requiredOption("--title <title>", "PR title").requiredOption("--source <branch>", "Source branch").option("--target <branch>", "Target branch (defaults to config)").option("--description <desc>", "PR description").option("--reviewers <users>", "Comma-separated reviewer usernames").action(async (opts) => {
|
|
957
957
|
const start = Date.now();
|
|
958
958
|
try {
|
|
959
|
-
const { client, project, repo } = getClient2(
|
|
960
|
-
const config = loadConfig({ configPath:
|
|
959
|
+
const { client, project, repo } = getClient2(bb);
|
|
960
|
+
const config = loadConfig({ configPath: bb.optsWithGlobals().config });
|
|
961
961
|
const target = opts.target ?? config.defaults.bitbucket?.targetBranch ?? "main";
|
|
962
962
|
const reviewers = opts.reviewers ? opts.reviewers.split(",").map((s) => s.trim()) : [];
|
|
963
963
|
const data = await client.createPR({ project, repo, title: opts.title, source: opts.source, target, description: opts.description, reviewers });
|
|
@@ -969,7 +969,7 @@ function registerBitbucketCommands(program2) {
|
|
|
969
969
|
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) => {
|
|
970
970
|
const start = Date.now();
|
|
971
971
|
try {
|
|
972
|
-
const { client, project, repo } = getClient2(
|
|
972
|
+
const { client, project, repo } = getClient2(bb);
|
|
973
973
|
const prId = parseInt(opts.id, 10);
|
|
974
974
|
const pr = await client.getPR(project, repo, prId);
|
|
975
975
|
const reviewers = opts.reviewers ? opts.reviewers.split(",").map((s) => s.trim()) : void 0;
|
|
@@ -982,7 +982,7 @@ function registerBitbucketCommands(program2) {
|
|
|
982
982
|
bb.command("merge-pr").description("Merge a pull request").requiredOption("--id <pr-id>", "Pull request ID").option("--strategy <strategy>", "Merge strategy: merge|squash|ff").option("--delete-branch", "Delete source branch after merge").action(async (opts) => {
|
|
983
983
|
const start = Date.now();
|
|
984
984
|
try {
|
|
985
|
-
const { client, project, repo } = getClient2(
|
|
985
|
+
const { client, project, repo } = getClient2(bb);
|
|
986
986
|
const prId = parseInt(opts.id, 10);
|
|
987
987
|
const pr = await client.getPR(project, repo, prId);
|
|
988
988
|
const data = await client.mergePR({ project, repo, id: prId, version: pr.version, strategy: opts.strategy, deleteBranch: opts.deleteBranch });
|
|
@@ -994,7 +994,7 @@ function registerBitbucketCommands(program2) {
|
|
|
994
994
|
bb.command("decline-pr").description("Decline a pull request").requiredOption("--id <pr-id>", "Pull request ID").action(async (opts) => {
|
|
995
995
|
const start = Date.now();
|
|
996
996
|
try {
|
|
997
|
-
const { client, project, repo } = getClient2(
|
|
997
|
+
const { client, project, repo } = getClient2(bb);
|
|
998
998
|
const prId = parseInt(opts.id, 10);
|
|
999
999
|
const pr = await client.getPR(project, repo, prId);
|
|
1000
1000
|
const data = await client.declinePR(project, repo, prId, pr.version);
|
|
@@ -1006,7 +1006,7 @@ function registerBitbucketCommands(program2) {
|
|
|
1006
1006
|
bb.command("list-comments").description("List comments on a pull request (includes threaded replies by default)").requiredOption("--pr <pr-id>", "Pull request ID").option("--no-with-replies", "Exclude replies; return top-level comments only").option("--inline-only", "Return only inline file comments").option("--general-only", "Return only general (non-inline) PR comments").action(async (opts) => {
|
|
1007
1007
|
const start = Date.now();
|
|
1008
1008
|
try {
|
|
1009
|
-
const { client, project, repo } = getClient2(
|
|
1009
|
+
const { client, project, repo } = getClient2(bb);
|
|
1010
1010
|
const data = await client.listComments(project, repo, parseInt(opts.pr, 10), {
|
|
1011
1011
|
withReplies: opts.withReplies,
|
|
1012
1012
|
inlineOnly: opts.inlineOnly,
|
|
@@ -1020,7 +1020,7 @@ function registerBitbucketCommands(program2) {
|
|
|
1020
1020
|
bb.command("add-comment").description("Add a comment to a pull request").requiredOption("--pr <pr-id>", "Pull request ID").requiredOption("--body <text>", "Comment text").action(async (opts) => {
|
|
1021
1021
|
const start = Date.now();
|
|
1022
1022
|
try {
|
|
1023
|
-
const { client, project, repo } = getClient2(
|
|
1023
|
+
const { client, project, repo } = getClient2(bb);
|
|
1024
1024
|
const data = await client.addComment(project, repo, parseInt(opts.pr, 10), opts.body);
|
|
1025
1025
|
success(data, "bitbucket", "add-comment", start);
|
|
1026
1026
|
} catch (err) {
|
|
@@ -1030,7 +1030,7 @@ function registerBitbucketCommands(program2) {
|
|
|
1030
1030
|
bb.command("add-inline-comment").description("Add an inline comment to a file in a pull request").requiredOption("--pr <pr-id>", "Pull request ID").requiredOption("--file <path>", "File path").requiredOption("--line <n>", "Line number").requiredOption("--body <text>", "Comment text").option("--line-type <type>", "Line type: ADDED|REMOVED|CONTEXT", "ADDED").action(async (opts) => {
|
|
1031
1031
|
const start = Date.now();
|
|
1032
1032
|
try {
|
|
1033
|
-
const { client, project, repo } = getClient2(
|
|
1033
|
+
const { client, project, repo } = getClient2(bb);
|
|
1034
1034
|
const data = await client.addInlineComment({
|
|
1035
1035
|
project,
|
|
1036
1036
|
repo,
|
|
@@ -1048,7 +1048,7 @@ function registerBitbucketCommands(program2) {
|
|
|
1048
1048
|
bb.command("reply-comment").description("Reply to a comment on a pull request").requiredOption("--pr <pr-id>", "Pull request ID").requiredOption("--comment-id <id>", "Comment ID to reply to").requiredOption("--body <text>", "Reply text").action(async (opts) => {
|
|
1049
1049
|
const start = Date.now();
|
|
1050
1050
|
try {
|
|
1051
|
-
const { client, project, repo } = getClient2(
|
|
1051
|
+
const { client, project, repo } = getClient2(bb);
|
|
1052
1052
|
const data = await client.replyComment(project, repo, parseInt(opts.pr, 10), parseInt(opts.commentId, 10), opts.body);
|
|
1053
1053
|
success(data, "bitbucket", "reply-comment", start);
|
|
1054
1054
|
} catch (err) {
|
|
@@ -1058,7 +1058,7 @@ function registerBitbucketCommands(program2) {
|
|
|
1058
1058
|
bb.command("resolve-comment").description("Resolve a comment on a pull request").requiredOption("--pr <pr-id>", "Pull request ID").requiredOption("--comment-id <id>", "Comment ID").option("--version <n>", "Comment version", "0").action(async (opts) => {
|
|
1059
1059
|
const start = Date.now();
|
|
1060
1060
|
try {
|
|
1061
|
-
const { client, project, repo } = getClient2(
|
|
1061
|
+
const { client, project, repo } = getClient2(bb);
|
|
1062
1062
|
await client.resolveComment(project, repo, parseInt(opts.pr, 10), parseInt(opts.commentId, 10), parseInt(opts.version ?? "0", 10));
|
|
1063
1063
|
success({ resolved: true }, "bitbucket", "resolve-comment", start);
|
|
1064
1064
|
} catch (err) {
|
|
@@ -1068,7 +1068,7 @@ function registerBitbucketCommands(program2) {
|
|
|
1068
1068
|
bb.command("delete-comment").description("Delete a comment on a pull request").requiredOption("--pr <pr-id>", "Pull request ID").requiredOption("--comment-id <id>", "Comment ID").option("--version <n>", "Comment version", "0").action(async (opts) => {
|
|
1069
1069
|
const start = Date.now();
|
|
1070
1070
|
try {
|
|
1071
|
-
const { client, project, repo } = getClient2(
|
|
1071
|
+
const { client, project, repo } = getClient2(bb);
|
|
1072
1072
|
await client.deleteComment(project, repo, parseInt(opts.pr, 10), parseInt(opts.commentId, 10), parseInt(opts.version ?? "0", 10));
|
|
1073
1073
|
success({ deleted: true }, "bitbucket", "delete-comment", start);
|
|
1074
1074
|
} catch (err) {
|
|
@@ -1078,7 +1078,7 @@ function registerBitbucketCommands(program2) {
|
|
|
1078
1078
|
bb.command("diff").description("Get unified diff for a pull request").requiredOption("--pr <pr-id>", "Pull request ID").option("--file <path>", "Limit diff to a specific file").option("--context-lines <n>", "Lines of context around changes").action(async (opts) => {
|
|
1079
1079
|
const start = Date.now();
|
|
1080
1080
|
try {
|
|
1081
|
-
const { client, project, repo } = getClient2(
|
|
1081
|
+
const { client, project, repo } = getClient2(bb);
|
|
1082
1082
|
const contextLines = opts.contextLines ? parseInt(opts.contextLines, 10) : void 0;
|
|
1083
1083
|
const diff = await client.getDiff(project, repo, parseInt(opts.pr, 10), opts.file, contextLines);
|
|
1084
1084
|
success({ diff }, "bitbucket", "diff", start);
|
|
@@ -1089,7 +1089,7 @@ function registerBitbucketCommands(program2) {
|
|
|
1089
1089
|
bb.command("list-files").description("List files changed in a pull request").requiredOption("--pr <pr-id>", "Pull request ID").action(async (opts) => {
|
|
1090
1090
|
const start = Date.now();
|
|
1091
1091
|
try {
|
|
1092
|
-
const { client, project, repo } = getClient2(
|
|
1092
|
+
const { client, project, repo } = getClient2(bb);
|
|
1093
1093
|
const data = await client.listFiles(project, repo, parseInt(opts.pr, 10));
|
|
1094
1094
|
success(data, "bitbucket", "list-files", start);
|
|
1095
1095
|
} catch (err) {
|
|
@@ -1099,7 +1099,7 @@ function registerBitbucketCommands(program2) {
|
|
|
1099
1099
|
bb.command("approve").description("Approve a pull request").requiredOption("--pr <pr-id>", "Pull request ID").action(async (opts) => {
|
|
1100
1100
|
const start = Date.now();
|
|
1101
1101
|
try {
|
|
1102
|
-
const { client, project, repo } = getClient2(
|
|
1102
|
+
const { client, project, repo } = getClient2(bb);
|
|
1103
1103
|
const data = await client.approvePR(project, repo, parseInt(opts.pr, 10));
|
|
1104
1104
|
success(data, "bitbucket", "approve", start);
|
|
1105
1105
|
} catch (err) {
|
|
@@ -1109,7 +1109,7 @@ function registerBitbucketCommands(program2) {
|
|
|
1109
1109
|
bb.command("unapprove").description("Remove approval from a pull request").requiredOption("--pr <pr-id>", "Pull request ID").action(async (opts) => {
|
|
1110
1110
|
const start = Date.now();
|
|
1111
1111
|
try {
|
|
1112
|
-
const { client, project, repo } = getClient2(
|
|
1112
|
+
const { client, project, repo } = getClient2(bb);
|
|
1113
1113
|
const data = await client.unapprovePR(project, repo, parseInt(opts.pr, 10));
|
|
1114
1114
|
success(data, "bitbucket", "unapprove", start);
|
|
1115
1115
|
} catch (err) {
|
|
@@ -1119,7 +1119,7 @@ function registerBitbucketCommands(program2) {
|
|
|
1119
1119
|
bb.command("needs-work").description("Mark a pull request as needs-work").requiredOption("--pr <pr-id>", "Pull request ID").action(async (opts) => {
|
|
1120
1120
|
const start = Date.now();
|
|
1121
1121
|
try {
|
|
1122
|
-
const { client, project, repo } = getClient2(
|
|
1122
|
+
const { client, project, repo } = getClient2(bb);
|
|
1123
1123
|
const data = await client.needsWorkPR(project, repo, parseInt(opts.pr, 10));
|
|
1124
1124
|
success(data, "bitbucket", "needs-work", start);
|
|
1125
1125
|
} catch (err) {
|
|
@@ -1129,7 +1129,7 @@ function registerBitbucketCommands(program2) {
|
|
|
1129
1129
|
bb.command("list-reviewers").description("List reviewers of a pull request").requiredOption("--pr <pr-id>", "Pull request ID").action(async (opts) => {
|
|
1130
1130
|
const start = Date.now();
|
|
1131
1131
|
try {
|
|
1132
|
-
const { client, project, repo } = getClient2(
|
|
1132
|
+
const { client, project, repo } = getClient2(bb);
|
|
1133
1133
|
const data = await client.listReviewers(project, repo, parseInt(opts.pr, 10));
|
|
1134
1134
|
success(data, "bitbucket", "list-reviewers", start);
|
|
1135
1135
|
} catch (err) {
|
|
@@ -1139,7 +1139,7 @@ function registerBitbucketCommands(program2) {
|
|
|
1139
1139
|
bb.command("list-builds").description("List build statuses for a pull request").requiredOption("--pr <pr-id>", "Pull request ID").action(async (opts) => {
|
|
1140
1140
|
const start = Date.now();
|
|
1141
1141
|
try {
|
|
1142
|
-
const { client, project, repo } = getClient2(
|
|
1142
|
+
const { client, project, repo } = getClient2(bb);
|
|
1143
1143
|
const data = await client.listBuilds(project, repo, parseInt(opts.pr, 10));
|
|
1144
1144
|
success(data, "bitbucket", "list-builds", start);
|
|
1145
1145
|
} catch (err) {
|
|
@@ -1149,7 +1149,7 @@ function registerBitbucketCommands(program2) {
|
|
|
1149
1149
|
bb.command("get-build-status").description("Get build status for a commit SHA").requiredOption("--commit <sha>", "Commit SHA").action(async (opts) => {
|
|
1150
1150
|
const start = Date.now();
|
|
1151
1151
|
try {
|
|
1152
|
-
const { client } = getClient2(
|
|
1152
|
+
const { client } = getClient2(bb);
|
|
1153
1153
|
const data = await client.getBuildStatus(opts.commit);
|
|
1154
1154
|
success(data, "bitbucket", "get-build-status", start);
|
|
1155
1155
|
} catch (err) {
|
|
@@ -1336,7 +1336,7 @@ function registerConfluenceCommands(program2) {
|
|
|
1336
1336
|
fail(err, "confluence", "get-page-by-title", start);
|
|
1337
1337
|
}
|
|
1338
1338
|
});
|
|
1339
|
-
confluence.command("list-pages").description("List pages in a Confluence space").requiredOption("--space <key>", "Space key").option("--limit <n>", "Max results per page (default: all)").option("--start <n>", "Offset for first result").action(async (opts) => {
|
|
1339
|
+
confluence.command("list-pages").description("List pages in a Confluence space (consider --output-file for large results)").requiredOption("--space <key>", "Space key").option("--limit <n>", "Max results per page (default: all)").option("--start <n>", "Offset for first result").action(async (opts) => {
|
|
1340
1340
|
const start = Date.now();
|
|
1341
1341
|
try {
|
|
1342
1342
|
const client = getClient3(program2);
|
|
@@ -1631,7 +1631,7 @@ function registerSonarCommands(program2) {
|
|
|
1631
1631
|
fail(err, "sonar", "quality-gate", start);
|
|
1632
1632
|
}
|
|
1633
1633
|
});
|
|
1634
|
-
sonar.command("issues").description("Search/list issues for a project").option("--project <key>", "SonarQube project key (or set defaults.sonar.project in config)").option("--severities <list>", "Filter by severity: BLOCKER,CRITICAL,MAJOR,MINOR,INFO (comma-separated)").option("--types <list>", "Filter by type: BUG,VULNERABILITY,CODE_SMELL (comma-separated)").option("--statuses <list>", "Filter by status: OPEN,CONFIRMED,REOPENED,RESOLVED,CLOSED (comma-separated)").option("--branch <name>", "Branch name").option("--resolved <bool>", "Filter resolved issues: true or false").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page (max 500)", "100").option("--all", "Fetch all pages (ignores --page/--page-size)").action(async (opts) => {
|
|
1634
|
+
sonar.command("issues").description("Search/list issues for a project").option("--project <key>", "SonarQube project key (or set defaults.sonar.project in config)").option("--severities <list>", "Filter by severity: BLOCKER,CRITICAL,MAJOR,MINOR,INFO (comma-separated)").option("--types <list>", "Filter by type: BUG,VULNERABILITY,CODE_SMELL (comma-separated)").option("--statuses <list>", "Filter by status: OPEN,CONFIRMED,REOPENED,RESOLVED,CLOSED (comma-separated)").option("--branch <name>", "Branch name").option("--resolved <bool>", "Filter resolved issues: true or false").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page (max 500)", "100").option("--all", "Fetch all pages (consider --output-file for large results; ignores --page/--page-size)").action(async (opts) => {
|
|
1635
1635
|
const start = Date.now();
|
|
1636
1636
|
try {
|
|
1637
1637
|
const { client, config } = getClient4(program2);
|
|
@@ -1674,7 +1674,7 @@ function registerSonarCommands(program2) {
|
|
|
1674
1674
|
fail(err, "sonar", "measures", start);
|
|
1675
1675
|
}
|
|
1676
1676
|
});
|
|
1677
|
-
sonar.command("projects").description("Search/list projects").option("--query <text>", "Search query").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page", "100").option("--all", "Fetch all pages").action(async (opts) => {
|
|
1677
|
+
sonar.command("projects").description("Search/list projects").option("--query <text>", "Search query").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page", "100").option("--all", "Fetch all pages (consider --output-file for large results)").action(async (opts) => {
|
|
1678
1678
|
const start = Date.now();
|
|
1679
1679
|
try {
|
|
1680
1680
|
const { client } = getClient4(program2);
|
|
@@ -1693,7 +1693,7 @@ function registerSonarCommands(program2) {
|
|
|
1693
1693
|
fail(err, "sonar", "projects", start);
|
|
1694
1694
|
}
|
|
1695
1695
|
});
|
|
1696
|
-
sonar.command("hotspots").description("List security hotspots for a project").option("--project <key>", "SonarQube project key (or set defaults.sonar.project in config)").option("--status <status>", "Filter: TO_REVIEW or REVIEWED").option("--resolution <list>", "Filter: FIXED,SAFE,ACKNOWLEDGED (comma-separated)").option("--branch <name>", "Branch name").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page", "100").option("--all", "Fetch all pages").action(async (opts) => {
|
|
1696
|
+
sonar.command("hotspots").description("List security hotspots for a project").option("--project <key>", "SonarQube project key (or set defaults.sonar.project in config)").option("--status <status>", "Filter: TO_REVIEW or REVIEWED").option("--resolution <list>", "Filter: FIXED,SAFE,ACKNOWLEDGED (comma-separated)").option("--branch <name>", "Branch name").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page", "100").option("--all", "Fetch all pages (consider --output-file for large results)").action(async (opts) => {
|
|
1697
1697
|
const start = Date.now();
|
|
1698
1698
|
try {
|
|
1699
1699
|
const { client, config } = getClient4(program2);
|
|
@@ -1901,7 +1901,7 @@ function registerSdeCommands(program2) {
|
|
|
1901
1901
|
fail(err, "sde", "whoami", start);
|
|
1902
1902
|
}
|
|
1903
1903
|
});
|
|
1904
|
-
sde.command("users").description("List users").option("--email <email>", "Filter by email address").option("--first-name <name>", "Filter by first name").option("--last-name <name>", "Filter by last name").option("--active <bool>", "Filter by active status: true or false").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page", "100").option("--all", "Fetch all pages").action(async (opts) => {
|
|
1904
|
+
sde.command("users").description("List users").option("--email <email>", "Filter by email address").option("--first-name <name>", "Filter by first name").option("--last-name <name>", "Filter by last name").option("--active <bool>", "Filter by active status: true or false").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page", "100").option("--all", "Fetch all pages (consider --output-file for large results)").action(async (opts) => {
|
|
1905
1905
|
const start = Date.now();
|
|
1906
1906
|
try {
|
|
1907
1907
|
const { client } = getClient5(program2);
|
|
@@ -1926,7 +1926,7 @@ function registerSdeCommands(program2) {
|
|
|
1926
1926
|
fail(err, "sde", "users", start);
|
|
1927
1927
|
}
|
|
1928
1928
|
});
|
|
1929
|
-
sde.command("projects").description("List projects").option("--name <name>", "Filter by project name").option("--search <text>", "Text search on name and profile").option("--active <val>", "Filter by active status: true, false, or all").option("--ordering <field>", "Sort by: name, created, updated (prefix with - for descending)").option("--expand <fields>", "Expand nested fields (comma-separated): application,business_unit,creator").option("--include <fields>", "Include extra fields (comma-separated): task_counts,permissions").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page", "100").option("--all", "Fetch all pages").action(async (opts) => {
|
|
1929
|
+
sde.command("projects").description("List projects").option("--name <name>", "Filter by project name").option("--search <text>", "Text search on name and profile").option("--active <val>", "Filter by active status: true, false, or all").option("--ordering <field>", "Sort by: name, created, updated (prefix with - for descending)").option("--expand <fields>", "Expand nested fields (comma-separated): application,business_unit,creator").option("--include <fields>", "Include extra fields (comma-separated): task_counts,permissions").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page", "100").option("--all", "Fetch all pages (consider --output-file for large results)").action(async (opts) => {
|
|
1930
1930
|
const start = Date.now();
|
|
1931
1931
|
try {
|
|
1932
1932
|
const { client } = getClient5(program2);
|
|
@@ -1964,7 +1964,7 @@ function registerSdeCommands(program2) {
|
|
|
1964
1964
|
fail(err, "sde", "project", start);
|
|
1965
1965
|
}
|
|
1966
1966
|
});
|
|
1967
|
-
sde.command("tasks").description("List countermeasures for a project").option("--project <id>", "Project ID (or set defaults.sde.project in config)").option("--phase <slug>", "Filter by phase slug (e.g. development, architecture-design)").option("--priority <n>", "Filter by priority (1-10)").option("--status <id>", "Filter by status ID (e.g. TS1, TS2)").option("--assigned-to <email>", "Filter by assignee email").option("--source <val>", "Filter by source: default, custom, manual, project").option("--verification <val>", "Filter by verification: pass, fail, partial, none").option("--tag <name>", "Filter by tag name").option("--accepted <bool>", "Filter by accepted status: true or false").option("--relevant <bool>", "Filter by relevant status: true or false").option("--expand <fields>", "Expand nested fields (comma-separated): status,phase,problem,text").option("--include <fields>", "Include extra fields (comma-separated): how_tos,last_note,references,regulation_sections").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page", "100").option("--all", "Fetch all pages").action(async (opts) => {
|
|
1967
|
+
sde.command("tasks").description("List countermeasures for a project").option("--project <id>", "Project ID (or set defaults.sde.project in config)").option("--phase <slug>", "Filter by phase slug (e.g. development, architecture-design)").option("--priority <n>", "Filter by priority (1-10)").option("--status <id>", "Filter by status ID (e.g. TS1, TS2)").option("--assigned-to <email>", "Filter by assignee email").option("--source <val>", "Filter by source: default, custom, manual, project").option("--verification <val>", "Filter by verification: pass, fail, partial, none").option("--tag <name>", "Filter by tag name").option("--accepted <bool>", "Filter by accepted status: true or false").option("--relevant <bool>", "Filter by relevant status: true or false").option("--expand <fields>", "Expand nested fields (comma-separated): status,phase,problem,text").option("--include <fields>", "Include extra fields (comma-separated): how_tos,last_note,references,regulation_sections").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page", "100").option("--all", "Fetch all pages (consider --output-file for large results)").action(async (opts) => {
|
|
1968
1968
|
const start = Date.now();
|
|
1969
1969
|
try {
|
|
1970
1970
|
const { client, config } = getClient5(program2);
|
|
@@ -2009,7 +2009,7 @@ function registerSdeCommands(program2) {
|
|
|
2009
2009
|
fail(err, "sde", "task", start);
|
|
2010
2010
|
}
|
|
2011
2011
|
});
|
|
2012
|
-
sde.command("threats").description("List threats for a project").option("--project <id>", "Project ID (or set defaults.sde.project in config)").option("--severity <n>", "Filter by severity (1-10)").option("--search <text>", "Full-text search on title and threat ID").option("--ordering <field>", "Sort by: threat__severity, threat_id, status (prefix - for descending)").option("--capec-id <id>", "Filter by CAPEC attack pattern ID").option("--component-id <id>", "Filter by component ID").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page", "100").option("--all", "Fetch all pages").action(async (opts) => {
|
|
2012
|
+
sde.command("threats").description("List threats for a project").option("--project <id>", "Project ID (or set defaults.sde.project in config)").option("--severity <n>", "Filter by severity (1-10)").option("--search <text>", "Full-text search on title and threat ID").option("--ordering <field>", "Sort by: threat__severity, threat_id, status (prefix - for descending)").option("--capec-id <id>", "Filter by CAPEC attack pattern ID").option("--component-id <id>", "Filter by component ID").option("--page <n>", "Page number (1-based)", "1").option("--page-size <n>", "Results per page", "100").option("--all", "Fetch all pages (consider --output-file for large results)").action(async (opts) => {
|
|
2013
2013
|
const start = Date.now();
|
|
2014
2014
|
try {
|
|
2015
2015
|
const { client, config } = getClient5(program2);
|
|
@@ -3603,7 +3603,7 @@ function registerConfigCommands(program2) {
|
|
|
3603
3603
|
if (cfg.ado.baseUrl) {
|
|
3604
3604
|
try {
|
|
3605
3605
|
const collection = cfg.defaults.ado?.collection;
|
|
3606
|
-
const path6 = collection ? `/${encodeURIComponent(collection)}/_apis/connectionData?api-version=7.1` : "/_apis/projectCollections?api-version=7.1";
|
|
3606
|
+
const path6 = collection ? `/${encodeURIComponent(collection)}/_apis/connectionData?api-version=7.1-preview.1` : "/_apis/projectCollections?api-version=7.1";
|
|
3607
3607
|
await http.ado(path6);
|
|
3608
3608
|
results.ado = { ok: true, message: "connected" };
|
|
3609
3609
|
} catch (err) {
|
|
@@ -3612,6 +3612,46 @@ function registerConfigCommands(program2) {
|
|
|
3612
3612
|
} else {
|
|
3613
3613
|
results.ado = { ok: null, message: "not configured" };
|
|
3614
3614
|
}
|
|
3615
|
+
if (cfg.jenkins.baseUrl && cfg.jenkins.username && cfg.jenkins.apiToken) {
|
|
3616
|
+
try {
|
|
3617
|
+
await http.jenkins("/api/json", { params: { tree: "nodeName" } });
|
|
3618
|
+
results.jenkins = { ok: true, message: "connected" };
|
|
3619
|
+
} catch (err) {
|
|
3620
|
+
results.jenkins = { ok: false, message: err instanceof Error ? err.message : String(err) };
|
|
3621
|
+
}
|
|
3622
|
+
} else {
|
|
3623
|
+
results.jenkins = { ok: null, message: "not configured" };
|
|
3624
|
+
}
|
|
3625
|
+
if (cfg.artifactory.baseUrl) {
|
|
3626
|
+
try {
|
|
3627
|
+
await http.artifactoryText("/api/system/ping");
|
|
3628
|
+
results.artifactory = { ok: true, message: "connected" };
|
|
3629
|
+
} catch (err) {
|
|
3630
|
+
results.artifactory = { ok: false, message: err instanceof Error ? err.message : String(err) };
|
|
3631
|
+
}
|
|
3632
|
+
} else {
|
|
3633
|
+
results.artifactory = { ok: null, message: "not configured" };
|
|
3634
|
+
}
|
|
3635
|
+
if (cfg.udeploy.baseUrl && (cfg.udeploy.pat || cfg.udeploy.username && cfg.udeploy.password)) {
|
|
3636
|
+
try {
|
|
3637
|
+
await http.udeploy("/cli/application");
|
|
3638
|
+
results.udeploy = { ok: true, message: "connected" };
|
|
3639
|
+
} catch (err) {
|
|
3640
|
+
results.udeploy = { ok: false, message: err instanceof Error ? err.message : String(err) };
|
|
3641
|
+
}
|
|
3642
|
+
} else {
|
|
3643
|
+
results.udeploy = { ok: null, message: "not configured" };
|
|
3644
|
+
}
|
|
3645
|
+
if (cfg.checkmarx.baseUrl && cfg.checkmarx.username && cfg.checkmarx.password) {
|
|
3646
|
+
try {
|
|
3647
|
+
await http.checkmarx("/cxrestapi/projects");
|
|
3648
|
+
results.checkmarx = { ok: true, message: "connected" };
|
|
3649
|
+
} catch (err) {
|
|
3650
|
+
results.checkmarx = { ok: false, message: err instanceof Error ? err.message : String(err) };
|
|
3651
|
+
}
|
|
3652
|
+
} else {
|
|
3653
|
+
results.checkmarx = { ok: null, message: "not configured" };
|
|
3654
|
+
}
|
|
3615
3655
|
success(results, "config", "test", start);
|
|
3616
3656
|
} catch (err) {
|
|
3617
3657
|
fail(err, "config", "test", start);
|
|
@@ -3705,13 +3745,37 @@ function registerConfigCommands(program2) {
|
|
|
3705
3745
|
} else {
|
|
3706
3746
|
try {
|
|
3707
3747
|
const collection = cfg.defaults.ado?.collection;
|
|
3708
|
-
const path6 = collection ? `/${encodeURIComponent(collection)}/_apis/connectionData?api-version=7.1` : "/_apis/projectCollections?api-version=7.1";
|
|
3748
|
+
const path6 = collection ? `/${encodeURIComponent(collection)}/_apis/connectionData?api-version=7.1-preview.1` : "/_apis/projectCollections?api-version=7.1";
|
|
3709
3749
|
await http.ado(path6, { timeoutMs: 1e4 });
|
|
3710
3750
|
results.ado = { status: "valid", message: "ok" };
|
|
3711
3751
|
} catch (err) {
|
|
3712
3752
|
results.ado = categorize2(err);
|
|
3713
3753
|
}
|
|
3714
3754
|
}
|
|
3755
|
+
if (!cfg.jenkins.apiToken || !cfg.jenkins.username) {
|
|
3756
|
+
results.jenkins = { status: "blank", message: "not configured" };
|
|
3757
|
+
} else if (!cfg.jenkins.baseUrl) {
|
|
3758
|
+
results.jenkins = { status: "error", message: "baseUrl not configured" };
|
|
3759
|
+
} else {
|
|
3760
|
+
try {
|
|
3761
|
+
await http.jenkins("/api/json", { params: { tree: "nodeName" }, timeoutMs: 1e4 });
|
|
3762
|
+
results.jenkins = { status: "valid", message: "ok" };
|
|
3763
|
+
} catch (err) {
|
|
3764
|
+
results.jenkins = categorize2(err);
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3767
|
+
if (!cfg.udeploy.pat && !(cfg.udeploy.username && cfg.udeploy.password)) {
|
|
3768
|
+
results.udeploy = { status: "blank", message: "not configured" };
|
|
3769
|
+
} else if (!cfg.udeploy.baseUrl) {
|
|
3770
|
+
results.udeploy = { status: "error", message: "baseUrl not configured" };
|
|
3771
|
+
} else {
|
|
3772
|
+
try {
|
|
3773
|
+
await http.udeploy("/cli/application", { timeoutMs: 1e4 });
|
|
3774
|
+
results.udeploy = { status: "valid", message: "ok" };
|
|
3775
|
+
} catch (err) {
|
|
3776
|
+
results.udeploy = categorize2(err);
|
|
3777
|
+
}
|
|
3778
|
+
}
|
|
3715
3779
|
const artResult = await checkArtifactoryConnectivity(cfg.artifactory);
|
|
3716
3780
|
if (!artResult.configured) {
|
|
3717
3781
|
results.artifactory = { status: "blank", message: artResult.error ?? "not configured" };
|
|
@@ -3722,11 +3786,23 @@ function registerConfigCommands(program2) {
|
|
|
3722
3786
|
} else {
|
|
3723
3787
|
results.artifactory = { status: "valid", message: "ok" };
|
|
3724
3788
|
}
|
|
3789
|
+
if (!cfg.checkmarx.username || !cfg.checkmarx.password) {
|
|
3790
|
+
results.checkmarx = { status: "blank", message: "not configured" };
|
|
3791
|
+
} else if (!cfg.checkmarx.baseUrl) {
|
|
3792
|
+
results.checkmarx = { status: "error", message: "baseUrl not configured" };
|
|
3793
|
+
} else {
|
|
3794
|
+
try {
|
|
3795
|
+
await http.checkmarx("/cxrestapi/projects", { timeoutMs: 1e4 });
|
|
3796
|
+
results.checkmarx = { status: "valid", message: "ok" };
|
|
3797
|
+
} catch (err) {
|
|
3798
|
+
results.checkmarx = categorize2(err);
|
|
3799
|
+
}
|
|
3800
|
+
}
|
|
3725
3801
|
const statuses = Object.values(results).map((r) => r.status);
|
|
3726
3802
|
if (statuses.includes("invalid")) process.exitCode = ExitCode.AUTH_ERROR;
|
|
3727
3803
|
else if (statuses.includes("error")) process.exitCode = ExitCode.NETWORK_ERROR;
|
|
3728
3804
|
if (cmdOpts.output === "table") {
|
|
3729
|
-
const services = ["jira", "bitbucket", "confluence", "sonar", "sde", "ado", "artifactory"];
|
|
3805
|
+
const services = ["jira", "bitbucket", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx"];
|
|
3730
3806
|
const labelWidth = 14;
|
|
3731
3807
|
const statusWidth = 9;
|
|
3732
3808
|
for (const svc of services) {
|
|
@@ -3740,7 +3816,7 @@ function registerConfigCommands(program2) {
|
|
|
3740
3816
|
process.stdout.write("\n");
|
|
3741
3817
|
} else {
|
|
3742
3818
|
if (opts.pretty) {
|
|
3743
|
-
const services = ["jira", "bitbucket", "confluence", "sonar", "sde", "ado", "artifactory"];
|
|
3819
|
+
const services = ["jira", "bitbucket", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx"];
|
|
3744
3820
|
const labelWidth = 14;
|
|
3745
3821
|
const statusWidth = 9;
|
|
3746
3822
|
for (const svc of services) {
|
|
@@ -3951,6 +4027,128 @@ async function initGlobalConfig(start) {
|
|
|
3951
4027
|
}
|
|
3952
4028
|
}
|
|
3953
4029
|
}
|
|
4030
|
+
process.stderr.write("\n\u2500\u2500 Jenkins \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
|
|
4031
|
+
const useJenkins = await confirm({
|
|
4032
|
+
message: "Configure Jenkins Data Center for pipeline operations?",
|
|
4033
|
+
default: false
|
|
4034
|
+
});
|
|
4035
|
+
let jenkinsBaseUrl = "";
|
|
4036
|
+
let jenkinsUsername = "";
|
|
4037
|
+
let jenkinsApiToken = "";
|
|
4038
|
+
if (useJenkins) {
|
|
4039
|
+
jenkinsBaseUrl = await input({
|
|
4040
|
+
message: "Jenkins base URL (e.g. https://jenkins.your-company.com):",
|
|
4041
|
+
default: ""
|
|
4042
|
+
});
|
|
4043
|
+
jenkinsUsername = await input({
|
|
4044
|
+
message: "Jenkins username:",
|
|
4045
|
+
default: ""
|
|
4046
|
+
});
|
|
4047
|
+
jenkinsApiToken = await password({
|
|
4048
|
+
message: "Jenkins API token:"
|
|
4049
|
+
});
|
|
4050
|
+
}
|
|
4051
|
+
process.stderr.write("\n\u2500\u2500 IBM UrbanCode Deploy \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
|
|
4052
|
+
const useUdeploy = await confirm({
|
|
4053
|
+
message: "Configure IBM UrbanCode Deploy for component imports and deployment processes?",
|
|
4054
|
+
default: false
|
|
4055
|
+
});
|
|
4056
|
+
let udeployBaseUrl = "";
|
|
4057
|
+
let udeployPat = "";
|
|
4058
|
+
let udeployUsername = "";
|
|
4059
|
+
let udeployPassword = "";
|
|
4060
|
+
let udeployDefaultApp = "";
|
|
4061
|
+
let udeployDefaultEnv = "";
|
|
4062
|
+
if (useUdeploy) {
|
|
4063
|
+
udeployBaseUrl = await input({
|
|
4064
|
+
message: "UDeploy base URL (e.g. https://ucd.company.com:8443):",
|
|
4065
|
+
default: ""
|
|
4066
|
+
});
|
|
4067
|
+
udeployUsername = await input({
|
|
4068
|
+
message: "UDeploy username (leave blank to use a PAT instead):",
|
|
4069
|
+
default: ""
|
|
4070
|
+
});
|
|
4071
|
+
if (udeployUsername) {
|
|
4072
|
+
udeployPassword = await password({
|
|
4073
|
+
message: "UDeploy password:",
|
|
4074
|
+
validate: (v) => v.length > 0 || "Password cannot be blank"
|
|
4075
|
+
});
|
|
4076
|
+
} else {
|
|
4077
|
+
udeployPat = await password({
|
|
4078
|
+
message: "UDeploy personal access token:"
|
|
4079
|
+
});
|
|
4080
|
+
}
|
|
4081
|
+
udeployDefaultApp = await input({
|
|
4082
|
+
message: "Default application name (optional):",
|
|
4083
|
+
default: ""
|
|
4084
|
+
});
|
|
4085
|
+
udeployDefaultEnv = await input({
|
|
4086
|
+
message: "Default environment name (optional):",
|
|
4087
|
+
default: ""
|
|
4088
|
+
});
|
|
4089
|
+
const hasCredentials = udeployBaseUrl && (udeployPat || udeployUsername && udeployPassword);
|
|
4090
|
+
if (hasCredentials) {
|
|
4091
|
+
process.stderr.write("\n Verifying connection...\n");
|
|
4092
|
+
try {
|
|
4093
|
+
const tempConfig = {
|
|
4094
|
+
...loadConfig(),
|
|
4095
|
+
udeploy: {
|
|
4096
|
+
baseUrl: udeployBaseUrl,
|
|
4097
|
+
pat: udeployPat || void 0,
|
|
4098
|
+
username: udeployUsername || void 0,
|
|
4099
|
+
password: udeployPassword || void 0
|
|
4100
|
+
}
|
|
4101
|
+
};
|
|
4102
|
+
const tempHttp = createHttpClient(tempConfig);
|
|
4103
|
+
await tempHttp.udeploy("/cli/application");
|
|
4104
|
+
process.stderr.write(" Connected.\n");
|
|
4105
|
+
} catch (err) {
|
|
4106
|
+
warn(`Could not connect to UDeploy: ${err instanceof Error ? err.message : String(err)}`);
|
|
4107
|
+
warn("Config will be saved anyway. Check your URL and credentials and re-run pncli config init or pncli config test.");
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
4110
|
+
}
|
|
4111
|
+
process.stderr.write("\n\u2500\u2500 Checkmarx \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
|
|
4112
|
+
const useCheckmarx = await confirm({
|
|
4113
|
+
message: "Configure Checkmarx CxSAST for vulnerability scanning?",
|
|
4114
|
+
default: false
|
|
4115
|
+
});
|
|
4116
|
+
let checkmarxBaseUrl = "";
|
|
4117
|
+
let checkmarxUsername = "";
|
|
4118
|
+
let checkmarxPassword = "";
|
|
4119
|
+
if (useCheckmarx) {
|
|
4120
|
+
checkmarxBaseUrl = await input({
|
|
4121
|
+
message: "Checkmarx base URL (e.g. https://cx.company.com):",
|
|
4122
|
+
default: ""
|
|
4123
|
+
});
|
|
4124
|
+
checkmarxUsername = await input({
|
|
4125
|
+
message: "Checkmarx username:",
|
|
4126
|
+
default: ""
|
|
4127
|
+
});
|
|
4128
|
+
checkmarxPassword = await password({
|
|
4129
|
+
message: "Checkmarx password:",
|
|
4130
|
+
validate: (v) => v.length > 0 || "Password cannot be blank"
|
|
4131
|
+
});
|
|
4132
|
+
if (checkmarxBaseUrl && checkmarxUsername && checkmarxPassword) {
|
|
4133
|
+
process.stderr.write("\n Verifying connection...\n");
|
|
4134
|
+
try {
|
|
4135
|
+
const tempConfig = {
|
|
4136
|
+
...loadConfig(),
|
|
4137
|
+
checkmarx: {
|
|
4138
|
+
baseUrl: checkmarxBaseUrl,
|
|
4139
|
+
username: checkmarxUsername,
|
|
4140
|
+
password: checkmarxPassword
|
|
4141
|
+
}
|
|
4142
|
+
};
|
|
4143
|
+
const tempHttp = createHttpClient(tempConfig);
|
|
4144
|
+
await tempHttp.checkmarx("/cxrestapi/projects");
|
|
4145
|
+
process.stderr.write(" Connected.\n");
|
|
4146
|
+
} catch (err) {
|
|
4147
|
+
warn(`Could not connect to Checkmarx: ${err instanceof Error ? err.message : String(err)}`);
|
|
4148
|
+
warn("Config will be saved anyway. Check your URL and credentials and re-run pncli config init or pncli config test.");
|
|
4149
|
+
}
|
|
4150
|
+
}
|
|
4151
|
+
}
|
|
3954
4152
|
process.stderr.write("\n\u2500\u2500 Defaults \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
|
|
3955
4153
|
const jiraProject = await input({
|
|
3956
4154
|
message: "Default Jira project key (optional):",
|
|
@@ -4022,6 +4220,28 @@ async function initGlobalConfig(start) {
|
|
|
4022
4220
|
...adoDiscoveredTypes.length ? { discoveredTypes: adoDiscoveredTypes } : {}
|
|
4023
4221
|
}
|
|
4024
4222
|
} : {},
|
|
4223
|
+
...useJenkins && jenkinsBaseUrl ? {
|
|
4224
|
+
jenkins: {
|
|
4225
|
+
baseUrl: jenkinsBaseUrl,
|
|
4226
|
+
...jenkinsUsername ? { username: jenkinsUsername } : {},
|
|
4227
|
+
...jenkinsApiToken ? { apiToken: jenkinsApiToken } : {}
|
|
4228
|
+
}
|
|
4229
|
+
} : {},
|
|
4230
|
+
...useUdeploy && udeployBaseUrl ? {
|
|
4231
|
+
udeploy: {
|
|
4232
|
+
baseUrl: udeployBaseUrl,
|
|
4233
|
+
...udeployPat ? { pat: udeployPat } : {},
|
|
4234
|
+
...udeployUsername ? { username: udeployUsername } : {},
|
|
4235
|
+
...udeployPassword ? { password: udeployPassword } : {}
|
|
4236
|
+
}
|
|
4237
|
+
} : {},
|
|
4238
|
+
...useCheckmarx && checkmarxBaseUrl ? {
|
|
4239
|
+
checkmarx: {
|
|
4240
|
+
baseUrl: checkmarxBaseUrl,
|
|
4241
|
+
username: checkmarxUsername || void 0,
|
|
4242
|
+
password: checkmarxPassword || void 0
|
|
4243
|
+
}
|
|
4244
|
+
} : {},
|
|
4025
4245
|
defaults: {
|
|
4026
4246
|
jira: {
|
|
4027
4247
|
project: jiraProject || void 0
|
|
@@ -4041,6 +4261,12 @@ async function initGlobalConfig(start) {
|
|
|
4041
4261
|
collection: adoCollection || void 0,
|
|
4042
4262
|
project: adoProject || void 0
|
|
4043
4263
|
}
|
|
4264
|
+
} : {},
|
|
4265
|
+
...useUdeploy && (udeployDefaultApp || udeployDefaultEnv) ? {
|
|
4266
|
+
udeploy: {
|
|
4267
|
+
application: udeployDefaultApp || void 0,
|
|
4268
|
+
environment: udeployDefaultEnv || void 0
|
|
4269
|
+
}
|
|
4044
4270
|
} : {}
|
|
4045
4271
|
}
|
|
4046
4272
|
});
|
|
@@ -4558,7 +4784,7 @@ function registerAdoWorkCommands(ado) {
|
|
|
4558
4784
|
fail(err, "ado", "work-link", start);
|
|
4559
4785
|
}
|
|
4560
4786
|
});
|
|
4561
|
-
work.command("search").description("Run a WIQL query").requiredOption("--wiql <query>", "WIQL query string").action(async (opts) => {
|
|
4787
|
+
work.command("search").description("Run a WIQL query (consider --output-file for large results)").requiredOption("--wiql <query>", "WIQL query string").action(async (opts) => {
|
|
4562
4788
|
const start = Date.now();
|
|
4563
4789
|
try {
|
|
4564
4790
|
const { collection, project, workClient } = getAdoContext(ado);
|
|
@@ -4617,7 +4843,7 @@ function registerAdoWorkCommands(ado) {
|
|
|
4617
4843
|
fail(err, "ado", "work-list-states", start);
|
|
4618
4844
|
}
|
|
4619
4845
|
});
|
|
4620
|
-
work.command("fields").description("List work item fields available in the collection").option("--type <type>", "Scope to fields for a specific work item type (e.g. Bug)").option("--custom-only", "Exclude System.* and Microsoft.VSTS.* fields").option("--discover", "Fetch from server (always true for this command)").option("--save", "Save discovered fields and aliases to ~/.pncli/config.json").action(async (opts) => {
|
|
4846
|
+
work.command("fields").description("List work item fields available in the collection (consider --output-file for large results)").option("--type <type>", "Scope to fields for a specific work item type (e.g. Bug)").option("--custom-only", "Exclude System.* and Microsoft.VSTS.* fields").option("--discover", "Fetch from server (always true for this command)").option("--save", "Save discovered fields and aliases to ~/.pncli/config.json").action(async (opts) => {
|
|
4621
4847
|
const start = Date.now();
|
|
4622
4848
|
try {
|
|
4623
4849
|
const { collection, project, workClient } = getAdoContext(ado);
|
|
@@ -4868,11 +5094,17 @@ function registerAdoRepoCommands(ado) {
|
|
|
4868
5094
|
fail(err, "ado", "repo-list-files", start);
|
|
4869
5095
|
}
|
|
4870
5096
|
});
|
|
4871
|
-
repo.command("diff").description("Show files changed in a pull request with change types and commit metadata").requiredOption("--pr <n>", "Pull request ID").action(async (opts) => {
|
|
5097
|
+
repo.command("diff").description("Show files changed in a pull request with change types and commit metadata").requiredOption("--pr <n>", "Pull request ID").option("--path <p>", "Filter diff to a specific file path").action(async (opts) => {
|
|
4872
5098
|
const start = Date.now();
|
|
4873
5099
|
try {
|
|
4874
5100
|
const { collection, project, repo: repo2, gitClient } = getAdoContext(ado, true);
|
|
4875
5101
|
const data = await gitClient.getPRDiff(collection, project, repo2, parseInt(opts.pr, 10));
|
|
5102
|
+
if (opts.path) {
|
|
5103
|
+
if (opts.path) {
|
|
5104
|
+
const needle = opts.path.startsWith("/") ? opts.path : `/${opts.path}`;
|
|
5105
|
+
data.changes = (data.changes ?? []).filter((c) => c.item.path === needle);
|
|
5106
|
+
}
|
|
5107
|
+
}
|
|
4876
5108
|
success(data, "ado", "repo-diff", start);
|
|
4877
5109
|
} catch (err) {
|
|
4878
5110
|
fail(err, "ado", "repo-diff", start);
|
|
@@ -4903,11 +5135,11 @@ function registerAdoRepoCommands(ado) {
|
|
|
4903
5135
|
const start = Date.now();
|
|
4904
5136
|
try {
|
|
4905
5137
|
const globalOpts = ado.optsWithGlobals();
|
|
4906
|
-
const { loadConfig: lc } = await import("./config-
|
|
5138
|
+
const { loadConfig: lc } = await import("./config-PXL632ZX.js");
|
|
4907
5139
|
const config = lc({ configPath: globalOpts.config });
|
|
4908
5140
|
const { collection, project, repo: repo2, gitClient } = getAdoContext(ado, true);
|
|
4909
|
-
const { AdoCoreClient: AdoCoreClient2 } = await import("./core-
|
|
4910
|
-
const { createHttpClient: createHttpClient2 } = await import("./http-
|
|
5141
|
+
const { AdoCoreClient: AdoCoreClient2 } = await import("./core-SWYVXOQ4.js");
|
|
5142
|
+
const { createHttpClient: createHttpClient2 } = await import("./http-TID3W235.js");
|
|
4911
5143
|
const http = createHttpClient2(config, Boolean(globalOpts.dryRun));
|
|
4912
5144
|
const coreClient = new AdoCoreClient2(http);
|
|
4913
5145
|
const me = await coreClient.getConnectionData(collection);
|
|
@@ -5035,7 +5267,7 @@ function registerAdoPipelineCommands(ado) {
|
|
|
5035
5267
|
fail(err, "ado", "pipeline-cancel-run", start);
|
|
5036
5268
|
}
|
|
5037
5269
|
});
|
|
5038
|
-
pipeline.command("logs").description("List or retrieve build logs").requiredOption("--id <n>", "Build ID").option("--log-id <n>", "Specific log ID (omit to list all logs)").action(async (opts) => {
|
|
5270
|
+
pipeline.command("logs").description("List or retrieve build logs (consider --output-file for large logs)").requiredOption("--id <n>", "Build ID").option("--log-id <n>", "Specific log ID (omit to list all logs)").action(async (opts) => {
|
|
5039
5271
|
const start = Date.now();
|
|
5040
5272
|
try {
|
|
5041
5273
|
const { collection, project, buildClient } = getAdoContext(ado);
|
|
@@ -5082,18 +5314,725 @@ function registerAdoCommands(program2) {
|
|
|
5082
5314
|
registerAdoPipelineCommands(ado);
|
|
5083
5315
|
}
|
|
5084
5316
|
|
|
5317
|
+
// src/services/jenkins/client.ts
|
|
5318
|
+
function jobPath(name) {
|
|
5319
|
+
return name.split("/").map(encodeURIComponent).join("/job/");
|
|
5320
|
+
}
|
|
5321
|
+
var JenkinsClient = class {
|
|
5322
|
+
constructor(http) {
|
|
5323
|
+
this.http = http;
|
|
5324
|
+
}
|
|
5325
|
+
http;
|
|
5326
|
+
async listJobs() {
|
|
5327
|
+
const data = await this.http.jenkins(
|
|
5328
|
+
"/api/json",
|
|
5329
|
+
{ params: { tree: "jobs[name,url,color,description,buildable,lastBuild[number,url,result]]" } }
|
|
5330
|
+
);
|
|
5331
|
+
return data.jobs;
|
|
5332
|
+
}
|
|
5333
|
+
async getJob(name) {
|
|
5334
|
+
return this.http.jenkins(
|
|
5335
|
+
`/job/${jobPath(name)}/api/json`,
|
|
5336
|
+
{ params: { tree: "name,url,color,description,buildable,nextBuildNumber,inQueue,lastBuild[number,url,result],builds[number,url,result,duration,timestamp,building,displayName]{0,10}" } }
|
|
5337
|
+
);
|
|
5338
|
+
}
|
|
5339
|
+
async triggerBuild(name, params) {
|
|
5340
|
+
const hasParams = params && Object.keys(params).length > 0;
|
|
5341
|
+
const endpoint = hasParams ? `/job/${jobPath(name)}/buildWithParameters` : `/job/${jobPath(name)}/build`;
|
|
5342
|
+
const res = await this.http.jenkinsRaw(endpoint, {
|
|
5343
|
+
method: "POST",
|
|
5344
|
+
params: hasParams ? params : void 0
|
|
5345
|
+
});
|
|
5346
|
+
const location = res.headers.get("Location") ?? "";
|
|
5347
|
+
const match = location.match(/\/queue\/item\/(\d+)\/?$/);
|
|
5348
|
+
if (!match) throw new PncliError("Build queued but Jenkins did not return a Location header \u2014 cannot track queue item");
|
|
5349
|
+
const queueItemId = parseInt(match[1], 10);
|
|
5350
|
+
return { queueItemId };
|
|
5351
|
+
}
|
|
5352
|
+
async getQueueItem(queueItemId) {
|
|
5353
|
+
return this.http.jenkins(`/queue/item/${queueItemId}/api/json`);
|
|
5354
|
+
}
|
|
5355
|
+
async listBuilds(name, top = 25) {
|
|
5356
|
+
const data = await this.http.jenkins(
|
|
5357
|
+
`/job/${jobPath(name)}/api/json`,
|
|
5358
|
+
{ params: { tree: `builds[number,url,result,duration,timestamp,building,displayName,fullDisplayName,description,id,queueId]{0,${top}}` } }
|
|
5359
|
+
);
|
|
5360
|
+
return data.builds ?? [];
|
|
5361
|
+
}
|
|
5362
|
+
async getBuild(name, buildNumber) {
|
|
5363
|
+
return this.http.jenkins(
|
|
5364
|
+
`/job/${jobPath(name)}/${buildNumber}/api/json`
|
|
5365
|
+
);
|
|
5366
|
+
}
|
|
5367
|
+
async getConsoleLog(name, buildNumber) {
|
|
5368
|
+
const res = await this.http.jenkinsRaw(
|
|
5369
|
+
`/job/${jobPath(name)}/${buildNumber}/consoleText`,
|
|
5370
|
+
{ method: "GET" }
|
|
5371
|
+
);
|
|
5372
|
+
return res.text;
|
|
5373
|
+
}
|
|
5374
|
+
};
|
|
5375
|
+
|
|
5376
|
+
// src/services/jenkins/commands.ts
|
|
5377
|
+
function getClient6(program2) {
|
|
5378
|
+
const opts = program2.optsWithGlobals();
|
|
5379
|
+
const config = loadConfig({ configPath: opts.config });
|
|
5380
|
+
if (!config.jenkins.baseUrl) throw new PncliError("Jenkins not configured. Run: pncli config init");
|
|
5381
|
+
const http = createHttpClient(config, Boolean(opts.dryRun));
|
|
5382
|
+
return new JenkinsClient(http);
|
|
5383
|
+
}
|
|
5384
|
+
async function pollQueueItem(client, queueItemId, pollMs, timeoutMs) {
|
|
5385
|
+
const deadline = Date.now() + timeoutMs;
|
|
5386
|
+
while (Date.now() < deadline) {
|
|
5387
|
+
const item = await client.getQueueItem(queueItemId);
|
|
5388
|
+
if (item.executable?.number !== void 0) return item.executable.number;
|
|
5389
|
+
if (item.why === null && !item.blocked && !item.buildable) {
|
|
5390
|
+
throw new PncliError("Build was cancelled while in queue", 1);
|
|
5391
|
+
}
|
|
5392
|
+
await new Promise((r) => setTimeout(r, pollMs));
|
|
5393
|
+
}
|
|
5394
|
+
throw new PncliError(`Timed out waiting for build to leave queue after ${timeoutMs / 1e3}s`, 1);
|
|
5395
|
+
}
|
|
5396
|
+
async function pollBuildComplete(client, jobName, buildNumber, pollMs, timeoutMs) {
|
|
5397
|
+
const deadline = Date.now() + timeoutMs;
|
|
5398
|
+
while (Date.now() < deadline) {
|
|
5399
|
+
const build = await client.getBuild(jobName, buildNumber);
|
|
5400
|
+
if (!build.building) return build;
|
|
5401
|
+
await new Promise((r) => setTimeout(r, pollMs));
|
|
5402
|
+
}
|
|
5403
|
+
throw new PncliError(`Timed out waiting for build #${buildNumber} to complete after ${timeoutMs / 1e3}s`, 1);
|
|
5404
|
+
}
|
|
5405
|
+
function registerJenkinsCommands(program2) {
|
|
5406
|
+
const jenkins = program2.command("jenkins").description("Jenkins Data Center operations");
|
|
5407
|
+
const pipeline = jenkins.command("pipeline").description("Pipeline (job/build) operations");
|
|
5408
|
+
pipeline.command("list").description("List all jobs on the Jenkins instance").action(async () => {
|
|
5409
|
+
const start = Date.now();
|
|
5410
|
+
try {
|
|
5411
|
+
const client = getClient6(program2);
|
|
5412
|
+
const data = await client.listJobs();
|
|
5413
|
+
success(data, "jenkins", "pipeline-list", start);
|
|
5414
|
+
} catch (err) {
|
|
5415
|
+
fail(err, "jenkins", "pipeline-list", start);
|
|
5416
|
+
}
|
|
5417
|
+
});
|
|
5418
|
+
pipeline.command("get").description("Get details for a specific job").requiredOption("--name <job>", "Job name").action(async (opts) => {
|
|
5419
|
+
const start = Date.now();
|
|
5420
|
+
try {
|
|
5421
|
+
const client = getClient6(program2);
|
|
5422
|
+
const data = await client.getJob(opts.name);
|
|
5423
|
+
success(data, "jenkins", "pipeline-get", start);
|
|
5424
|
+
} catch (err) {
|
|
5425
|
+
fail(err, "jenkins", "pipeline-get", start);
|
|
5426
|
+
}
|
|
5427
|
+
});
|
|
5428
|
+
pipeline.command("run").description("Trigger a build").requiredOption("--name <job>", "Job name").option("--parameter <k=v>", "Build parameter (repeatable)", (v, acc) => {
|
|
5429
|
+
acc.push(v);
|
|
5430
|
+
return acc;
|
|
5431
|
+
}, []).option("--wait", "Wait for the build to complete before returning").option("--timeout <s>", "Max wait time in seconds per phase (queue-wait and build-wait each get this budget; default 600)", "600").option("--poll <s>", "Poll interval in seconds (default 10)", "10").action(async (opts) => {
|
|
5432
|
+
const start = Date.now();
|
|
5433
|
+
try {
|
|
5434
|
+
const client = getClient6(program2);
|
|
5435
|
+
for (const p of opts.parameter) {
|
|
5436
|
+
if (!p.includes("=")) throw new PncliError(`Invalid --parameter "${p}". Expected format: key=value`, 1);
|
|
5437
|
+
}
|
|
5438
|
+
const params = opts.parameter.length > 0 ? Object.fromEntries(opts.parameter.map((p) => {
|
|
5439
|
+
const eq = p.indexOf("=");
|
|
5440
|
+
return [p.slice(0, eq), p.slice(eq + 1)];
|
|
5441
|
+
})) : void 0;
|
|
5442
|
+
const { queueItemId } = await client.triggerBuild(opts.name, params);
|
|
5443
|
+
log(`Queued build (queue item #${queueItemId})`);
|
|
5444
|
+
if (!opts.wait) {
|
|
5445
|
+
success({ queueItemId }, "jenkins", "pipeline-run", start);
|
|
5446
|
+
return;
|
|
5447
|
+
}
|
|
5448
|
+
const timeoutSec = parseInt(opts.timeout, 10);
|
|
5449
|
+
const pollSec = parseInt(opts.poll, 10);
|
|
5450
|
+
if (isNaN(timeoutSec) || timeoutSec <= 0) throw new PncliError(`Invalid --timeout "${opts.timeout}". Expected a positive number of seconds.`, 1);
|
|
5451
|
+
if (isNaN(pollSec) || pollSec <= 0) throw new PncliError(`Invalid --poll "${opts.poll}". Expected a positive number of seconds.`, 1);
|
|
5452
|
+
log(`Waiting for build to start (timeout ${timeoutSec}s)...`);
|
|
5453
|
+
const buildNumber = await pollQueueItem(client, queueItemId, pollSec * 1e3, timeoutSec * 1e3);
|
|
5454
|
+
log(`Build #${buildNumber} started \u2014 waiting for completion...`);
|
|
5455
|
+
const build = await pollBuildComplete(client, opts.name, buildNumber, pollSec * 1e3, timeoutSec * 1e3);
|
|
5456
|
+
if (build.result !== null && build.result !== "SUCCESS") {
|
|
5457
|
+
process.exitCode = ExitCode.GENERAL_ERROR;
|
|
5458
|
+
}
|
|
5459
|
+
success(build, "jenkins", "pipeline-run", start);
|
|
5460
|
+
} catch (err) {
|
|
5461
|
+
fail(err, "jenkins", "pipeline-run", start);
|
|
5462
|
+
}
|
|
5463
|
+
});
|
|
5464
|
+
pipeline.command("list-runs").description("List recent builds for a job").requiredOption("--name <job>", "Job name").option("--top <n>", "Maximum number of builds to return (default 25)", "25").action(async (opts) => {
|
|
5465
|
+
const start = Date.now();
|
|
5466
|
+
try {
|
|
5467
|
+
const client = getClient6(program2);
|
|
5468
|
+
const top = parseInt(opts.top, 10);
|
|
5469
|
+
if (isNaN(top) || top <= 0) throw new PncliError(`Invalid --top "${opts.top}". Expected a positive integer.`, 1);
|
|
5470
|
+
const data = await client.listBuilds(opts.name, top);
|
|
5471
|
+
success(data, "jenkins", "pipeline-list-runs", start);
|
|
5472
|
+
} catch (err) {
|
|
5473
|
+
fail(err, "jenkins", "pipeline-list-runs", start);
|
|
5474
|
+
}
|
|
5475
|
+
});
|
|
5476
|
+
pipeline.command("get-run").description("Get details for a specific build").requiredOption("--name <job>", "Job name").requiredOption("--number <n>", "Build number").action(async (opts) => {
|
|
5477
|
+
const start = Date.now();
|
|
5478
|
+
try {
|
|
5479
|
+
const client = getClient6(program2);
|
|
5480
|
+
const buildNumber = parseInt(opts.number, 10);
|
|
5481
|
+
if (isNaN(buildNumber)) throw new PncliError(`Invalid --number "${opts.number}". Expected an integer.`, 1);
|
|
5482
|
+
const data = await client.getBuild(opts.name, buildNumber);
|
|
5483
|
+
success(data, "jenkins", "pipeline-get-run", start);
|
|
5484
|
+
} catch (err) {
|
|
5485
|
+
fail(err, "jenkins", "pipeline-get-run", start);
|
|
5486
|
+
}
|
|
5487
|
+
});
|
|
5488
|
+
pipeline.command("logs").description("Fetch console log for a build (consider --output-file for large logs)").requiredOption("--name <job>", "Job name").requiredOption("--number <n>", "Build number").action(async (opts) => {
|
|
5489
|
+
const start = Date.now();
|
|
5490
|
+
try {
|
|
5491
|
+
const client = getClient6(program2);
|
|
5492
|
+
const buildNumber = parseInt(opts.number, 10);
|
|
5493
|
+
if (isNaN(buildNumber)) throw new PncliError(`Invalid --number "${opts.number}". Expected an integer.`, 1);
|
|
5494
|
+
const text = await client.getConsoleLog(opts.name, buildNumber);
|
|
5495
|
+
success({ log: text }, "jenkins", "pipeline-logs", start);
|
|
5496
|
+
} catch (err) {
|
|
5497
|
+
fail(err, "jenkins", "pipeline-logs", start);
|
|
5498
|
+
}
|
|
5499
|
+
});
|
|
5500
|
+
}
|
|
5501
|
+
|
|
5502
|
+
// src/services/artifactory/client.ts
|
|
5503
|
+
var API9 = "api";
|
|
5504
|
+
var ArtifactoryClient = class {
|
|
5505
|
+
constructor(http) {
|
|
5506
|
+
this.http = http;
|
|
5507
|
+
}
|
|
5508
|
+
http;
|
|
5509
|
+
async ping() {
|
|
5510
|
+
return this.http.artifactoryText(`${API9}/system/ping`);
|
|
5511
|
+
}
|
|
5512
|
+
async listRepos(opts = {}) {
|
|
5513
|
+
return this.http.artifactory(`${API9}/repositories`, {
|
|
5514
|
+
params: {
|
|
5515
|
+
type: opts.type,
|
|
5516
|
+
packageType: opts.packageType
|
|
5517
|
+
}
|
|
5518
|
+
});
|
|
5519
|
+
}
|
|
5520
|
+
async getStorageInfo(repo, path6) {
|
|
5521
|
+
const cleanPath = path6.startsWith("/") ? path6.slice(1) : path6;
|
|
5522
|
+
return this.http.artifactory(`${API9}/storage/${repo}/${cleanPath}`);
|
|
5523
|
+
}
|
|
5524
|
+
async getProperties(repo, path6) {
|
|
5525
|
+
const cleanPath = path6.startsWith("/") ? path6.slice(1) : path6;
|
|
5526
|
+
return this.http.artifactory(`${API9}/storage/${repo}/${cleanPath}`, {
|
|
5527
|
+
params: { properties: "" }
|
|
5528
|
+
});
|
|
5529
|
+
}
|
|
5530
|
+
async setProperties(repo, path6, properties, opts = {}) {
|
|
5531
|
+
const cleanPath = path6.startsWith("/") ? path6.slice(1) : path6;
|
|
5532
|
+
const propString = Object.entries(properties).map(([k, v]) => `${k}=${v}`).join(";");
|
|
5533
|
+
await this.http.artifactory(`${API9}/storage/${repo}/${cleanPath}`, {
|
|
5534
|
+
method: "PUT",
|
|
5535
|
+
params: {
|
|
5536
|
+
properties: propString,
|
|
5537
|
+
...opts.recursive ? { recursive: 1 } : {}
|
|
5538
|
+
}
|
|
5539
|
+
});
|
|
5540
|
+
}
|
|
5541
|
+
async searchAql(opts = {}) {
|
|
5542
|
+
const criteria = [];
|
|
5543
|
+
if (opts.repo) criteria.push(`"repo": {"$eq": "${opts.repo}"}`);
|
|
5544
|
+
if (opts.name) criteria.push(`"name": {"$match": "${opts.name}"}`);
|
|
5545
|
+
if (opts.path) criteria.push(`"path": {"$match": "${opts.path}"}`);
|
|
5546
|
+
if (opts.after) criteria.push(`"created": {"$gt": "${opts.after}"}`);
|
|
5547
|
+
if (opts.before) criteria.push(`"created": {"$lt": "${opts.before}"}`);
|
|
5548
|
+
const findClause = criteria.length > 0 ? `{${criteria.join(",")}}` : "{}";
|
|
5549
|
+
const includeFields = opts.includeProperties ? '.include("repo","path","name","type","size","created","modified","sha256","properties")' : '.include("repo","path","name","type","size","created","modified","sha256")';
|
|
5550
|
+
const sortClause = '.sort({"$desc": ["created"]})';
|
|
5551
|
+
const limitClause = opts.limit ? `.limit(${opts.limit})` : ".limit(100)";
|
|
5552
|
+
const aql = `items.find(${findClause})${includeFields}${sortClause}${limitClause}`;
|
|
5553
|
+
return this.http.artifactory(`${API9}/search/aql`, {
|
|
5554
|
+
method: "POST",
|
|
5555
|
+
body: aql,
|
|
5556
|
+
headers: { "Content-Type": "text/plain" }
|
|
5557
|
+
});
|
|
5558
|
+
}
|
|
5559
|
+
async listBuilds() {
|
|
5560
|
+
return this.http.artifactory(`${API9}/build`);
|
|
5561
|
+
}
|
|
5562
|
+
async listBuildRuns(buildName) {
|
|
5563
|
+
return this.http.artifactory(`${API9}/build/${encodeURIComponent(buildName)}`);
|
|
5564
|
+
}
|
|
5565
|
+
async getBuildInfo(buildName, buildNumber) {
|
|
5566
|
+
return this.http.artifactory(
|
|
5567
|
+
`${API9}/build/${encodeURIComponent(buildName)}/${encodeURIComponent(buildNumber)}`
|
|
5568
|
+
);
|
|
5569
|
+
}
|
|
5570
|
+
};
|
|
5571
|
+
|
|
5572
|
+
// src/services/artifactory/commands.ts
|
|
5573
|
+
function getClient7(program2) {
|
|
5574
|
+
const opts = program2.optsWithGlobals();
|
|
5575
|
+
const config = loadConfig({ configPath: opts.config });
|
|
5576
|
+
if (!config.artifactory.baseUrl) throw new PncliError("Artifactory not configured. Run: pncli config init");
|
|
5577
|
+
if (!config.artifactory.token) throw new PncliError("Artifactory token not configured. Run: pncli config init");
|
|
5578
|
+
const http = createHttpClient(config, Boolean(opts.dryRun));
|
|
5579
|
+
return new ArtifactoryClient(http);
|
|
5580
|
+
}
|
|
5581
|
+
function registerArtifactoryCommands(program2) {
|
|
5582
|
+
const art = program2.command("artifactory").description("Artifactory operations");
|
|
5583
|
+
art.command("ping").description("Check Artifactory connectivity and authentication").action(async () => {
|
|
5584
|
+
const start = Date.now();
|
|
5585
|
+
try {
|
|
5586
|
+
const client = getClient7(program2);
|
|
5587
|
+
const data = await client.ping();
|
|
5588
|
+
success(data, "artifactory", "ping", start);
|
|
5589
|
+
} catch (err) {
|
|
5590
|
+
fail(err, "artifactory", "ping", start);
|
|
5591
|
+
}
|
|
5592
|
+
});
|
|
5593
|
+
art.command("repos").description("List repositories").option("--type <type>", "Filter by type: local, virtual, remote, federated").option("--package-type <type>", "Filter by package type: npm, maven, nuget, docker, pypi, etc.").action(async (opts) => {
|
|
5594
|
+
const start = Date.now();
|
|
5595
|
+
try {
|
|
5596
|
+
const client = getClient7(program2);
|
|
5597
|
+
const data = await client.listRepos({ type: opts.type, packageType: opts.packageType });
|
|
5598
|
+
success(data, "artifactory", "repos", start);
|
|
5599
|
+
} catch (err) {
|
|
5600
|
+
fail(err, "artifactory", "repos", start);
|
|
5601
|
+
}
|
|
5602
|
+
});
|
|
5603
|
+
art.command("artifact-info <repo> <path>").description("Get metadata for an artifact or folder (checksums, size, timestamps)").action(async (repo, path6) => {
|
|
5604
|
+
const start = Date.now();
|
|
5605
|
+
try {
|
|
5606
|
+
const client = getClient7(program2);
|
|
5607
|
+
const data = await client.getStorageInfo(repo, path6);
|
|
5608
|
+
success(data, "artifactory", "artifact-info", start);
|
|
5609
|
+
} catch (err) {
|
|
5610
|
+
fail(err, "artifactory", "artifact-info", start);
|
|
5611
|
+
}
|
|
5612
|
+
});
|
|
5613
|
+
art.command("properties-get <repo> <path>").description("Read custom properties on an artifact").action(async (repo, path6) => {
|
|
5614
|
+
const start = Date.now();
|
|
5615
|
+
try {
|
|
5616
|
+
const client = getClient7(program2);
|
|
5617
|
+
const data = await client.getProperties(repo, path6);
|
|
5618
|
+
success(data, "artifactory", "properties-get", start);
|
|
5619
|
+
} catch (err) {
|
|
5620
|
+
fail(err, "artifactory", "properties-get", start);
|
|
5621
|
+
}
|
|
5622
|
+
});
|
|
5623
|
+
art.command("properties-set <repo> <path> [keyvals...]").description("Set custom properties on an artifact (format: key=value key=value...)").option("--recursive", "Apply recursively to folder contents", false).action(async (repo, path6, keyvals, opts) => {
|
|
5624
|
+
const start = Date.now();
|
|
5625
|
+
try {
|
|
5626
|
+
if (!keyvals.length) throw new PncliError("Provide at least one key=value pair");
|
|
5627
|
+
const properties = {};
|
|
5628
|
+
for (const kv of keyvals) {
|
|
5629
|
+
const eq = kv.indexOf("=");
|
|
5630
|
+
if (eq < 1) throw new PncliError(`Invalid property format: "${kv}" \u2014 expected key=value`);
|
|
5631
|
+
properties[kv.slice(0, eq)] = kv.slice(eq + 1);
|
|
5632
|
+
}
|
|
5633
|
+
const client = getClient7(program2);
|
|
5634
|
+
await client.setProperties(repo, path6, properties, { recursive: opts.recursive });
|
|
5635
|
+
success({ repo, path: path6, properties }, "artifactory", "properties-set", start);
|
|
5636
|
+
} catch (err) {
|
|
5637
|
+
fail(err, "artifactory", "properties-set", start);
|
|
5638
|
+
}
|
|
5639
|
+
});
|
|
5640
|
+
art.command("search").description("Search artifacts using AQL (Artifactory Query Language) (consider --output-file for large results)").option("--repo <name>", "Filter by repository key").option("--name <pattern>", "Filter by artifact name (supports * and ? wildcards)").option("--path <pattern>", "Filter by artifact path (supports * and ? wildcards)").option("--after <date>", "Created after this date (ISO 8601, e.g. 2024-01-01)").option("--before <date>", "Created before this date (ISO 8601)").option("--limit <n>", "Maximum results to return (default: 100)", "100").option("--properties", "Include artifact properties in results", false).action(async (opts) => {
|
|
5641
|
+
const start = Date.now();
|
|
5642
|
+
try {
|
|
5643
|
+
if (!opts.repo && !opts.name && !opts.path) {
|
|
5644
|
+
throw new PncliError("Provide at least one filter: --repo, --name, or --path");
|
|
5645
|
+
}
|
|
5646
|
+
const client = getClient7(program2);
|
|
5647
|
+
const data = await client.searchAql({
|
|
5648
|
+
repo: opts.repo,
|
|
5649
|
+
name: opts.name,
|
|
5650
|
+
path: opts.path,
|
|
5651
|
+
after: opts.after,
|
|
5652
|
+
before: opts.before,
|
|
5653
|
+
limit: Number.isNaN(parseInt(opts.limit, 10)) ? 100 : parseInt(opts.limit, 10),
|
|
5654
|
+
includeProperties: opts.properties
|
|
5655
|
+
});
|
|
5656
|
+
success(data, "artifactory", "search", start);
|
|
5657
|
+
} catch (err) {
|
|
5658
|
+
fail(err, "artifactory", "search", start);
|
|
5659
|
+
}
|
|
5660
|
+
});
|
|
5661
|
+
art.command("builds").description("List all build names in Artifactory").action(async () => {
|
|
5662
|
+
const start = Date.now();
|
|
5663
|
+
try {
|
|
5664
|
+
const client = getClient7(program2);
|
|
5665
|
+
const data = await client.listBuilds();
|
|
5666
|
+
success(data, "artifactory", "builds", start);
|
|
5667
|
+
} catch (err) {
|
|
5668
|
+
fail(err, "artifactory", "builds", start);
|
|
5669
|
+
}
|
|
5670
|
+
});
|
|
5671
|
+
art.command("build-runs <name>").description("List all run numbers for a build").action(async (name) => {
|
|
5672
|
+
const start = Date.now();
|
|
5673
|
+
try {
|
|
5674
|
+
const client = getClient7(program2);
|
|
5675
|
+
const data = await client.listBuildRuns(name);
|
|
5676
|
+
success(data, "artifactory", "build-runs", start);
|
|
5677
|
+
} catch (err) {
|
|
5678
|
+
fail(err, "artifactory", "build-runs", start);
|
|
5679
|
+
}
|
|
5680
|
+
});
|
|
5681
|
+
art.command("build-info <name> <number>").description("Get build info (artifacts, dependencies, VCS revision) for a specific run").action(async (name, number) => {
|
|
5682
|
+
const start = Date.now();
|
|
5683
|
+
try {
|
|
5684
|
+
const client = getClient7(program2);
|
|
5685
|
+
const data = await client.getBuildInfo(name, number);
|
|
5686
|
+
success(data, "artifactory", "build-info", start);
|
|
5687
|
+
} catch (err) {
|
|
5688
|
+
fail(err, "artifactory", "build-info", start);
|
|
5689
|
+
}
|
|
5690
|
+
});
|
|
5691
|
+
}
|
|
5692
|
+
|
|
5693
|
+
// src/services/udeploy/client.ts
|
|
5694
|
+
var UdeployClient = class {
|
|
5695
|
+
constructor(http) {
|
|
5696
|
+
this.http = http;
|
|
5697
|
+
}
|
|
5698
|
+
http;
|
|
5699
|
+
listApplications() {
|
|
5700
|
+
return this.http.udeploy("/cli/application");
|
|
5701
|
+
}
|
|
5702
|
+
listEnvironments(application) {
|
|
5703
|
+
return this.http.udeploy("/cli/application/environmentsInApplication", {
|
|
5704
|
+
params: { application }
|
|
5705
|
+
});
|
|
5706
|
+
}
|
|
5707
|
+
listComponents() {
|
|
5708
|
+
return this.http.udeploy("/cli/component");
|
|
5709
|
+
}
|
|
5710
|
+
listVersions(component) {
|
|
5711
|
+
return this.http.udeploy("/cli/component/versions", {
|
|
5712
|
+
params: { component }
|
|
5713
|
+
});
|
|
5714
|
+
}
|
|
5715
|
+
createVersion(component, name) {
|
|
5716
|
+
return this.http.udeploy("/cli/version/createVersion", {
|
|
5717
|
+
method: "POST",
|
|
5718
|
+
params: { component, name }
|
|
5719
|
+
});
|
|
5720
|
+
}
|
|
5721
|
+
finishImporting(component, version) {
|
|
5722
|
+
return this.http.udeploy("/cli/version/finishedImporting", {
|
|
5723
|
+
method: "POST",
|
|
5724
|
+
params: { component, version }
|
|
5725
|
+
});
|
|
5726
|
+
}
|
|
5727
|
+
runProcess(opts) {
|
|
5728
|
+
return this.http.udeploy("/cli/applicationProcessRequest/request", {
|
|
5729
|
+
method: "PUT",
|
|
5730
|
+
body: {
|
|
5731
|
+
application: opts.application,
|
|
5732
|
+
applicationProcess: opts.applicationProcess,
|
|
5733
|
+
environment: opts.environment,
|
|
5734
|
+
onlyChanged: opts.onlyChanged ?? false,
|
|
5735
|
+
...opts.versions ? { versions: opts.versions } : {},
|
|
5736
|
+
...opts.snapshot ? { snapshot: opts.snapshot } : {},
|
|
5737
|
+
...opts.properties ? { properties: opts.properties } : {}
|
|
5738
|
+
}
|
|
5739
|
+
});
|
|
5740
|
+
}
|
|
5741
|
+
getRequestStatus(requestId) {
|
|
5742
|
+
return this.http.udeploy("/cli/applicationProcessRequest/requestStatus", {
|
|
5743
|
+
params: { requestId }
|
|
5744
|
+
});
|
|
5745
|
+
}
|
|
5746
|
+
getRequestInfo(requestId) {
|
|
5747
|
+
return this.http.udeploy("/cli/applicationProcessRequest/requestInfo", {
|
|
5748
|
+
params: { requestId }
|
|
5749
|
+
});
|
|
5750
|
+
}
|
|
5751
|
+
};
|
|
5752
|
+
|
|
5753
|
+
// src/services/udeploy/commands.ts
|
|
5754
|
+
var POLL_INTERVAL_MS = 5e3;
|
|
5755
|
+
var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
5756
|
+
function getClient8(program2) {
|
|
5757
|
+
const opts = program2.optsWithGlobals();
|
|
5758
|
+
const config = loadConfig({ configPath: opts.config });
|
|
5759
|
+
if (!config.udeploy.baseUrl) {
|
|
5760
|
+
throw new PncliError("UDeploy baseUrl not configured. Run: pncli config init", 78);
|
|
5761
|
+
}
|
|
5762
|
+
const http = createHttpClient(config, opts.dryRun);
|
|
5763
|
+
return { client: new UdeployClient(http), config };
|
|
5764
|
+
}
|
|
5765
|
+
function resolveApplication(config, cliValue) {
|
|
5766
|
+
const app = cliValue ?? config.defaults.udeploy.application;
|
|
5767
|
+
if (!app) throw new PncliError("--application is required (or set defaults.udeploy.application in config)", 2);
|
|
5768
|
+
return app;
|
|
5769
|
+
}
|
|
5770
|
+
function resolveEnvironment(config, cliValue) {
|
|
5771
|
+
const env = cliValue ?? config.defaults.udeploy.environment;
|
|
5772
|
+
if (!env) throw new PncliError("--environment is required (or set defaults.udeploy.environment in config)", 2);
|
|
5773
|
+
return env;
|
|
5774
|
+
}
|
|
5775
|
+
function registerUdeployCommands(program2) {
|
|
5776
|
+
const udeploy = program2.command("udeploy").description("IBM UrbanCode Deploy \u2014 component versions and deployment processes").option("--application <name>", "Application name or ID").option("--environment <name>", "Environment name or ID");
|
|
5777
|
+
udeploy.command("apps").description("List all applications").action(async () => {
|
|
5778
|
+
const start = Date.now();
|
|
5779
|
+
try {
|
|
5780
|
+
const { client } = getClient8(program2);
|
|
5781
|
+
const apps = await client.listApplications();
|
|
5782
|
+
success(apps, "udeploy", "apps", start);
|
|
5783
|
+
} catch (err) {
|
|
5784
|
+
fail(err, "udeploy", "apps", start);
|
|
5785
|
+
}
|
|
5786
|
+
});
|
|
5787
|
+
udeploy.command("environments").description("List environments for an application").action(async () => {
|
|
5788
|
+
const start = Date.now();
|
|
5789
|
+
try {
|
|
5790
|
+
const { client, config } = getClient8(program2);
|
|
5791
|
+
const parentOpts = udeploy.opts();
|
|
5792
|
+
const application = resolveApplication(config, parentOpts.application);
|
|
5793
|
+
const envs = await client.listEnvironments(application);
|
|
5794
|
+
success(envs, "udeploy", "environments", start);
|
|
5795
|
+
} catch (err) {
|
|
5796
|
+
fail(err, "udeploy", "environments", start);
|
|
5797
|
+
}
|
|
5798
|
+
});
|
|
5799
|
+
udeploy.command("components").description("List all components").action(async () => {
|
|
5800
|
+
const start = Date.now();
|
|
5801
|
+
try {
|
|
5802
|
+
const { client } = getClient8(program2);
|
|
5803
|
+
const components = await client.listComponents();
|
|
5804
|
+
success(components, "udeploy", "components", start);
|
|
5805
|
+
} catch (err) {
|
|
5806
|
+
fail(err, "udeploy", "components", start);
|
|
5807
|
+
}
|
|
5808
|
+
});
|
|
5809
|
+
udeploy.command("versions").description("List versions for a component").requiredOption("--component <name>", "Component name or ID").action(async (cmdOpts) => {
|
|
5810
|
+
const start = Date.now();
|
|
5811
|
+
try {
|
|
5812
|
+
const { client } = getClient8(program2);
|
|
5813
|
+
const versions = await client.listVersions(cmdOpts.component);
|
|
5814
|
+
success(versions, "udeploy", "versions", start);
|
|
5815
|
+
} catch (err) {
|
|
5816
|
+
fail(err, "udeploy", "versions", start);
|
|
5817
|
+
}
|
|
5818
|
+
});
|
|
5819
|
+
udeploy.command("import-version").description("Create a component version and mark it ready for deployment").requiredOption("--component <name>", "Component name or ID").requiredOption("--name <name>", "Version name").option("--no-finish", "Skip marking the version as finished importing").action(async (cmdOpts) => {
|
|
5820
|
+
const start = Date.now();
|
|
5821
|
+
try {
|
|
5822
|
+
const { client } = getClient8(program2);
|
|
5823
|
+
const created = await client.createVersion(cmdOpts.component, cmdOpts.name);
|
|
5824
|
+
if (cmdOpts.finish) {
|
|
5825
|
+
await client.finishImporting(cmdOpts.component, cmdOpts.name);
|
|
5826
|
+
}
|
|
5827
|
+
success({ ...created, finishedImporting: cmdOpts.finish }, "udeploy", "import-version", start);
|
|
5828
|
+
} catch (err) {
|
|
5829
|
+
fail(err, "udeploy", "import-version", start);
|
|
5830
|
+
}
|
|
5831
|
+
});
|
|
5832
|
+
udeploy.command("run").description("Run an application deployment process").requiredOption("--process <name>", "Application process name or ID").option("--component <name>", "Component name (repeatable)", collect, []).option("--component-version <name>", "Component version (repeatable; positionally paired with --component)", collect, []).option("--snapshot <name>", "Snapshot name or ID (alternative to specifying versions)").option("--only-changed", "Deploy only changed components", false).option("--wait", "Poll until the process completes", false).option("--timeout <ms>", "Max wait time in milliseconds", String(DEFAULT_TIMEOUT_MS)).action(async (cmdOpts) => {
|
|
5833
|
+
const start = Date.now();
|
|
5834
|
+
try {
|
|
5835
|
+
const { client, config } = getClient8(program2);
|
|
5836
|
+
const parentOpts = udeploy.opts();
|
|
5837
|
+
const application = resolveApplication(config, parentOpts.application);
|
|
5838
|
+
const environment = resolveEnvironment(config, parentOpts.environment);
|
|
5839
|
+
const components = cmdOpts.component;
|
|
5840
|
+
const versions = cmdOpts.componentVersion;
|
|
5841
|
+
const versionEntries = components.map((c, i) => ({
|
|
5842
|
+
component: c,
|
|
5843
|
+
version: versions[i] ?? "latest"
|
|
5844
|
+
}));
|
|
5845
|
+
const result = await client.runProcess({
|
|
5846
|
+
application,
|
|
5847
|
+
applicationProcess: cmdOpts.process,
|
|
5848
|
+
environment,
|
|
5849
|
+
onlyChanged: cmdOpts.onlyChanged,
|
|
5850
|
+
...versionEntries.length > 0 ? { versions: versionEntries } : {},
|
|
5851
|
+
...cmdOpts.snapshot ? { snapshot: cmdOpts.snapshot } : {}
|
|
5852
|
+
});
|
|
5853
|
+
if (!cmdOpts.wait) {
|
|
5854
|
+
success(result, "udeploy", "run", start);
|
|
5855
|
+
return;
|
|
5856
|
+
}
|
|
5857
|
+
const timeoutMs = parseInt(cmdOpts.timeout, 10);
|
|
5858
|
+
const requestId = result.requestId;
|
|
5859
|
+
log(`Process started. Request ID: ${requestId}. Polling for completion...`);
|
|
5860
|
+
while (true) {
|
|
5861
|
+
const elapsed = Date.now() - start;
|
|
5862
|
+
if (elapsed >= timeoutMs) {
|
|
5863
|
+
throw new PncliError(`Timed out after ${timeoutMs}ms waiting for request ${requestId}`, 1);
|
|
5864
|
+
}
|
|
5865
|
+
await sleep(POLL_INTERVAL_MS);
|
|
5866
|
+
const status = await client.getRequestStatus(requestId);
|
|
5867
|
+
if (status.status === "CLOSED") {
|
|
5868
|
+
const outcome = { requestId, status: status.status, result: status.result, elapsed_ms: Date.now() - start };
|
|
5869
|
+
if (status.result === "SUCCEEDED") {
|
|
5870
|
+
success(outcome, "udeploy", "run", start);
|
|
5871
|
+
} else {
|
|
5872
|
+
throw new PncliError(
|
|
5873
|
+
`Deployment ${status.result ?? "failed"} (requestId: ${requestId})`,
|
|
5874
|
+
1
|
|
5875
|
+
);
|
|
5876
|
+
}
|
|
5877
|
+
return;
|
|
5878
|
+
}
|
|
5879
|
+
log(`Status: ${status.status} (${Math.round((Date.now() - start) / 1e3)}s elapsed)`);
|
|
5880
|
+
}
|
|
5881
|
+
} catch (err) {
|
|
5882
|
+
fail(err, "udeploy", "run", start);
|
|
5883
|
+
}
|
|
5884
|
+
});
|
|
5885
|
+
udeploy.command("request-status").description("Get the current status of a deployment process request").requiredOption("--request-id <id>", "Request ID returned by udeploy run").action(async (cmdOpts) => {
|
|
5886
|
+
const start = Date.now();
|
|
5887
|
+
try {
|
|
5888
|
+
const { client } = getClient8(program2);
|
|
5889
|
+
const status = await client.getRequestStatus(cmdOpts.requestId);
|
|
5890
|
+
success(status, "udeploy", "request-status", start);
|
|
5891
|
+
} catch (err) {
|
|
5892
|
+
fail(err, "udeploy", "request-status", start);
|
|
5893
|
+
}
|
|
5894
|
+
});
|
|
5895
|
+
udeploy.command("request-info").description("Get full details of a deployment process request").requiredOption("--request-id <id>", "Request ID returned by udeploy run").action(async (cmdOpts) => {
|
|
5896
|
+
const start = Date.now();
|
|
5897
|
+
try {
|
|
5898
|
+
const { client } = getClient8(program2);
|
|
5899
|
+
const info = await client.getRequestInfo(cmdOpts.requestId);
|
|
5900
|
+
success(info, "udeploy", "request-info", start);
|
|
5901
|
+
} catch (err) {
|
|
5902
|
+
fail(err, "udeploy", "request-info", start);
|
|
5903
|
+
}
|
|
5904
|
+
});
|
|
5905
|
+
}
|
|
5906
|
+
function collect(val, prev) {
|
|
5907
|
+
return [...prev, val];
|
|
5908
|
+
}
|
|
5909
|
+
function sleep(ms) {
|
|
5910
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5911
|
+
}
|
|
5912
|
+
|
|
5913
|
+
// src/services/checkmarx/client.ts
|
|
5914
|
+
var CheckmarxClient = class {
|
|
5915
|
+
constructor(http) {
|
|
5916
|
+
this.http = http;
|
|
5917
|
+
}
|
|
5918
|
+
http;
|
|
5919
|
+
async listProjects() {
|
|
5920
|
+
return this.http.checkmarx("/cxrestapi/projects");
|
|
5921
|
+
}
|
|
5922
|
+
async getProject(id) {
|
|
5923
|
+
return this.http.checkmarx(`/cxrestapi/projects/${id}`);
|
|
5924
|
+
}
|
|
5925
|
+
async listScans(opts = {}) {
|
|
5926
|
+
return this.http.checkmarx("/cxrestapi/sast/scans", {
|
|
5927
|
+
params: {
|
|
5928
|
+
projectId: opts.projectId,
|
|
5929
|
+
last: opts.last
|
|
5930
|
+
}
|
|
5931
|
+
});
|
|
5932
|
+
}
|
|
5933
|
+
async getScan(id) {
|
|
5934
|
+
return this.http.checkmarx(`/cxrestapi/sast/scans/${id}`);
|
|
5935
|
+
}
|
|
5936
|
+
async getScanResultsStatistics(scanId) {
|
|
5937
|
+
return this.http.checkmarx(`/cxrestapi/sast/scans/${scanId}/resultsStatistics`);
|
|
5938
|
+
}
|
|
5939
|
+
};
|
|
5940
|
+
|
|
5941
|
+
// src/services/checkmarx/commands.ts
|
|
5942
|
+
function getClient9(program2) {
|
|
5943
|
+
const opts = program2.optsWithGlobals();
|
|
5944
|
+
const config = loadConfig({ configPath: opts.config });
|
|
5945
|
+
if (!config.checkmarx.baseUrl) throw new PncliError("Checkmarx not configured. Run: pncli config init");
|
|
5946
|
+
const http = createHttpClient(config, Boolean(opts.dryRun));
|
|
5947
|
+
return new CheckmarxClient(http);
|
|
5948
|
+
}
|
|
5949
|
+
function registerCheckmarxCommands(program2) {
|
|
5950
|
+
const cx = program2.command("checkmarx").description("Checkmarx CxSAST operations");
|
|
5951
|
+
const project = cx.command("project").description("Project operations");
|
|
5952
|
+
const scan = cx.command("scan").description("Scan operations");
|
|
5953
|
+
project.command("list").description("List all projects").action(async () => {
|
|
5954
|
+
const start = Date.now();
|
|
5955
|
+
try {
|
|
5956
|
+
const data = await getClient9(program2).listProjects();
|
|
5957
|
+
success(data, "checkmarx", "project-list", start);
|
|
5958
|
+
} catch (err) {
|
|
5959
|
+
fail(err, "checkmarx", "project-list", start);
|
|
5960
|
+
}
|
|
5961
|
+
});
|
|
5962
|
+
project.command("get").description("Get a project by ID").requiredOption("--id <id>", "Project ID").action(async (opts) => {
|
|
5963
|
+
const start = Date.now();
|
|
5964
|
+
try {
|
|
5965
|
+
const id = parseInt(opts.id, 10);
|
|
5966
|
+
if (isNaN(id)) throw new PncliError(`Invalid --id "${opts.id}". Expected an integer.`, 1);
|
|
5967
|
+
const data = await getClient9(program2).getProject(id);
|
|
5968
|
+
success(data, "checkmarx", "project-get", start);
|
|
5969
|
+
} catch (err) {
|
|
5970
|
+
fail(err, "checkmarx", "project-get", start);
|
|
5971
|
+
}
|
|
5972
|
+
});
|
|
5973
|
+
scan.command("list").description("List scans").option("--project <id>", "Filter by project ID").option("--last <n>", "Return only the last N scans per project").action(async (opts) => {
|
|
5974
|
+
const start = Date.now();
|
|
5975
|
+
try {
|
|
5976
|
+
const projectId = opts.project ? parseInt(opts.project, 10) : void 0;
|
|
5977
|
+
const last = opts.last ? parseInt(opts.last, 10) : void 0;
|
|
5978
|
+
if (opts.project && isNaN(projectId)) throw new PncliError(`Invalid --project "${opts.project}". Expected an integer.`, 1);
|
|
5979
|
+
if (opts.last && isNaN(last)) throw new PncliError(`Invalid --last "${opts.last}". Expected an integer.`, 1);
|
|
5980
|
+
const data = await getClient9(program2).listScans({ projectId, last });
|
|
5981
|
+
success(data, "checkmarx", "scan-list", start);
|
|
5982
|
+
} catch (err) {
|
|
5983
|
+
fail(err, "checkmarx", "scan-list", start);
|
|
5984
|
+
}
|
|
5985
|
+
});
|
|
5986
|
+
scan.command("get").description("Get a scan by ID").requiredOption("--id <id>", "Scan ID").action(async (opts) => {
|
|
5987
|
+
const start = Date.now();
|
|
5988
|
+
try {
|
|
5989
|
+
const id = parseInt(opts.id, 10);
|
|
5990
|
+
if (isNaN(id)) throw new PncliError(`Invalid --id "${opts.id}". Expected an integer.`, 1);
|
|
5991
|
+
const data = await getClient9(program2).getScan(id);
|
|
5992
|
+
success(data, "checkmarx", "scan-get", start);
|
|
5993
|
+
} catch (err) {
|
|
5994
|
+
fail(err, "checkmarx", "scan-get", start);
|
|
5995
|
+
}
|
|
5996
|
+
});
|
|
5997
|
+
scan.command("stats").description("Get results statistics for a scan").requiredOption("--id <id>", "Scan ID").action(async (opts) => {
|
|
5998
|
+
const start = Date.now();
|
|
5999
|
+
try {
|
|
6000
|
+
const id = parseInt(opts.id, 10);
|
|
6001
|
+
if (isNaN(id)) throw new PncliError(`Invalid --id "${opts.id}". Expected an integer.`, 1);
|
|
6002
|
+
const data = await getClient9(program2).getScanResultsStatistics(id);
|
|
6003
|
+
success(data, "checkmarx", "scan-stats", start);
|
|
6004
|
+
} catch (err) {
|
|
6005
|
+
fail(err, "checkmarx", "scan-stats", start);
|
|
6006
|
+
}
|
|
6007
|
+
});
|
|
6008
|
+
}
|
|
6009
|
+
|
|
5085
6010
|
// src/services/skills/commands.ts
|
|
5086
6011
|
import fs3 from "fs";
|
|
5087
6012
|
import path5 from "path";
|
|
5088
|
-
|
|
5089
|
-
var
|
|
5090
|
-
var
|
|
6013
|
+
import os from "os";
|
|
6014
|
+
var GITHUB_API = "https://api.github.com/repos/kolatts/pncli/contents/skills";
|
|
6015
|
+
var RAW_BASE = "https://raw.githubusercontent.com/kolatts/pncli/main/skills";
|
|
6016
|
+
var AGENT_PATHS = {
|
|
6017
|
+
"github-copilot": { project: ".agents/skills", user: path5.join(os.homedir(), ".copilot/skills") },
|
|
6018
|
+
"claude-code": { project: ".claude/skills", user: path5.join(os.homedir(), ".claude/skills") }
|
|
6019
|
+
};
|
|
5091
6020
|
function registerSkillsCommands(program2) {
|
|
5092
6021
|
const skills = program2.command("skills").description("Manage pncli Claude Code skills");
|
|
5093
|
-
skills.command("install").description("Download latest pncli skills
|
|
6022
|
+
skills.command("install").description("Download latest pncli skills into the current repo").option("--agent <agent>", "Target agent host: github-copilot | claude-code", "github-copilot").option("--scope <scope>", "Installation scope: project | user", "project").option("--target <dir>", "Override install directory (ignores --agent and --scope)").action(async (opts) => {
|
|
5094
6023
|
const start = Date.now();
|
|
5095
6024
|
try {
|
|
5096
|
-
|
|
6025
|
+
let targetDir;
|
|
6026
|
+
if (opts.target) {
|
|
6027
|
+
targetDir = path5.resolve(opts.target);
|
|
6028
|
+
} else {
|
|
6029
|
+
const agentConfig = AGENT_PATHS[opts.agent];
|
|
6030
|
+
if (!agentConfig) {
|
|
6031
|
+
throw new Error(`Unknown agent: "${opts.agent}". Use: ${Object.keys(AGENT_PATHS).join(" | ")}`);
|
|
6032
|
+
}
|
|
6033
|
+
const scopePath = opts.scope === "user" ? agentConfig.user : agentConfig.project;
|
|
6034
|
+
targetDir = path5.resolve(scopePath);
|
|
6035
|
+
}
|
|
5097
6036
|
warn("Fetching skill list from GitHub...");
|
|
5098
6037
|
const listRes = await fetch(GITHUB_API, {
|
|
5099
6038
|
headers: { "Accept": "application/vnd.github.v3+json", "User-Agent": "pncli" }
|
|
@@ -5114,7 +6053,7 @@ function registerSkillsCommands(program2) {
|
|
|
5114
6053
|
fs3.rmSync(existingDir, { recursive: true, force: true });
|
|
5115
6054
|
}
|
|
5116
6055
|
}
|
|
5117
|
-
warn(`Found ${skillDirs.length} skills. Downloading...`);
|
|
6056
|
+
warn(`Found ${skillDirs.length} skills. Downloading to ${targetDir}...`);
|
|
5118
6057
|
const installed = [];
|
|
5119
6058
|
const failed = [];
|
|
5120
6059
|
for (const skillName of skillDirs) {
|
|
@@ -5144,54 +6083,60 @@ function registerSkillsCommands(program2) {
|
|
|
5144
6083
|
if (failed.length > 0) {
|
|
5145
6084
|
warn(`Failed to download: ${failed.join(", ")}`);
|
|
5146
6085
|
}
|
|
5147
|
-
let instructionsWritten = false;
|
|
5148
|
-
if (!opts.skipInstructions) {
|
|
5149
|
-
warn("Downloading copilot-instructions.md...");
|
|
5150
|
-
try {
|
|
5151
|
-
const res = await fetch(RAW_COPILOT_INSTRUCTIONS, {
|
|
5152
|
-
headers: { "User-Agent": "pncli" }
|
|
5153
|
-
});
|
|
5154
|
-
if (res.ok) {
|
|
5155
|
-
const content = await res.text();
|
|
5156
|
-
fs3.writeFileSync("copilot-instructions.md", content, "utf8");
|
|
5157
|
-
instructionsWritten = true;
|
|
5158
|
-
warn("Wrote copilot-instructions.md");
|
|
5159
|
-
} else {
|
|
5160
|
-
warn(`Failed to download copilot-instructions.md (${res.status})`);
|
|
5161
|
-
}
|
|
5162
|
-
} catch {
|
|
5163
|
-
warn("Failed to download copilot-instructions.md");
|
|
5164
|
-
}
|
|
5165
|
-
}
|
|
5166
6086
|
success({
|
|
5167
6087
|
installed,
|
|
5168
6088
|
failed,
|
|
5169
6089
|
target: targetDir,
|
|
5170
6090
|
total: installed.length,
|
|
5171
|
-
|
|
6091
|
+
agent: opts.target ? "custom" : opts.agent,
|
|
6092
|
+
scope: opts.target ? "custom" : opts.scope
|
|
5172
6093
|
}, "skills", "install", start);
|
|
5173
6094
|
} catch (err) {
|
|
5174
6095
|
fail(err, "skills", "install", start);
|
|
5175
6096
|
}
|
|
5176
6097
|
});
|
|
5177
|
-
skills.command("list").description("List locally installed skills").option("--
|
|
6098
|
+
skills.command("list").description("List locally installed skills").option("--agent <agent>", "Target agent host: github-copilot | claude-code", "github-copilot").option("--scope <scope>", "Installation scope: project | user", "project").option("--target <dir>", "Override skills directory to scan").action((opts) => {
|
|
5178
6099
|
const start = Date.now();
|
|
5179
6100
|
try {
|
|
5180
|
-
|
|
6101
|
+
let targetDir;
|
|
6102
|
+
if (opts.target) {
|
|
6103
|
+
targetDir = path5.resolve(opts.target);
|
|
6104
|
+
} else {
|
|
6105
|
+
const agentConfig = AGENT_PATHS[opts.agent];
|
|
6106
|
+
if (!agentConfig) {
|
|
6107
|
+
throw new Error(`Unknown agent: "${opts.agent}". Use: ${Object.keys(AGENT_PATHS).join(" | ")}`);
|
|
6108
|
+
}
|
|
6109
|
+
const scopePath = opts.scope === "user" ? agentConfig.user : agentConfig.project;
|
|
6110
|
+
targetDir = path5.resolve(scopePath);
|
|
6111
|
+
}
|
|
5181
6112
|
if (!fs3.existsSync(targetDir)) {
|
|
5182
|
-
success({ skills: [], message:
|
|
6113
|
+
success({ skills: [], message: `No skills directory found at ${targetDir}. Run: pncli skills install` }, "skills", "list", start);
|
|
5183
6114
|
return;
|
|
5184
6115
|
}
|
|
5185
6116
|
const skillDirs = fs3.readdirSync(targetDir).filter((name) => {
|
|
5186
6117
|
const skillPath = path5.join(targetDir, name, "SKILL.md");
|
|
5187
6118
|
return fs3.existsSync(skillPath);
|
|
5188
6119
|
});
|
|
5189
|
-
const
|
|
6120
|
+
const skillsList = skillDirs.map((name) => {
|
|
5190
6121
|
const content = fs3.readFileSync(path5.join(targetDir, name, "SKILL.md"), "utf8");
|
|
5191
6122
|
const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
5192
6123
|
const data = {};
|
|
6124
|
+
const metadata = {};
|
|
5193
6125
|
if (frontmatter) {
|
|
6126
|
+
let inMetadata = false;
|
|
5194
6127
|
for (const line of frontmatter[1].split("\n")) {
|
|
6128
|
+
if (line.trimEnd() === "metadata:") {
|
|
6129
|
+
inMetadata = true;
|
|
6130
|
+
continue;
|
|
6131
|
+
}
|
|
6132
|
+
if (inMetadata && line.startsWith(" ")) {
|
|
6133
|
+
const colonIdx2 = line.indexOf(":");
|
|
6134
|
+
if (colonIdx2 !== -1) {
|
|
6135
|
+
metadata[line.slice(0, colonIdx2).trim()] = line.slice(colonIdx2 + 1).trim();
|
|
6136
|
+
}
|
|
6137
|
+
continue;
|
|
6138
|
+
}
|
|
6139
|
+
inMetadata = false;
|
|
5195
6140
|
const colonIdx = line.indexOf(":");
|
|
5196
6141
|
if (colonIdx === -1) continue;
|
|
5197
6142
|
data[line.slice(0, colonIdx).trim()] = line.slice(colonIdx + 1).trim();
|
|
@@ -5200,12 +6145,13 @@ function registerSkillsCommands(program2) {
|
|
|
5200
6145
|
return {
|
|
5201
6146
|
name: data.name || name,
|
|
5202
6147
|
slug: name,
|
|
5203
|
-
category: data.category || "other",
|
|
5204
|
-
services: data.services || "",
|
|
5205
|
-
providers: data.providers || "none"
|
|
6148
|
+
category: metadata.category || data.category || "other",
|
|
6149
|
+
services: metadata.services || data.services || "",
|
|
6150
|
+
providers: metadata.providers || data.providers || "none",
|
|
6151
|
+
userInvocable: data["user-invocable"] === "true"
|
|
5206
6152
|
};
|
|
5207
6153
|
});
|
|
5208
|
-
success({ skills:
|
|
6154
|
+
success({ skills: skillsList, total: skillsList.length }, "skills", "list", start);
|
|
5209
6155
|
} catch (err) {
|
|
5210
6156
|
fail(err, "skills", "list", start);
|
|
5211
6157
|
}
|
|
@@ -5220,12 +6166,13 @@ var require2 = createRequire(import.meta.url);
|
|
|
5220
6166
|
var pkg = require2("../package.json");
|
|
5221
6167
|
var TAGLINE = "One command does what three meetings couldn't.";
|
|
5222
6168
|
var program = new Command();
|
|
5223
|
-
program.name("pncli").description(`The Paperwork Nightmare CLI \u2014 ${TAGLINE}`).version(`${pkg.version} \u2014 ${TAGLINE}`, "-v, --version").option("--pretty", "Human-readable formatted output", false).option("--verbose", "Include full response metadata", false).option("--dry-run", "Print API requests without executing", false).option("--config <path>", "Override global config file location");
|
|
6169
|
+
program.name("pncli").description(`The Paperwork Nightmare CLI \u2014 ${TAGLINE}`).version(`${pkg.version} \u2014 ${TAGLINE}`, "-v, --version").option("--pretty", "Human-readable formatted output", false).option("--verbose", "Include full response metadata", false).option("--dry-run", "Print API requests without executing", false).option("--config <path>", "Override global config file location").option("--output-file <path>", "Write JSON output to file instead of stdout");
|
|
5224
6170
|
program.hook("preAction", (thisCommand) => {
|
|
5225
6171
|
const opts = thisCommand.optsWithGlobals();
|
|
5226
6172
|
setGlobalOptions({
|
|
5227
6173
|
pretty: Boolean(opts.pretty),
|
|
5228
|
-
verbose: Boolean(opts.verbose)
|
|
6174
|
+
verbose: Boolean(opts.verbose),
|
|
6175
|
+
outputFile: opts.outputFile
|
|
5229
6176
|
});
|
|
5230
6177
|
try {
|
|
5231
6178
|
const config = loadConfig({ configPath: opts.config });
|
|
@@ -5242,6 +6189,10 @@ registerSdeCommands(program);
|
|
|
5242
6189
|
registerDepsCommands(program);
|
|
5243
6190
|
registerConfigCommands(program);
|
|
5244
6191
|
registerAdoCommands(program);
|
|
6192
|
+
registerJenkinsCommands(program);
|
|
6193
|
+
registerArtifactoryCommands(program);
|
|
6194
|
+
registerUdeployCommands(program);
|
|
6195
|
+
registerCheckmarxCommands(program);
|
|
5245
6196
|
registerSkillsCommands(program);
|
|
5246
6197
|
program.addHelpText("after", `
|
|
5247
6198
|
Services:
|
|
@@ -5253,6 +6204,10 @@ Services:
|
|
|
5253
6204
|
sonar SonarQube Server (quality gates, issues, metrics, hotspots)
|
|
5254
6205
|
sde SDElements (threat modeling, countermeasures, compliance)
|
|
5255
6206
|
ado Azure DevOps Server (work items, repos, PRs, pipelines)
|
|
6207
|
+
jenkins Jenkins Data Center (jobs, builds, logs)
|
|
6208
|
+
artifactory Artifactory (repos, artifact search, build info, properties)
|
|
6209
|
+
udeploy IBM UrbanCode Deploy (component versions, deployment processes)
|
|
6210
|
+
checkmarx Checkmarx CxSAST (projects, scans, scan statistics)
|
|
5256
6211
|
config Manage pncli configuration
|
|
5257
6212
|
skills Download and manage Claude Code skills
|
|
5258
6213
|
`);
|