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.
- package/README.md +114 -36
- package/completions/_shipwright +212 -32
- package/completions/shipwright.bash +97 -25
- package/docs/strategy/01-market-research.md +619 -0
- package/docs/strategy/02-mission-and-brand.md +587 -0
- package/docs/strategy/03-gtm-and-roadmap.md +759 -0
- package/docs/strategy/QUICK-START.txt +289 -0
- package/docs/strategy/README.md +172 -0
- package/package.json +4 -2
- package/scripts/sw +208 -1
- package/scripts/sw-activity.sh +500 -0
- package/scripts/sw-adaptive.sh +925 -0
- package/scripts/sw-adversarial.sh +1 -1
- package/scripts/sw-architecture-enforcer.sh +1 -1
- package/scripts/sw-auth.sh +613 -0
- package/scripts/sw-autonomous.sh +664 -0
- package/scripts/sw-changelog.sh +704 -0
- package/scripts/sw-checkpoint.sh +1 -1
- package/scripts/sw-ci.sh +602 -0
- package/scripts/sw-cleanup.sh +1 -1
- package/scripts/sw-code-review.sh +637 -0
- package/scripts/sw-connect.sh +1 -1
- package/scripts/sw-context.sh +605 -0
- package/scripts/sw-cost.sh +1 -1
- package/scripts/sw-daemon.sh +432 -130
- package/scripts/sw-dashboard.sh +1 -1
- package/scripts/sw-db.sh +540 -0
- package/scripts/sw-decompose.sh +539 -0
- package/scripts/sw-deps.sh +551 -0
- package/scripts/sw-developer-simulation.sh +1 -1
- package/scripts/sw-discovery.sh +412 -0
- package/scripts/sw-docs-agent.sh +539 -0
- package/scripts/sw-docs.sh +1 -1
- package/scripts/sw-doctor.sh +59 -1
- package/scripts/sw-dora.sh +615 -0
- package/scripts/sw-durable.sh +710 -0
- package/scripts/sw-e2e-orchestrator.sh +535 -0
- package/scripts/sw-eventbus.sh +393 -0
- package/scripts/sw-feedback.sh +471 -0
- package/scripts/sw-fix.sh +1 -1
- package/scripts/sw-fleet-discover.sh +567 -0
- package/scripts/sw-fleet-viz.sh +404 -0
- package/scripts/sw-fleet.sh +8 -1
- package/scripts/sw-github-app.sh +596 -0
- package/scripts/sw-github-checks.sh +1 -1
- package/scripts/sw-github-deploy.sh +1 -1
- package/scripts/sw-github-graphql.sh +1 -1
- package/scripts/sw-guild.sh +569 -0
- package/scripts/sw-heartbeat.sh +1 -1
- package/scripts/sw-hygiene.sh +559 -0
- package/scripts/sw-incident.sh +617 -0
- package/scripts/sw-init.sh +88 -1
- package/scripts/sw-instrument.sh +699 -0
- package/scripts/sw-intelligence.sh +1 -1
- package/scripts/sw-jira.sh +1 -1
- package/scripts/sw-launchd.sh +363 -28
- package/scripts/sw-linear.sh +1 -1
- package/scripts/sw-logs.sh +1 -1
- package/scripts/sw-loop.sh +64 -3
- package/scripts/sw-memory.sh +1 -1
- package/scripts/sw-mission-control.sh +487 -0
- package/scripts/sw-model-router.sh +545 -0
- package/scripts/sw-otel.sh +596 -0
- package/scripts/sw-oversight.sh +689 -0
- package/scripts/sw-pipeline-composer.sh +1 -1
- package/scripts/sw-pipeline-vitals.sh +1 -1
- package/scripts/sw-pipeline.sh +687 -24
- package/scripts/sw-pm.sh +693 -0
- package/scripts/sw-pr-lifecycle.sh +522 -0
- package/scripts/sw-predictive.sh +1 -1
- package/scripts/sw-prep.sh +1 -1
- package/scripts/sw-ps.sh +1 -1
- package/scripts/sw-public-dashboard.sh +798 -0
- package/scripts/sw-quality.sh +595 -0
- package/scripts/sw-reaper.sh +1 -1
- package/scripts/sw-recruit.sh +573 -0
- package/scripts/sw-regression.sh +642 -0
- package/scripts/sw-release-manager.sh +736 -0
- package/scripts/sw-release.sh +706 -0
- package/scripts/sw-remote.sh +1 -1
- package/scripts/sw-replay.sh +520 -0
- package/scripts/sw-retro.sh +691 -0
- package/scripts/sw-scale.sh +444 -0
- package/scripts/sw-security-audit.sh +505 -0
- package/scripts/sw-self-optimize.sh +1 -1
- package/scripts/sw-session.sh +1 -1
- package/scripts/sw-setup.sh +1 -1
- package/scripts/sw-standup.sh +712 -0
- package/scripts/sw-status.sh +1 -1
- package/scripts/sw-strategic.sh +658 -0
- package/scripts/sw-stream.sh +450 -0
- package/scripts/sw-swarm.sh +583 -0
- package/scripts/sw-team-stages.sh +511 -0
- package/scripts/sw-templates.sh +1 -1
- package/scripts/sw-testgen.sh +515 -0
- package/scripts/sw-tmux-pipeline.sh +554 -0
- package/scripts/sw-tmux.sh +1 -1
- package/scripts/sw-trace.sh +485 -0
- package/scripts/sw-tracker-github.sh +188 -0
- package/scripts/sw-tracker-jira.sh +172 -0
- package/scripts/sw-tracker-linear.sh +251 -0
- package/scripts/sw-tracker.sh +117 -2
- package/scripts/sw-triage.sh +603 -0
- package/scripts/sw-upgrade.sh +1 -1
- package/scripts/sw-ux.sh +677 -0
- package/scripts/sw-webhook.sh +627 -0
- package/scripts/sw-widgets.sh +530 -0
- package/scripts/sw-worktree.sh +1 -1
package/scripts/sw-checkpoint.sh
CHANGED
|
@@ -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="
|
|
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) ─────────────────────────────────────
|
package/scripts/sw-ci.sh
ADDED
|
@@ -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 "[](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
|
package/scripts/sw-cleanup.sh
CHANGED
|
@@ -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="
|
|
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
|
|