shipwright-cli 1.10.0 → 2.0.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.
Files changed (108) hide show
  1. package/README.md +114 -36
  2. package/completions/_shipwright +212 -32
  3. package/completions/shipwright.bash +97 -25
  4. package/docs/strategy/01-market-research.md +619 -0
  5. package/docs/strategy/02-mission-and-brand.md +587 -0
  6. package/docs/strategy/03-gtm-and-roadmap.md +759 -0
  7. package/docs/strategy/QUICK-START.txt +289 -0
  8. package/docs/strategy/README.md +172 -0
  9. package/package.json +4 -2
  10. package/scripts/sw +208 -1
  11. package/scripts/sw-activity.sh +500 -0
  12. package/scripts/sw-adaptive.sh +925 -0
  13. package/scripts/sw-adversarial.sh +1 -1
  14. package/scripts/sw-architecture-enforcer.sh +1 -1
  15. package/scripts/sw-auth.sh +613 -0
  16. package/scripts/sw-autonomous.sh +664 -0
  17. package/scripts/sw-changelog.sh +704 -0
  18. package/scripts/sw-checkpoint.sh +1 -1
  19. package/scripts/sw-ci.sh +602 -0
  20. package/scripts/sw-cleanup.sh +1 -1
  21. package/scripts/sw-code-review.sh +637 -0
  22. package/scripts/sw-connect.sh +1 -1
  23. package/scripts/sw-context.sh +605 -0
  24. package/scripts/sw-cost.sh +1 -1
  25. package/scripts/sw-daemon.sh +432 -130
  26. package/scripts/sw-dashboard.sh +1 -1
  27. package/scripts/sw-db.sh +540 -0
  28. package/scripts/sw-decompose.sh +539 -0
  29. package/scripts/sw-deps.sh +551 -0
  30. package/scripts/sw-developer-simulation.sh +1 -1
  31. package/scripts/sw-discovery.sh +412 -0
  32. package/scripts/sw-docs-agent.sh +539 -0
  33. package/scripts/sw-docs.sh +1 -1
  34. package/scripts/sw-doctor.sh +59 -1
  35. package/scripts/sw-dora.sh +615 -0
  36. package/scripts/sw-durable.sh +710 -0
  37. package/scripts/sw-e2e-orchestrator.sh +535 -0
  38. package/scripts/sw-eventbus.sh +393 -0
  39. package/scripts/sw-feedback.sh +471 -0
  40. package/scripts/sw-fix.sh +1 -1
  41. package/scripts/sw-fleet-discover.sh +567 -0
  42. package/scripts/sw-fleet-viz.sh +404 -0
  43. package/scripts/sw-fleet.sh +8 -1
  44. package/scripts/sw-github-app.sh +596 -0
  45. package/scripts/sw-github-checks.sh +1 -1
  46. package/scripts/sw-github-deploy.sh +1 -1
  47. package/scripts/sw-github-graphql.sh +1 -1
  48. package/scripts/sw-guild.sh +569 -0
  49. package/scripts/sw-heartbeat.sh +1 -1
  50. package/scripts/sw-hygiene.sh +559 -0
  51. package/scripts/sw-incident.sh +617 -0
  52. package/scripts/sw-init.sh +88 -1
  53. package/scripts/sw-instrument.sh +699 -0
  54. package/scripts/sw-intelligence.sh +1 -1
  55. package/scripts/sw-jira.sh +1 -1
  56. package/scripts/sw-launchd.sh +363 -28
  57. package/scripts/sw-linear.sh +1 -1
  58. package/scripts/sw-logs.sh +1 -1
  59. package/scripts/sw-loop.sh +64 -3
  60. package/scripts/sw-memory.sh +1 -1
  61. package/scripts/sw-mission-control.sh +487 -0
  62. package/scripts/sw-model-router.sh +545 -0
  63. package/scripts/sw-otel.sh +596 -0
  64. package/scripts/sw-oversight.sh +689 -0
  65. package/scripts/sw-pipeline-composer.sh +1 -1
  66. package/scripts/sw-pipeline-vitals.sh +1 -1
  67. package/scripts/sw-pipeline.sh +687 -24
  68. package/scripts/sw-pm.sh +693 -0
  69. package/scripts/sw-pr-lifecycle.sh +522 -0
  70. package/scripts/sw-predictive.sh +1 -1
  71. package/scripts/sw-prep.sh +1 -1
  72. package/scripts/sw-ps.sh +1 -1
  73. package/scripts/sw-public-dashboard.sh +798 -0
  74. package/scripts/sw-quality.sh +595 -0
  75. package/scripts/sw-reaper.sh +1 -1
  76. package/scripts/sw-recruit.sh +573 -0
  77. package/scripts/sw-regression.sh +642 -0
  78. package/scripts/sw-release-manager.sh +736 -0
  79. package/scripts/sw-release.sh +706 -0
  80. package/scripts/sw-remote.sh +1 -1
  81. package/scripts/sw-replay.sh +520 -0
  82. package/scripts/sw-retro.sh +691 -0
  83. package/scripts/sw-scale.sh +444 -0
  84. package/scripts/sw-security-audit.sh +505 -0
  85. package/scripts/sw-self-optimize.sh +1 -1
  86. package/scripts/sw-session.sh +1 -1
  87. package/scripts/sw-setup.sh +1 -1
  88. package/scripts/sw-standup.sh +712 -0
  89. package/scripts/sw-status.sh +1 -1
  90. package/scripts/sw-strategic.sh +658 -0
  91. package/scripts/sw-stream.sh +450 -0
  92. package/scripts/sw-swarm.sh +583 -0
  93. package/scripts/sw-team-stages.sh +511 -0
  94. package/scripts/sw-templates.sh +1 -1
  95. package/scripts/sw-testgen.sh +515 -0
  96. package/scripts/sw-tmux-pipeline.sh +554 -0
  97. package/scripts/sw-tmux.sh +1 -1
  98. package/scripts/sw-trace.sh +485 -0
  99. package/scripts/sw-tracker-github.sh +188 -0
  100. package/scripts/sw-tracker-jira.sh +172 -0
  101. package/scripts/sw-tracker-linear.sh +251 -0
  102. package/scripts/sw-tracker.sh +117 -2
  103. package/scripts/sw-triage.sh +603 -0
  104. package/scripts/sw-upgrade.sh +1 -1
  105. package/scripts/sw-ux.sh +677 -0
  106. package/scripts/sw-webhook.sh +627 -0
  107. package/scripts/sw-widgets.sh +530 -0
  108. package/scripts/sw-worktree.sh +1 -1
@@ -8,7 +8,7 @@
8
8
  set -euo pipefail
9
9
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
10
10
 
11
- VERSION="1.10.0"
11
+ VERSION="2.0.0"
12
12
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
13
 
14
14
  # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
@@ -0,0 +1,602 @@
1
+ #!/usr/bin/env bash
2
+ # ╔═══════════════════════════════════════════════════════════════════════════╗
3
+ # ║ shipwright ci — GitHub Actions CI/CD Orchestration ║
4
+ # ║ Workflow generation · Matrix testing · Caching · Secret management ║
5
+ # ╚═══════════════════════════════════════════════════════════════════════════╝
6
+ set -euo pipefail
7
+ trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
+
9
+ VERSION="2.0.0"
10
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
+ REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
+
13
+ # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
14
+ CYAN='\033[38;2;0;212;255m' # #00d4ff — primary accent
15
+ PURPLE='\033[38;2;124;58;237m' # #7c3aed — secondary
16
+ BLUE='\033[38;2;0;102;255m' # #0066ff — tertiary
17
+ GREEN='\033[38;2;74;222;128m' # success
18
+ YELLOW='\033[38;2;250;204;21m' # warning
19
+ RED='\033[38;2;248;113;113m' # error
20
+ DIM='\033[2m'
21
+ BOLD='\033[1m'
22
+ RESET='\033[0m'
23
+
24
+ # ─── Cross-platform compatibility ──────────────────────────────────────────
25
+ # shellcheck source=lib/compat.sh
26
+ [[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
27
+
28
+ # ─── Output Helpers ─────────────────────────────────────────────────────────
29
+ info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
30
+ success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
31
+ warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
32
+ error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
33
+
34
+ now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
35
+ now_epoch() { date +%s; }
36
+
37
+ # ─── Structured Event Log ──────────────────────────────────────────────────
38
+ EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
39
+
40
+ emit_event() {
41
+ local event_type="$1"
42
+ shift
43
+ local json_fields=""
44
+ for kv in "$@"; do
45
+ local key="${kv%%=*}"
46
+ local val="${kv#*=}"
47
+ if [[ "$val" =~ ^-?[0-9]+\.?[0-9]*$ ]]; then
48
+ json_fields="${json_fields},\"${key}\":${val}"
49
+ else
50
+ local escaped_val
51
+ escaped_val=$(printf '%s' "$val" | jq -Rs '.' 2>/dev/null || printf '"%s"' "${val//\"/\\\"}")
52
+ json_fields="${json_fields},\"${key}\":${escaped_val}"
53
+ fi
54
+ done
55
+ mkdir -p "${HOME}/.shipwright"
56
+ echo "{\"ts\":\"$(now_iso)\",\"ts_epoch\":$(now_epoch),\"type\":\"${event_type}\"${json_fields}}" >> "$EVENTS_FILE"
57
+ }
58
+
59
+ # ─── Generate Workflow YAML from Pipeline Template ──────────────────────────
60
+
61
+ cmd_generate() {
62
+ local pipeline_config="${1:-.claude/pipeline-artifacts/composed-pipeline.json}"
63
+ local workflow_name="${2:-shipwright-generated}"
64
+
65
+ [[ ! -f "$pipeline_config" ]] && {
66
+ error "Pipeline config not found: $pipeline_config"
67
+ exit 1
68
+ }
69
+
70
+ info "Generating GitHub Actions workflow from pipeline config"
71
+
72
+ local yaml_file=".github/workflows/${workflow_name}.yml"
73
+ mkdir -p ".github/workflows"
74
+
75
+ # Build YAML header
76
+ local yaml_content
77
+ yaml_content="name: ${workflow_name^}
78
+
79
+ on:
80
+ push:
81
+ branches: [main]
82
+ pull_request:
83
+ branches: [main]
84
+
85
+ env:
86
+ SHIPWRIGHT_PIPELINE: ${workflow_name}
87
+
88
+ jobs:
89
+ "
90
+
91
+ # Extract stages from pipeline config and generate jobs
92
+ local stages
93
+ stages=$(jq -r '.stages[] | select(.enabled==true) | .id' "$pipeline_config" 2>/dev/null || echo "")
94
+
95
+ while IFS= read -r stage_id; do
96
+ [[ -z "$stage_id" ]] && continue
97
+
98
+ local stage_config
99
+ stage_config=$(jq ".stages[] | select(.id==\"$stage_id\")" "$pipeline_config" 2>/dev/null || echo "{}")
100
+
101
+ local gate
102
+ gate=$(jq -r '.gate // "auto"' <<< "$stage_config")
103
+
104
+ yaml_content+=" ${stage_id}:
105
+ runs-on: ubuntu-latest
106
+ needs: []
107
+ if: success()
108
+ steps:
109
+ - uses: actions/checkout@v4
110
+ - name: Install dependencies
111
+ run: |
112
+ sudo apt-get update
113
+ sudo apt-get install -y jq tmux
114
+ - name: Run ${stage_id} stage
115
+ run: shipwright pipeline run --stage ${stage_id}
116
+ "
117
+ done <<< "$stages"
118
+
119
+ # Write YAML to file atomically
120
+ local tmp_file="${yaml_file}.tmp"
121
+ echo "$yaml_content" > "$tmp_file"
122
+ mv "$tmp_file" "$yaml_file"
123
+
124
+ success "Generated workflow: $yaml_file"
125
+ emit_event "ci_workflow_generated" "workflow=${workflow_name}" "stages=$(echo "$stages" | wc -l)"
126
+ }
127
+
128
+ # ─── Generate Test Matrix Configuration ─────────────────────────────────────
129
+
130
+ cmd_matrix() {
131
+ local output_file="${1:-.github/workflows/test-matrix.yml}"
132
+
133
+ info "Generating test matrix configuration"
134
+
135
+ local matrix_yaml
136
+ matrix_yaml="name: Test Matrix
137
+
138
+ on:
139
+ push:
140
+ branches: [main]
141
+ pull_request:
142
+ branches: [main]
143
+
144
+ jobs:
145
+ test:
146
+ runs-on: \${{ matrix.os }}
147
+ strategy:
148
+ fail-fast: false
149
+ matrix:
150
+ os: [ubuntu-latest, macos-latest]
151
+ bash-version: ['3.2', '4.0', '5.0', '5.2']
152
+ node-version: ['18', '20', 'lts/*']
153
+
154
+ steps:
155
+ - uses: actions/checkout@v4
156
+ - uses: actions/setup-node@v3
157
+ with:
158
+ node-version: \${{ matrix.node-version }}
159
+
160
+ - name: Install bash \${{ matrix.bash-version }}
161
+ run: |
162
+ if [[ \"\${{ runner.os }}\" == \"macOS\" ]]; then
163
+ brew install bash@\${{ matrix.bash-version }}
164
+ else
165
+ sudo apt-get update
166
+ sudo apt-get install -y bash
167
+ fi
168
+
169
+ - name: Run tests with bash \${{ matrix.bash-version }}
170
+ run: bash scripts/sw-pipeline-test.sh
171
+ "
172
+
173
+ mkdir -p ".github/workflows"
174
+ local tmp_file="${output_file}.tmp"
175
+ echo "$matrix_yaml" > "$tmp_file"
176
+ mv "$tmp_file" "$output_file"
177
+
178
+ success "Generated matrix config: $output_file"
179
+ emit_event "ci_matrix_generated" "os=2" "bash_versions=4" "node_versions=3"
180
+ }
181
+
182
+ # ─── Optimize Caching Strategy ─────────────────────────────────────────────
183
+
184
+ cmd_cache() {
185
+ local workflow_file="${1:-.github/workflows/test.yml}"
186
+
187
+ [[ ! -f "$workflow_file" ]] && {
188
+ error "Workflow file not found: $workflow_file"
189
+ exit 1
190
+ }
191
+
192
+ info "Optimizing caching strategy for workflow"
193
+
194
+ local cache_steps
195
+ cache_steps=' - name: Cache node_modules
196
+ uses: actions/cache@v3
197
+ with:
198
+ path: node_modules
199
+ key: ${{ runner.os }}-node-${{ hashFiles('"'"'package-lock.json'"'"') }}
200
+ restore-keys: |
201
+ ${{ runner.os }}-node-
202
+
203
+ - name: Cache npm packages
204
+ uses: actions/cache@v3
205
+ with:
206
+ path: ~/.npm
207
+ key: ${{ runner.os }}-npm-${{ hashFiles('"'"'package-lock.json'"'"') }}
208
+ restore-keys: |
209
+ ${{ runner.os }}-npm-
210
+
211
+ - name: Cache test results
212
+ uses: actions/cache@v3
213
+ with:
214
+ path: coverage
215
+ key: ${{ runner.os }}-coverage-${{ github.sha }}
216
+ restore-keys: |
217
+ ${{ runner.os }}-coverage-'
218
+
219
+ # Parse workflow and insert cache steps after checkout (basic insertion)
220
+ local tmp_file="${workflow_file}.tmp"
221
+ awk -v cache_steps="$cache_steps" '
222
+ /- uses: actions\/checkout@v/ {
223
+ print;
224
+ if (!printed_cache) {
225
+ print cache_steps;
226
+ printed_cache = 1
227
+ }
228
+ next
229
+ }
230
+ { print }
231
+ ' "$workflow_file" > "$tmp_file"
232
+
233
+ mv "$tmp_file" "$workflow_file"
234
+
235
+ success "Optimized caching in: $workflow_file"
236
+ emit_event "ci_cache_optimized" "workflow=$(basename "$workflow_file")"
237
+ }
238
+
239
+ # ─── Analyze Workflow Efficiency ────────────────────────────────────────────
240
+
241
+ cmd_analyze() {
242
+ local workflow_file="${1:-.github/workflows/test.yml}"
243
+
244
+ [[ ! -f "$workflow_file" ]] && {
245
+ error "Workflow file not found: $workflow_file"
246
+ exit 1
247
+ }
248
+
249
+ info "Analyzing workflow efficiency"
250
+
251
+ local job_count step_count matrix_enabled
252
+ job_count=$(grep -c "^ [a-z_-]*:" "$workflow_file" 2>/dev/null || echo "0")
253
+ step_count=$(grep -c " - name:" "$workflow_file" 2>/dev/null || echo "0")
254
+ matrix_enabled=$(grep -c "matrix:" "$workflow_file" 2>/dev/null || echo "0")
255
+
256
+ local has_cache
257
+ has_cache=$(grep -c "actions/cache" "$workflow_file" 2>/dev/null || echo "0")
258
+
259
+ local has_timeout
260
+ has_timeout=$(grep -c "timeout-minutes:" "$workflow_file" 2>/dev/null || echo "0")
261
+
262
+ echo ""
263
+ echo -e "${BOLD}Workflow Analysis: $(basename "$workflow_file")${RESET}"
264
+ echo -e " ${CYAN}Jobs:${RESET} $job_count"
265
+ echo -e " ${CYAN}Steps:${RESET} $step_count"
266
+ echo -e " ${CYAN}Matrix enabled:${RESET} $([ "$matrix_enabled" -gt 0 ] && echo "yes" || echo "no")"
267
+ echo -e " ${CYAN}Cache steps:${RESET} $has_cache"
268
+ echo -e " ${CYAN}Timeouts configured:${RESET} $([ "$has_timeout" -gt 0 ] && echo "yes" || echo "no")"
269
+ echo ""
270
+
271
+ # Recommendations
272
+ if [[ $has_cache -eq 0 ]]; then
273
+ warn "No caching detected. Run 'shipwright ci cache' to optimize"
274
+ fi
275
+
276
+ if [[ $has_timeout -eq 0 ]]; then
277
+ warn "No job timeouts configured. Consider adding timeout-minutes"
278
+ fi
279
+
280
+ success "Analysis complete"
281
+ emit_event "ci_workflow_analyzed" "jobs=$job_count" "steps=$step_count" "has_cache=$has_cache"
282
+ }
283
+
284
+ # ─── Audit Required Secrets ────────────────────────────────────────────────
285
+
286
+ cmd_secrets() {
287
+ local action="${1:-audit}"
288
+
289
+ case "$action" in
290
+ audit)
291
+ info "Auditing required secrets"
292
+
293
+ local required_secrets=()
294
+ [[ -f ".github/workflows/test.yml" ]] && {
295
+ required_secrets+=("GITHUB_TOKEN")
296
+ }
297
+
298
+ # Check for common secret patterns in workflows
299
+ local secrets_found
300
+ secrets_found=$(grep -rh '\${{ secrets\.' .github/workflows 2>/dev/null | \
301
+ grep -oE 'secrets\.[A-Z_]+' | sort -u | sed 's/secrets\.//' || true)
302
+
303
+ if [[ -z "$secrets_found" ]]; then
304
+ success "No secrets referenced in workflows"
305
+ else
306
+ echo ""
307
+ echo -e "${BOLD}Required Secrets:${RESET}"
308
+ echo "$secrets_found" | while read -r secret; do
309
+ echo -e " ${CYAN}•${RESET} $secret"
310
+ done
311
+ echo ""
312
+ fi
313
+
314
+ emit_event "ci_secrets_audited" "secrets_found=$(echo "$secrets_found" | wc -l)"
315
+ ;;
316
+
317
+ check)
318
+ info "Checking secret availability in GitHub"
319
+
320
+ local repo="${2:-.}"
321
+ local owner org_name
322
+
323
+ # Parse owner from git remote
324
+ owner=$(cd "$repo" && git config --get remote.origin.url | \
325
+ grep -oE '[:/]([^/]+)/[^/]+\.git' | sed 's|[:/]||g' | cut -d/ -f1)
326
+
327
+ if [[ -z "$owner" ]]; then
328
+ error "Could not determine repository owner"
329
+ exit 1
330
+ fi
331
+
332
+ success "Secrets check would query: $owner"
333
+ emit_event "ci_secrets_checked" "owner=$owner"
334
+ ;;
335
+
336
+ rotate)
337
+ warn "Secret rotation should be done manually in GitHub repository settings"
338
+ info "Visit: https://github.com/$owner/settings/secrets/actions"
339
+ ;;
340
+
341
+ *)
342
+ error "Unknown secrets action: $action"
343
+ exit 1
344
+ ;;
345
+ esac
346
+ }
347
+
348
+ # ─── Generate Reusable Workflow Templates ──────────────────────────────────
349
+
350
+ cmd_reusable() {
351
+ local template_name="${1:-base-test}"
352
+ local output_dir="${2:-.github/workflows}"
353
+
354
+ info "Generating reusable workflow template: $template_name"
355
+
356
+ mkdir -p "$output_dir"
357
+
358
+ local workflow_file="${output_dir}/${template_name}.yml"
359
+ local template_content
360
+
361
+ case "$template_name" in
362
+ base-test)
363
+ template_content='name: Reusable Test Workflow
364
+
365
+ on:
366
+ workflow_call:
367
+ inputs:
368
+ test-cmd:
369
+ required: true
370
+ type: string
371
+ os:
372
+ required: false
373
+ type: string
374
+ default: "ubuntu-latest"
375
+ secrets:
376
+ GITHUB_TOKEN:
377
+ required: true
378
+
379
+ jobs:
380
+ test:
381
+ runs-on: ${{ inputs.os }}
382
+ steps:
383
+ - uses: actions/checkout@v4
384
+ - name: Install dependencies
385
+ run: sudo apt-get update && sudo apt-get install -y jq tmux
386
+ - name: Run tests
387
+ run: ${{ inputs.test-cmd }}
388
+ env:
389
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
390
+ '
391
+ ;;
392
+
393
+ deploy)
394
+ template_content='name: Reusable Deploy Workflow
395
+
396
+ on:
397
+ workflow_call:
398
+ inputs:
399
+ environment:
400
+ required: true
401
+ type: string
402
+ deploy-cmd:
403
+ required: true
404
+ type: string
405
+ secrets:
406
+ DEPLOY_KEY:
407
+ required: true
408
+
409
+ jobs:
410
+ deploy:
411
+ runs-on: ubuntu-latest
412
+ environment: ${{ inputs.environment }}
413
+ steps:
414
+ - uses: actions/checkout@v4
415
+ - name: Deploy to ${{ inputs.environment }}
416
+ run: ${{ inputs.deploy-cmd }}
417
+ env:
418
+ DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
419
+ '
420
+ ;;
421
+
422
+ *)
423
+ error "Unknown template: $template_name"
424
+ exit 1
425
+ ;;
426
+ esac
427
+
428
+ local tmp_file="${workflow_file}.tmp"
429
+ echo "$template_content" > "$tmp_file"
430
+ mv "$tmp_file" "$workflow_file"
431
+
432
+ success "Generated reusable workflow: $workflow_file"
433
+ emit_event "ci_reusable_generated" "template=$template_name"
434
+ }
435
+
436
+ # ─── Generate Status Badges ────────────────────────────────────────────────
437
+
438
+ cmd_badges() {
439
+ local workflow_name="${1:-test}"
440
+ local repo="${2:-.}"
441
+
442
+ info "Generating status badges for workflow: $workflow_name"
443
+
444
+ # Extract owner/repo from git remote
445
+ local owner repo_name
446
+ owner=$(cd "$repo" && git config --get remote.origin.url | \
447
+ grep -oE '[:/]([^/]+)/[^/]+\.git' | sed 's|[:/]||g' | cut -d/ -f1)
448
+ repo_name=$(cd "$repo" && git config --get remote.origin.url | \
449
+ grep -oE '[^/]+\.git$' | sed 's/\.git//')
450
+
451
+ if [[ -z "$owner" ]] || [[ -z "$repo_name" ]]; then
452
+ error "Could not determine repository owner/name"
453
+ exit 1
454
+ fi
455
+
456
+ echo ""
457
+ echo -e "${BOLD}Status Badge Markdown:${RESET}"
458
+ echo ""
459
+ echo "[![${workflow_name}](https://github.com/${owner}/${repo_name}/actions/workflows/${workflow_name}.yml/badge.svg)](https://github.com/${owner}/${repo_name}/actions/workflows/${workflow_name}.yml)"
460
+ echo ""
461
+
462
+ success "Badges generated for ${owner}/${repo_name}"
463
+ emit_event "ci_badges_generated" "workflow=$workflow_name" "repo=${owner}/${repo_name}"
464
+ }
465
+
466
+ # ─── Validate Workflow YAML Syntax ─────────────────────────────────────────
467
+
468
+ cmd_validate() {
469
+ local workflow_file="${1:-.github/workflows/test.yml}"
470
+
471
+ [[ ! -f "$workflow_file" ]] && {
472
+ error "Workflow file not found: $workflow_file"
473
+ exit 1
474
+ }
475
+
476
+ info "Validating workflow YAML: $workflow_file"
477
+
478
+ # Check for valid YAML structure
479
+ if ! jq -e 'type' <<< "$(yq eval -o=json "$workflow_file" 2>/dev/null || echo '{}')" &>/dev/null; then
480
+ # Fallback: basic validation
481
+ if grep -q "^name:" "$workflow_file" && grep -q "^on:" "$workflow_file" && grep -q "^jobs:" "$workflow_file"; then
482
+ success "Workflow structure looks valid"
483
+ emit_event "ci_workflow_validated" "file=$(basename "$workflow_file")"
484
+ else
485
+ error "Workflow missing required sections (name, on, jobs)"
486
+ exit 1
487
+ fi
488
+ else
489
+ success "Workflow YAML is valid"
490
+ emit_event "ci_workflow_validated" "file=$(basename "$workflow_file")"
491
+ fi
492
+ }
493
+
494
+ # ─── Runner Management & Recommendations ────────────────────────────────────
495
+
496
+ cmd_runners() {
497
+ local action="${1:-list}"
498
+
499
+ case "$action" in
500
+ list)
501
+ info "GitHub-hosted runners available:"
502
+ echo ""
503
+ echo -e " ${CYAN}ubuntu-latest${RESET} - Linux (Ubuntu 22.04)"
504
+ echo -e " ${CYAN}macos-latest${RESET} - macOS (ARM64)"
505
+ echo -e " ${CYAN}windows-latest${RESET} - Windows Server 2022"
506
+ echo -e " ${CYAN}ubuntu-20.04${RESET} - Linux (Ubuntu 20.04)"
507
+ echo -e " ${CYAN}macos-12${RESET} - macOS (Intel)"
508
+ echo ""
509
+ emit_event "ci_runners_listed"
510
+ ;;
511
+
512
+ recommend)
513
+ info "Runner recommendations based on workload"
514
+ echo ""
515
+ echo -e " ${CYAN}Build/test (fast):${RESET} ubuntu-latest"
516
+ echo -e " ${CYAN}Multi-OS testing:${RESET} matrix [ubuntu, macos, windows]"
517
+ echo -e " ${CYAN}Bash scripting:${RESET} ubuntu-latest or macos-latest"
518
+ echo -e " ${CYAN}Production deploy:${RESET} self-hosted runner"
519
+ echo ""
520
+ emit_event "ci_runners_recommended"
521
+ ;;
522
+
523
+ *)
524
+ error "Unknown runners action: $action"
525
+ exit 1
526
+ ;;
527
+ esac
528
+ }
529
+
530
+ # ─── Show Help ──────────────────────────────────────────────────────────────
531
+
532
+ show_help() {
533
+ cat << EOF
534
+ ${CYAN}${BOLD}shipwright ci${RESET} — GitHub Actions CI/CD Orchestration
535
+
536
+ ${BOLD}USAGE${RESET}
537
+ shipwright ci <command> [options]
538
+
539
+ ${BOLD}COMMANDS${RESET}
540
+ ${CYAN}generate${RESET} [config] [name] Generate workflow YAML from pipeline config
541
+ ${CYAN}analyze${RESET} [workflow] Analyze workflow efficiency and recommendations
542
+ ${CYAN}matrix${RESET} [output] Generate test matrix configuration
543
+ ${CYAN}cache${RESET} [workflow] Optimize caching strategy in workflow
544
+ ${CYAN}secrets${RESET} <audit|check|rotate> Manage and audit required secrets
545
+ ${CYAN}reusable${RESET} [template] [dir] Generate reusable workflow templates
546
+ ${CYAN}badges${RESET} [workflow] [repo] Generate status badge markdown
547
+ ${CYAN}runners${RESET} <list|recommend> Runner management and recommendations
548
+ ${CYAN}validate${RESET} [workflow] Validate workflow YAML syntax
549
+ ${CYAN}help${RESET} Show this help message
550
+ ${CYAN}version${RESET} Show version
551
+
552
+ ${BOLD}EXAMPLES${RESET}
553
+ ${DIM}shipwright ci generate${RESET} # Generate from composed pipeline
554
+ ${DIM}shipwright ci matrix${RESET} # Create test matrix with bash/node versions
555
+ ${DIM}shipwright ci analyze .github/workflows/test.yml${RESET}
556
+ ${DIM}shipwright ci cache .github/workflows/test.yml${RESET}
557
+ ${DIM}shipwright ci secrets audit${RESET}
558
+ ${DIM}shipwright ci badges test${RESET}
559
+ ${DIM}shipwright ci validate${RESET}
560
+
561
+ ${BOLD}FEATURES${RESET}
562
+ • Workflow generation from pipeline templates
563
+ • Multi-OS and multi-version test matrices
564
+ • Smart dependency caching optimization
565
+ • Workflow efficiency analysis
566
+ • Secret auditing and rotation guidance
567
+ • Reusable workflow templates
568
+ • Status badge generation
569
+ • YAML validation
570
+
571
+ EOF
572
+ }
573
+
574
+ # ─── Source Guard & Main ────────────────────────────────────────────────────
575
+
576
+ if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
577
+ cmd="${1:-help}"
578
+
579
+ case "$cmd" in
580
+ generate) shift; cmd_generate "$@" ;;
581
+ analyze) shift; cmd_analyze "$@" ;;
582
+ matrix) shift; cmd_matrix "$@" ;;
583
+ cache) shift; cmd_cache "$@" ;;
584
+ secrets) shift; cmd_secrets "$@" ;;
585
+ reusable) shift; cmd_reusable "$@" ;;
586
+ badges) shift; cmd_badges "$@" ;;
587
+ runners) shift; cmd_runners "$@" ;;
588
+ validate) shift; cmd_validate "$@" ;;
589
+ help|--help|-h)
590
+ show_help
591
+ ;;
592
+ version|--version|-v)
593
+ echo "shipwright ci v${VERSION}"
594
+ ;;
595
+ *)
596
+ error "Unknown command: $cmd"
597
+ echo ""
598
+ show_help
599
+ exit 1
600
+ ;;
601
+ esac
602
+ fi
@@ -5,7 +5,7 @@
5
5
  # ║ Default: dry-run (shows what would be cleaned). ║
6
6
  # ║ Use --force to actually kill sessions and remove files. ║
7
7
  # ╚═══════════════════════════════════════════════════════════════════════════╝
8
- VERSION="1.10.0"
8
+ VERSION="2.0.0"
9
9
  set -euo pipefail
10
10
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
11
11