@gogd-core/ggd 0.1.3 → 0.1.4

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/main.js CHANGED
@@ -24,9 +24,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // apps/ggd/src/main.ts
27
- var import_commander6 = require("commander");
27
+ var import_commander7 = require("commander");
28
28
 
29
- // apps/ggd/src/commands/env.ts
29
+ // apps/ggd/src/commands/shell/env.ts
30
30
  var import_commander = require("commander");
31
31
  function createEnvCommand() {
32
32
  const env = new import_commander.Command("env").description("Manage environments");
@@ -36,7 +36,7 @@ function createEnvCommand() {
36
36
  return env;
37
37
  }
38
38
 
39
- // apps/ggd/src/commands/run.ts
39
+ // apps/ggd/src/commands/shell/run.ts
40
40
  var import_commander2 = require("commander");
41
41
 
42
42
  // apps/ggd/src/core/exec.ts
@@ -69,7 +69,7 @@ function isExecaError(error) {
69
69
  return typeof error === "object" && error !== null && "exitCode" in error;
70
70
  }
71
71
 
72
- // apps/ggd/src/commands/run.ts
72
+ // apps/ggd/src/commands/shell/run.ts
73
73
  function createRunCommand() {
74
74
  const run = new import_commander2.Command("run").description("Execute a shell command").argument("<cmd...>", "Command and arguments to execute").allowExcessArguments(true).action(async (cmdParts) => {
75
75
  const cmd = cmdParts.join(" ");
@@ -81,7 +81,7 @@ function createRunCommand() {
81
81
  return run;
82
82
  }
83
83
 
84
- // apps/ggd/src/commands/rsh.ts
84
+ // apps/ggd/src/commands/git/rsh.ts
85
85
  var import_commander3 = require("commander");
86
86
  var import_execa2 = __toESM(require("execa"));
87
87
  var readline = __toESM(require("readline"));
@@ -147,7 +147,7 @@ function createRshCommand() {
147
147
  return rsh;
148
148
  }
149
149
 
150
- // apps/ggd/src/commands/mt.ts
150
+ // apps/ggd/src/commands/git/mt.ts
151
151
  var import_commander4 = require("commander");
152
152
  var import_execa3 = __toESM(require("execa"));
153
153
  async function localBranchExists(branch) {
@@ -166,7 +166,7 @@ async function switchBranch(branch) {
166
166
  }
167
167
  async function mergeBranch(source) {
168
168
  try {
169
- await (0, import_execa3.default)("git", ["merge", source], { stdio: "inherit" });
169
+ await (0, import_execa3.default)("git", ["merge", "--no-edit", source], { stdio: "inherit" });
170
170
  return 0;
171
171
  } catch {
172
172
  return 1;
@@ -229,8 +229,455 @@ Merge ${sourceBranch} into ${targetBranch}? (Y/n): `);
229
229
  return mt;
230
230
  }
231
231
 
232
- // apps/ggd/src/commands/completion.ts
232
+ // apps/ggd/src/commands/jenkins/jenkins.ts
233
233
  var import_commander5 = require("commander");
234
+ var fs2 = __toESM(require("fs"));
235
+ var path = __toESM(require("path"));
236
+ var os = __toESM(require("os"));
237
+ var readline2 = __toESM(require("readline"));
238
+
239
+ // apps/ggd/src/commands/jenkins/jenkins-build.ts
240
+ var http = __toESM(require("http"));
241
+ var https = __toESM(require("https"));
242
+ var import_child_process = require("child_process");
243
+ function findTarget(name, targets) {
244
+ return targets.find((t) => t.name === name);
245
+ }
246
+ function listTargetNames(targets) {
247
+ return targets.map((t) => t.name);
248
+ }
249
+ function parseOverrides(args) {
250
+ const overrides = {};
251
+ for (const arg of args) {
252
+ const match = arg.match(/^--([^=]+)=(.*)$/);
253
+ if (match && match[1] !== void 0) {
254
+ overrides[match[1]] = match[2] ?? "";
255
+ }
256
+ }
257
+ return overrides;
258
+ }
259
+ function mergeParams(defaults, overrides) {
260
+ const merged = { ...defaults };
261
+ for (const [key, value] of Object.entries(overrides)) {
262
+ merged[key] = value;
263
+ }
264
+ return merged;
265
+ }
266
+ function resolveJobPath(target, config) {
267
+ if (target.jobPathOverride)
268
+ return target.jobPathOverride;
269
+ return config.jobPaths[target.jobPathKey] ?? "";
270
+ }
271
+ function jenkinsRequest(url, method, user, token, headers = {}, body) {
272
+ return new Promise((resolve, reject) => {
273
+ const parsedUrl = new URL(url);
274
+ const isHttps = parsedUrl.protocol === "https:";
275
+ const lib = isHttps ? https : http;
276
+ const auth = Buffer.from(`${user}:${token}`).toString("base64");
277
+ const reqHeaders = {
278
+ Authorization: `Basic ${auth}`,
279
+ ...headers
280
+ };
281
+ if (body) {
282
+ reqHeaders["Content-Type"] = "application/x-www-form-urlencoded";
283
+ reqHeaders["Content-Length"] = Buffer.byteLength(body).toString();
284
+ }
285
+ const req = lib.request(
286
+ {
287
+ hostname: parsedUrl.hostname,
288
+ port: parsedUrl.port || (isHttps ? 443 : 80),
289
+ path: parsedUrl.pathname + parsedUrl.search,
290
+ method,
291
+ headers: reqHeaders
292
+ },
293
+ (res) => {
294
+ let data = "";
295
+ res.on("data", (chunk) => {
296
+ data += chunk.toString();
297
+ });
298
+ res.on("end", () => {
299
+ resolve({
300
+ statusCode: res.statusCode ?? 0,
301
+ headers: res.headers,
302
+ body: data
303
+ });
304
+ });
305
+ }
306
+ );
307
+ req.on("error", reject);
308
+ if (body)
309
+ req.write(body);
310
+ req.end();
311
+ });
312
+ }
313
+ async function fetchCrumb(config) {
314
+ try {
315
+ const res = await jenkinsRequest(
316
+ `${config.url}/crumbIssuer/api/json`,
317
+ "GET",
318
+ config.user,
319
+ config.token
320
+ );
321
+ const json = JSON.parse(res.body);
322
+ return json.crumb ?? null;
323
+ } catch {
324
+ return null;
325
+ }
326
+ }
327
+ async function triggerBuild(config, jobPath, params) {
328
+ const crumb = await fetchCrumb(config);
329
+ const formBody = Object.entries(params).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
330
+ const headers = {};
331
+ if (crumb) {
332
+ headers["Jenkins-Crumb"] = crumb;
333
+ }
334
+ const res = await jenkinsRequest(
335
+ `${config.url}/job/${jobPath}/buildWithParameters`,
336
+ "POST",
337
+ config.user,
338
+ config.token,
339
+ headers,
340
+ formBody
341
+ );
342
+ const location = res.headers["location"];
343
+ if (typeof location === "string" && location) {
344
+ return location;
345
+ }
346
+ return null;
347
+ }
348
+ async function waitForBuild(config, queueUrl) {
349
+ const maxAttempts = 60;
350
+ for (let i = 0; i < maxAttempts; i++) {
351
+ try {
352
+ const res = await jenkinsRequest(
353
+ `${queueUrl}api/json`,
354
+ "GET",
355
+ config.user,
356
+ config.token
357
+ );
358
+ const json = JSON.parse(res.body);
359
+ if (json.executable?.url) {
360
+ return json.executable.url;
361
+ }
362
+ const why = json.why ?? "Waiting...";
363
+ const queueId = json.id ?? "?";
364
+ const blocked = json.blocked ?? false;
365
+ process.stderr.write(`\r\x1B[33mQueued #${queueId} (blocked: ${blocked}) ${why}\x1B[0m `);
366
+ } catch {
367
+ }
368
+ await sleep(2e3);
369
+ }
370
+ return null;
371
+ }
372
+ async function watchBuild(config, buildUrl) {
373
+ let lastLines = "";
374
+ const maxAttempts = 360;
375
+ for (let i = 0; i < maxAttempts; i++) {
376
+ try {
377
+ const buildRes = await jenkinsRequest(
378
+ `${buildUrl}api/json`,
379
+ "GET",
380
+ config.user,
381
+ config.token
382
+ );
383
+ const json = JSON.parse(buildRes.body);
384
+ const consoleRes = await jenkinsRequest(
385
+ `${buildUrl}consoleText`,
386
+ "GET",
387
+ config.user,
388
+ config.token
389
+ );
390
+ const lines = consoleRes.body.trim().split("\n").slice(-5).join("\n");
391
+ if (lines !== lastLines) {
392
+ console.error(`
393
+ ${lines}`);
394
+ lastLines = lines;
395
+ }
396
+ if (json.result && json.result !== "null") {
397
+ const buildNum = json.number ?? "?";
398
+ return `${json.result} (#${buildNum})`;
399
+ }
400
+ } catch {
401
+ }
402
+ await sleep(5e3);
403
+ }
404
+ return "TIMEOUT";
405
+ }
406
+ function sleep(ms) {
407
+ return new Promise((resolve) => setTimeout(resolve, ms));
408
+ }
409
+ function notify(title, message) {
410
+ try {
411
+ if (process.platform === "win32") {
412
+ (0, import_child_process.execSync)(
413
+ `powershell -Command "New-BurntToastNotification -Text '${title}','${message}'" 2>nul || powershell -Command "[void][System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); $n = New-Object System.Windows.Forms.NotifyIcon; $n.Icon = [System.Drawing.SystemIcons]::Information; $n.Visible = $true; $n.ShowBalloonTip(5000, '${title}', '${message}', 'Info')"`,
414
+ { stdio: "ignore" }
415
+ );
416
+ } else if (process.platform === "darwin") {
417
+ (0, import_child_process.execSync)(
418
+ `osascript -e 'display notification "${message}" with title "${title}"'`,
419
+ { stdio: "ignore" }
420
+ );
421
+ } else {
422
+ (0, import_child_process.execSync)(`notify-send "${title}" "${message}"`, { stdio: "ignore" });
423
+ }
424
+ } catch {
425
+ }
426
+ }
427
+
428
+ // apps/ggd/src/commands/jenkins/jenkins-presets.ts
429
+ var fs = __toESM(require("fs"));
430
+ function loadPresetFile(filePath) {
431
+ if (!fs.existsSync(filePath)) {
432
+ throw new Error(`Preset file not found: ${filePath}`);
433
+ }
434
+ let raw;
435
+ try {
436
+ raw = fs.readFileSync(filePath, "utf8");
437
+ } catch {
438
+ throw new Error(`Failed to read preset file: ${filePath}`);
439
+ }
440
+ let parsed;
441
+ try {
442
+ parsed = JSON.parse(raw);
443
+ } catch {
444
+ throw new Error(`Invalid JSON in preset file: ${filePath}`);
445
+ }
446
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
447
+ throw new Error(`Preset file must contain a JSON object: ${filePath}`);
448
+ }
449
+ const obj = parsed;
450
+ if (obj["url"] !== void 0 && typeof obj["url"] !== "string") {
451
+ throw new Error(`Preset "url" must be a string`);
452
+ }
453
+ if (obj["environments"] !== void 0) {
454
+ if (typeof obj["environments"] !== "object" || obj["environments"] === null || Array.isArray(obj["environments"])) {
455
+ throw new Error(`Preset "environments" must be an object`);
456
+ }
457
+ }
458
+ if (!Array.isArray(obj["targets"])) {
459
+ throw new Error(`Preset "targets" must be an array`);
460
+ }
461
+ for (const target of obj["targets"]) {
462
+ if (typeof target !== "object" || target === null || Array.isArray(target)) {
463
+ throw new Error(`Each target must be an object`);
464
+ }
465
+ const t = target;
466
+ if (typeof t["name"] !== "string" || typeof t["displayName"] !== "string") {
467
+ throw new Error(`Each target must have "name" and "displayName" strings`);
468
+ }
469
+ if (typeof t["jobPathKey"] !== "string") {
470
+ throw new Error(`Each target must have a "jobPathKey" string`);
471
+ }
472
+ if (typeof t["defaults"] !== "object" || t["defaults"] === null || Array.isArray(t["defaults"])) {
473
+ throw new Error(`Each target must have a "defaults" object`);
474
+ }
475
+ }
476
+ return parsed;
477
+ }
478
+
479
+ // apps/ggd/src/commands/jenkins/jenkins.ts
480
+ function getConfigDir() {
481
+ return path.join(os.homedir(), ".ggd");
482
+ }
483
+ function getConfigPath() {
484
+ return path.join(getConfigDir(), "jenkins.json");
485
+ }
486
+ function readConfig() {
487
+ const configPath = getConfigPath();
488
+ try {
489
+ if (!fs2.existsSync(configPath))
490
+ return null;
491
+ const raw = fs2.readFileSync(configPath, "utf8");
492
+ return JSON.parse(raw);
493
+ } catch {
494
+ return null;
495
+ }
496
+ }
497
+ function writeConfig(config) {
498
+ const dir = getConfigDir();
499
+ if (!fs2.existsSync(dir)) {
500
+ fs2.mkdirSync(dir, { recursive: true });
501
+ }
502
+ fs2.writeFileSync(getConfigPath(), JSON.stringify(config, null, 2), "utf8");
503
+ }
504
+ function prompt(question, defaultValue) {
505
+ const rl = readline2.createInterface({
506
+ input: process.stdin,
507
+ output: process.stderr
508
+ });
509
+ const display = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
510
+ return new Promise((resolve) => {
511
+ rl.question(display, (answer) => {
512
+ rl.close();
513
+ resolve(answer.trim() || defaultValue || "");
514
+ });
515
+ });
516
+ }
517
+ function maskToken(token) {
518
+ if (token.length <= 8)
519
+ return "****";
520
+ return `${token.slice(0, 4)}...${token.slice(-4)}`;
521
+ }
522
+ async function runSetup(presetPath) {
523
+ const existing = readConfig();
524
+ console.error("\x1B[36mJenkins CI Setup\x1B[0m");
525
+ console.error("\u2500".repeat(40));
526
+ if (existing) {
527
+ console.error(`\x1B[33mExisting config found. Press Enter to keep current values.\x1B[0m
528
+ `);
529
+ }
530
+ let presetUrl = "";
531
+ let presetJobPaths = {};
532
+ let presetTargets = [];
533
+ if (presetPath) {
534
+ console.error(`\x1B[36mLoading preset from ${presetPath}\x1B[0m
535
+ `);
536
+ const preset = loadPresetFile(presetPath);
537
+ presetUrl = preset.url ?? "";
538
+ presetTargets = preset.targets;
539
+ if (preset.environments) {
540
+ const envNames = Object.keys(preset.environments);
541
+ console.error(`Available environments: ${envNames.join(", ")}`);
542
+ const envChoice = await prompt("Environment", envNames[0]);
543
+ presetJobPaths = preset.environments[envChoice] ?? {};
544
+ if (!preset.environments[envChoice]) {
545
+ console.error(`\x1B[31mUnknown environment: ${envChoice}. No job paths loaded.\x1B[0m`);
546
+ }
547
+ }
548
+ }
549
+ const url = await prompt("Jenkins URL", existing?.url || presetUrl || "");
550
+ const user = await prompt("API User", existing?.user);
551
+ const token = await prompt(
552
+ `API Token${existing?.token ? ` (current: ${maskToken(existing.token)})` : ""}`,
553
+ existing?.token
554
+ );
555
+ let jobPaths;
556
+ if (Object.keys(presetJobPaths).length > 0) {
557
+ jobPaths = presetJobPaths;
558
+ } else if (existing?.jobPaths && Object.keys(existing.jobPaths).length > 0) {
559
+ jobPaths = existing.jobPaths;
560
+ } else {
561
+ const uiPath = await prompt("UI job path", "");
562
+ const apiPath = await prompt("API job path", "");
563
+ const lambdaPath = await prompt("Lambda job path", "");
564
+ jobPaths = {};
565
+ if (uiPath)
566
+ jobPaths["ui"] = uiPath;
567
+ if (apiPath)
568
+ jobPaths["api"] = apiPath;
569
+ if (lambdaPath)
570
+ jobPaths["lambda"] = lambdaPath;
571
+ }
572
+ const targets = presetTargets.length > 0 ? presetTargets : existing?.targets ?? [];
573
+ const config = {
574
+ url,
575
+ user,
576
+ token,
577
+ jobPaths,
578
+ targets
579
+ };
580
+ writeConfig(config);
581
+ console.error(`
582
+ \x1B[32mConfig saved to ${getConfigPath()}\x1B[0m`);
583
+ console.error(` URL: ${config.url}`);
584
+ console.error(` User: ${config.user}`);
585
+ console.error(` Token: ${maskToken(config.token)}`);
586
+ console.error(` Targets: ${config.targets.length} build target(s)`);
587
+ console.error(` Job paths:`);
588
+ for (const [key, value] of Object.entries(config.jobPaths)) {
589
+ console.error(` ${key}: ${value}`);
590
+ }
591
+ return config;
592
+ }
593
+ async function ensureConfig() {
594
+ let config = readConfig();
595
+ if (config)
596
+ return config;
597
+ console.error("\x1B[33mNo Jenkins config found. Starting setup...\x1B[0m\n");
598
+ config = await runSetup();
599
+ return config;
600
+ }
601
+ function createJenkinsCommand() {
602
+ const jenkins = new import_commander5.Command("jenkins").description("Jenkins CI integration");
603
+ jenkins.command("setup").description("Configure Jenkins connection and environment").option("-p, --preset <path>", "Load configuration from a preset JSON file").action(async (opts) => {
604
+ await runSetup(opts.preset);
605
+ });
606
+ jenkins.command("build").description("Trigger a Jenkins build").argument("[target]", "Build target name").option("-d, --detach", "Run in detached mode (don't watch build)").allowUnknownOption(true).action(async (targetName, opts, cmd) => {
607
+ const config = await ensureConfig();
608
+ if (!config)
609
+ return;
610
+ const targets = config.targets;
611
+ if (!targetName) {
612
+ if (targets.length === 0) {
613
+ console.error("\x1B[33mNo build targets configured.\x1B[0m");
614
+ console.error("Add targets via: ggd jenkins setup --preset <file>");
615
+ return;
616
+ }
617
+ console.error("\x1B[36mAvailable build targets:\x1B[0m");
618
+ for (const t of targets) {
619
+ console.error(` ${t.name.padEnd(20)} ${t.displayName}`);
620
+ }
621
+ console.error(`
622
+ Usage: ggd jenkins build <target> [--KEY=value ...]`);
623
+ return;
624
+ }
625
+ const target = findTarget(targetName, targets);
626
+ if (!target) {
627
+ console.error(`\x1B[31mUnknown target: ${targetName}\x1B[0m`);
628
+ console.error(`Available: ${listTargetNames(targets).join(", ")}`);
629
+ process.exitCode = 1;
630
+ return;
631
+ }
632
+ const rawArgs = cmd.args.slice(1);
633
+ const overrides = parseOverrides(rawArgs);
634
+ const params = mergeParams(target.defaults, overrides);
635
+ const jobPath = resolveJobPath(target, config);
636
+ console.error(`\x1B[36mTarget:\x1B[0m ${target.displayName}`);
637
+ console.error(`\x1B[36mJob:\x1B[0m ${config.url}/job/${jobPath}`);
638
+ console.error(`\x1B[36mParams:\x1B[0m`);
639
+ for (const [k, v] of Object.entries(params)) {
640
+ console.error(` ${k}=${v || "<empty>"}`);
641
+ }
642
+ console.error("");
643
+ console.error("\x1B[33mTriggering build...\x1B[0m");
644
+ const queueUrl = await triggerBuild(config, jobPath, params);
645
+ if (!queueUrl) {
646
+ console.error("\x1B[31mFailed to trigger build. Check Jenkins URL / API token.\x1B[0m");
647
+ process.exitCode = 1;
648
+ return;
649
+ }
650
+ console.error(`\x1B[32mQueued!\x1B[0m ${queueUrl}`);
651
+ if (opts.detach) {
652
+ return;
653
+ }
654
+ const buildUrl = await waitForBuild(config, queueUrl);
655
+ if (!buildUrl) {
656
+ console.error("\n\x1B[31mTimed out waiting for build to start.\x1B[0m");
657
+ process.exitCode = 1;
658
+ return;
659
+ }
660
+ console.error(`
661
+ \x1B[32mBuild started:\x1B[0m ${buildUrl}`);
662
+ const result = await watchBuild(config, buildUrl);
663
+ console.error(`
664
+ \x1B[36mBuild result: ${result}\x1B[0m`);
665
+ notify("Jenkins Build", `${target.displayName}: ${result}`);
666
+ if (!result.startsWith("SUCCESS")) {
667
+ process.exitCode = 1;
668
+ }
669
+ });
670
+ jenkins.command("config").description("Show current Jenkins configuration").action(async () => {
671
+ const config = await ensureConfig();
672
+ if (!config)
673
+ return;
674
+ console.log(JSON.stringify(config, null, 2));
675
+ });
676
+ return jenkins;
677
+ }
678
+
679
+ // apps/ggd/src/commands/shell/completion.ts
680
+ var import_commander6 = require("commander");
234
681
  function detectShell() {
235
682
  const shell = process.env["SHELL"] || "";
236
683
  if (shell.includes("zsh"))
@@ -300,7 +747,7 @@ function getCompletions(program2, args) {
300
747
  }
301
748
  var VALID_SHELLS = ["bash", "zsh", "powershell"];
302
749
  function createCompletionCommand() {
303
- const completion = new import_commander5.Command("completion").description("Output shell completion script").argument("[shell]", "Shell type: bash, zsh, powershell (auto-detected if omitted)").action((shell) => {
750
+ const completion = new import_commander6.Command("completion").description("Output shell completion script").argument("[shell]", "Shell type: bash, zsh, powershell (auto-detected if omitted)").action((shell) => {
304
751
  const resolved = shell ? validateShell(shell) : detectShell();
305
752
  console.log(getCompletionScript(resolved));
306
753
  });
@@ -316,12 +763,13 @@ function validateShell(shell) {
316
763
  }
317
764
 
318
765
  // apps/ggd/src/main.ts
319
- var program = new import_commander6.Command();
766
+ var program = new import_commander7.Command();
320
767
  program.name("ggd").description("GoGoodDev workspace CLI").version("0.1.3");
321
768
  program.addCommand(createEnvCommand());
322
769
  program.addCommand(createRunCommand());
323
770
  program.addCommand(createRshCommand());
324
771
  program.addCommand(createMtCommand());
772
+ program.addCommand(createJenkinsCommand());
325
773
  program.addCommand(createCompletionCommand());
326
774
  if (process.argv.includes("--get-completions")) {
327
775
  const args = process.argv.slice(process.argv.indexOf("--get-completions") + 1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gogd-core/ggd",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "dependencies": {
5
5
  "commander": "12.1.0",
6
6
  "execa": "5.1.1"
package/postinstall.js CHANGED
@@ -43,7 +43,7 @@ var fs = __toESM(require("fs"));
43
43
  var path = __toESM(require("path"));
44
44
  var os = __toESM(require("os"));
45
45
 
46
- // apps/ggd/src/commands/completion.ts
46
+ // apps/ggd/src/commands/shell/completion.ts
47
47
  var import_commander = require("commander");
48
48
  function generateBashCompletion() {
49
49
  return `###-begin-ggd-completions-###
@@ -1,5 +1,6 @@
1
- export { createEnvCommand } from './env';
2
- export { createRunCommand } from './run';
3
- export { createRshCommand } from './rsh';
4
- export { createMtCommand } from './mt';
5
- export { createCompletionCommand, getCompletions } from './completion';
1
+ export { createEnvCommand } from './shell/env';
2
+ export { createRunCommand } from './shell/run';
3
+ export { createRshCommand } from './git/rsh';
4
+ export { createMtCommand } from './git/mt';
5
+ export { createJenkinsCommand } from './jenkins/jenkins';
6
+ export { createCompletionCommand, getCompletions } from './shell/completion';
@@ -0,0 +1,65 @@
1
+ /**
2
+ * `ggd jenkins build` — trigger Jenkins builds via REST API.
3
+ *
4
+ * All build targets are loaded from user config (~/.ggd/jenkins.json),
5
+ * populated via `ggd jenkins setup --preset <file>`.
6
+ */
7
+ import { JenkinsConfig } from './jenkins';
8
+ export interface BuildTarget {
9
+ name: string;
10
+ displayName: string;
11
+ jobPathKey: string;
12
+ /** Custom job path override (for targets with unique paths) */
13
+ jobPathOverride?: string;
14
+ defaults: Record<string, string>;
15
+ }
16
+ /**
17
+ * Finds a build target by name from the given targets list.
18
+ */
19
+ export declare function findTarget(name: string, targets: BuildTarget[]): BuildTarget | undefined;
20
+ /**
21
+ * Lists all available build target names from the given targets list.
22
+ */
23
+ export declare function listTargetNames(targets: BuildTarget[]): string[];
24
+ /**
25
+ * Parses --KEY=value args into an overrides map.
26
+ */
27
+ export declare function parseOverrides(args: string[]): Record<string, string>;
28
+ /**
29
+ * Merges defaults with overrides to produce final params.
30
+ */
31
+ export declare function mergeParams(defaults: Record<string, string>, overrides: Record<string, string>): Record<string, string>;
32
+ /**
33
+ * Resolves the job path for a target.
34
+ */
35
+ export declare function resolveJobPath(target: BuildTarget, config: JenkinsConfig): string;
36
+ interface JenkinsHttpResponse {
37
+ statusCode: number;
38
+ headers: Record<string, string | string[] | undefined>;
39
+ body: string;
40
+ }
41
+ /**
42
+ * Makes an HTTP/HTTPS request to Jenkins.
43
+ */
44
+ export declare function jenkinsRequest(url: string, method: string, user: string, token: string, headers?: Record<string, string>, body?: string): Promise<JenkinsHttpResponse>;
45
+ /**
46
+ * Fetches the Jenkins CSRF crumb token.
47
+ */
48
+ export declare function fetchCrumb(config: JenkinsConfig): Promise<string | null>;
49
+ /**
50
+ * Triggers a Jenkins build. Returns the queue URL or null on failure.
51
+ */
52
+ export declare function triggerBuild(config: JenkinsConfig, jobPath: string, params: Record<string, string>): Promise<string | null>;
53
+ /**
54
+ * Polls the queue URL until the build starts. Returns the build URL.
55
+ */
56
+ export declare function waitForBuild(config: JenkinsConfig, queueUrl: string): Promise<string | null>;
57
+ /**
58
+ * Watches a build until it completes. Returns the final status.
59
+ */
60
+ export declare function watchBuild(config: JenkinsConfig, buildUrl: string): Promise<string>;
61
+ /**
62
+ * Sends a desktop notification. Best-effort — silently ignores failures.
63
+ */
64
+ export declare function notify(title: string, message: string): void;
65
+ export {};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Jenkins preset file loader.
3
+ *
4
+ * Preset files are external JSON files that contain Jenkins configuration
5
+ * for a specific team/project. They can include environment-specific job paths
6
+ * and build targets.
7
+ */
8
+ import { BuildTarget } from './jenkins-build';
9
+ import { JenkinsJobPaths } from './jenkins';
10
+ export interface JenkinsPreset {
11
+ url?: string;
12
+ environments?: Record<string, JenkinsJobPaths>;
13
+ targets: BuildTarget[];
14
+ }
15
+ /**
16
+ * Loads and validates a preset file from disk.
17
+ * Throws on missing file or invalid JSON structure.
18
+ */
19
+ export declare function loadPresetFile(filePath: string): JenkinsPreset;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * `ggd jenkins` command — Jenkins CI integration.
3
+ *
4
+ * Config is stored at ~/.ggd/jenkins.json.
5
+ * Use `ggd jenkins setup --preset <file>` to load a team preset.
6
+ */
7
+ import { Command } from 'commander';
8
+ import { BuildTarget } from './jenkins-build';
9
+ export interface JenkinsJobPaths {
10
+ [key: string]: string;
11
+ }
12
+ export interface JenkinsConfig {
13
+ url: string;
14
+ user: string;
15
+ token: string;
16
+ jobPaths: JenkinsJobPaths;
17
+ targets: BuildTarget[];
18
+ }
19
+ /**
20
+ * Returns the path to the ggd config directory (~/.ggd).
21
+ */
22
+ export declare function getConfigDir(): string;
23
+ /**
24
+ * Returns the path to the Jenkins config file.
25
+ */
26
+ export declare function getConfigPath(): string;
27
+ /**
28
+ * Reads the Jenkins config from disk. Returns null if not found.
29
+ */
30
+ export declare function readConfig(): JenkinsConfig | null;
31
+ /**
32
+ * Writes the Jenkins config to disk.
33
+ */
34
+ export declare function writeConfig(config: JenkinsConfig): void;
35
+ /**
36
+ * Prompts the user for input with a default value.
37
+ */
38
+ export declare function prompt(question: string, defaultValue?: string): Promise<string>;
39
+ /**
40
+ * Masks a token for display (shows first 4 and last 4 chars).
41
+ */
42
+ export declare function maskToken(token: string): string;
43
+ /**
44
+ * Runs the interactive setup flow.
45
+ */
46
+ export declare function runSetup(presetPath?: string): Promise<JenkinsConfig>;
47
+ /**
48
+ * Ensures Jenkins config exists. If not, runs interactive setup.
49
+ */
50
+ export declare function ensureConfig(): Promise<JenkinsConfig | null>;
51
+ /**
52
+ * Creates the `jenkins` command with subcommands.
53
+ */
54
+ export declare function createJenkinsCommand(): Command;
File without changes
File without changes
File without changes
File without changes