shipwright-cli 2.4.0 → 3.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 +16 -11
- package/completions/_shipwright +1 -1
- package/completions/shipwright.bash +3 -8
- package/completions/shipwright.fish +1 -1
- package/config/defaults.json +111 -0
- package/config/event-schema.json +81 -0
- package/config/policy.json +13 -18
- package/dashboard/coverage/coverage-summary.json +14 -0
- package/dashboard/public/index.html +1 -1
- package/dashboard/server.ts +306 -17
- package/dashboard/src/components/charts/bar.test.ts +79 -0
- package/dashboard/src/components/charts/donut.test.ts +68 -0
- package/dashboard/src/components/charts/pipeline-rail.test.ts +117 -0
- package/dashboard/src/components/charts/sparkline.test.ts +125 -0
- package/dashboard/src/core/api.test.ts +309 -0
- package/dashboard/src/core/helpers.test.ts +301 -0
- package/dashboard/src/core/router.test.ts +307 -0
- package/dashboard/src/core/router.ts +7 -0
- package/dashboard/src/core/sse.test.ts +144 -0
- package/dashboard/src/views/metrics.test.ts +186 -0
- package/dashboard/src/views/overview.test.ts +173 -0
- package/dashboard/src/views/pipelines.test.ts +183 -0
- package/dashboard/src/views/team.test.ts +253 -0
- package/dashboard/vitest.config.ts +14 -5
- package/docs/TIPS.md +1 -1
- package/docs/patterns/README.md +1 -1
- package/package.json +5 -7
- package/scripts/adapters/docker-deploy.sh +1 -1
- package/scripts/adapters/tmux-adapter.sh +11 -1
- package/scripts/adapters/wezterm-adapter.sh +1 -1
- package/scripts/check-version-consistency.sh +1 -1
- package/scripts/lib/architecture.sh +126 -0
- package/scripts/lib/bootstrap.sh +75 -0
- package/scripts/lib/compat.sh +89 -6
- package/scripts/lib/config.sh +91 -0
- package/scripts/lib/daemon-adaptive.sh +3 -3
- package/scripts/lib/daemon-dispatch.sh +39 -16
- package/scripts/lib/daemon-health.sh +1 -1
- package/scripts/lib/daemon-patrol.sh +24 -12
- package/scripts/lib/daemon-poll.sh +37 -25
- package/scripts/lib/daemon-state.sh +115 -23
- package/scripts/lib/daemon-triage.sh +30 -8
- package/scripts/lib/fleet-failover.sh +63 -0
- package/scripts/lib/helpers.sh +30 -6
- package/scripts/lib/pipeline-detection.sh +2 -2
- package/scripts/lib/pipeline-github.sh +9 -9
- package/scripts/lib/pipeline-intelligence.sh +85 -35
- package/scripts/lib/pipeline-quality-checks.sh +16 -16
- package/scripts/lib/pipeline-quality.sh +1 -1
- package/scripts/lib/pipeline-stages.sh +242 -28
- package/scripts/lib/pipeline-state.sh +40 -4
- package/scripts/lib/test-helpers.sh +247 -0
- package/scripts/postinstall.mjs +3 -11
- package/scripts/sw +10 -4
- package/scripts/sw-activity.sh +1 -11
- package/scripts/sw-adaptive.sh +109 -85
- package/scripts/sw-adversarial.sh +4 -14
- package/scripts/sw-architecture-enforcer.sh +1 -11
- package/scripts/sw-auth.sh +8 -17
- package/scripts/sw-autonomous.sh +111 -49
- package/scripts/sw-changelog.sh +1 -11
- package/scripts/sw-checkpoint.sh +144 -20
- package/scripts/sw-ci.sh +2 -12
- package/scripts/sw-cleanup.sh +13 -17
- package/scripts/sw-code-review.sh +16 -36
- package/scripts/sw-connect.sh +5 -12
- package/scripts/sw-context.sh +9 -26
- package/scripts/sw-cost.sh +6 -16
- package/scripts/sw-daemon.sh +75 -70
- package/scripts/sw-dashboard.sh +57 -17
- package/scripts/sw-db.sh +506 -15
- package/scripts/sw-decompose.sh +1 -11
- package/scripts/sw-deps.sh +15 -25
- package/scripts/sw-developer-simulation.sh +1 -11
- package/scripts/sw-discovery.sh +112 -30
- package/scripts/sw-doc-fleet.sh +7 -17
- package/scripts/sw-docs-agent.sh +6 -16
- package/scripts/sw-docs.sh +4 -12
- package/scripts/sw-doctor.sh +134 -43
- package/scripts/sw-dora.sh +11 -19
- package/scripts/sw-durable.sh +35 -52
- package/scripts/sw-e2e-orchestrator.sh +11 -27
- package/scripts/sw-eventbus.sh +115 -115
- package/scripts/sw-evidence.sh +114 -30
- package/scripts/sw-feedback.sh +3 -13
- package/scripts/sw-fix.sh +2 -20
- package/scripts/sw-fleet-discover.sh +1 -11
- package/scripts/sw-fleet-viz.sh +10 -18
- package/scripts/sw-fleet.sh +13 -17
- package/scripts/sw-github-app.sh +6 -16
- package/scripts/sw-github-checks.sh +1 -11
- package/scripts/sw-github-deploy.sh +1 -11
- package/scripts/sw-github-graphql.sh +2 -12
- package/scripts/sw-guild.sh +1 -11
- package/scripts/sw-heartbeat.sh +49 -12
- package/scripts/sw-hygiene.sh +45 -43
- package/scripts/sw-incident.sh +48 -74
- package/scripts/sw-init.sh +35 -37
- package/scripts/sw-instrument.sh +1 -11
- package/scripts/sw-intelligence.sh +362 -51
- package/scripts/sw-jira.sh +5 -14
- package/scripts/sw-launchd.sh +2 -12
- package/scripts/sw-linear.sh +8 -17
- package/scripts/sw-logs.sh +4 -12
- package/scripts/sw-loop.sh +641 -90
- package/scripts/sw-memory.sh +243 -17
- package/scripts/sw-mission-control.sh +2 -12
- package/scripts/sw-model-router.sh +73 -34
- package/scripts/sw-otel.sh +11 -21
- package/scripts/sw-oversight.sh +1 -11
- package/scripts/sw-patrol-meta.sh +5 -11
- package/scripts/sw-pipeline-composer.sh +7 -17
- package/scripts/sw-pipeline-vitals.sh +1 -11
- package/scripts/sw-pipeline.sh +478 -122
- package/scripts/sw-pm.sh +2 -12
- package/scripts/sw-pr-lifecycle.sh +27 -25
- package/scripts/sw-predictive.sh +16 -22
- package/scripts/sw-prep.sh +6 -16
- package/scripts/sw-ps.sh +1 -11
- package/scripts/sw-public-dashboard.sh +2 -12
- package/scripts/sw-quality.sh +77 -10
- package/scripts/sw-reaper.sh +1 -11
- package/scripts/sw-recruit.sh +15 -25
- package/scripts/sw-regression.sh +11 -21
- package/scripts/sw-release-manager.sh +19 -28
- package/scripts/sw-release.sh +8 -16
- package/scripts/sw-remote.sh +1 -11
- package/scripts/sw-replay.sh +48 -44
- package/scripts/sw-retro.sh +70 -92
- package/scripts/sw-review-rerun.sh +1 -1
- package/scripts/sw-scale.sh +109 -32
- package/scripts/sw-security-audit.sh +12 -22
- package/scripts/sw-self-optimize.sh +239 -23
- package/scripts/sw-session.sh +3 -13
- package/scripts/sw-setup.sh +8 -18
- package/scripts/sw-standup.sh +5 -15
- package/scripts/sw-status.sh +32 -23
- package/scripts/sw-strategic.sh +129 -13
- package/scripts/sw-stream.sh +1 -11
- package/scripts/sw-swarm.sh +76 -36
- package/scripts/sw-team-stages.sh +10 -20
- package/scripts/sw-templates.sh +4 -14
- package/scripts/sw-testgen.sh +3 -13
- package/scripts/sw-tmux-pipeline.sh +1 -19
- package/scripts/sw-tmux-role-color.sh +0 -10
- package/scripts/sw-tmux-status.sh +3 -11
- package/scripts/sw-tmux.sh +2 -20
- package/scripts/sw-trace.sh +1 -19
- package/scripts/sw-tracker-github.sh +0 -10
- package/scripts/sw-tracker-jira.sh +1 -11
- package/scripts/sw-tracker-linear.sh +1 -11
- package/scripts/sw-tracker.sh +7 -24
- package/scripts/sw-triage.sh +24 -34
- package/scripts/sw-upgrade.sh +5 -23
- package/scripts/sw-ux.sh +1 -19
- package/scripts/sw-webhook.sh +18 -32
- package/scripts/sw-widgets.sh +3 -21
- package/scripts/sw-worktree.sh +11 -27
- package/scripts/update-homebrew-sha.sh +67 -0
- package/templates/pipelines/tdd.json +72 -0
- package/scripts/sw-pipeline.sh.mock +0 -7
package/scripts/sw-evidence.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="3.0.0"
|
|
12
12
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
13
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
14
14
|
|
|
@@ -16,6 +16,7 @@ REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
16
16
|
[[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
|
|
17
17
|
# shellcheck source=lib/helpers.sh
|
|
18
18
|
[[ -f "$SCRIPT_DIR/lib/helpers.sh" ]] && source "$SCRIPT_DIR/lib/helpers.sh"
|
|
19
|
+
[[ -f "$SCRIPT_DIR/lib/config.sh" ]] && source "$SCRIPT_DIR/lib/config.sh"
|
|
19
20
|
[[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
|
|
20
21
|
[[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
|
|
21
22
|
[[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
|
|
@@ -24,21 +25,13 @@ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
|
|
|
24
25
|
now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
|
|
25
26
|
now_epoch() { date +%s; }
|
|
26
27
|
fi
|
|
27
|
-
if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
28
|
-
emit_event() {
|
|
29
|
-
local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
|
|
30
|
-
local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
|
|
31
|
-
while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
|
|
32
|
-
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
33
|
-
}
|
|
34
|
-
fi
|
|
35
28
|
|
|
36
29
|
# Cross-platform timeout: macOS lacks GNU timeout
|
|
37
30
|
_run_with_timeout() {
|
|
38
31
|
local secs="$1"; shift
|
|
39
|
-
if command -v gtimeout
|
|
32
|
+
if command -v gtimeout >/dev/null 2>&1; then
|
|
40
33
|
gtimeout "$secs" "$@"
|
|
41
|
-
elif command -v timeout
|
|
34
|
+
elif command -v timeout >/dev/null 2>&1; then
|
|
42
35
|
timeout "$secs" "$@"
|
|
43
36
|
else
|
|
44
37
|
# Fallback: run without timeout
|
|
@@ -83,6 +76,61 @@ get_require_fresh() {
|
|
|
83
76
|
fi
|
|
84
77
|
}
|
|
85
78
|
|
|
79
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
80
|
+
# ASSERTION EVALUATION
|
|
81
|
+
# Checks response body against assertions defined in policy.json.
|
|
82
|
+
# Assertion names map to simple content checks (case-insensitive).
|
|
83
|
+
# Returns the count of failed assertions (0 = all passed).
|
|
84
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
85
|
+
|
|
86
|
+
evaluate_assertions() {
|
|
87
|
+
local collector_json="$1"
|
|
88
|
+
local response_body="$2"
|
|
89
|
+
local failed=0
|
|
90
|
+
|
|
91
|
+
local assertions
|
|
92
|
+
assertions=$(echo "$collector_json" | jq -r '.assertions[]? // empty' 2>/dev/null)
|
|
93
|
+
[[ -z "$assertions" ]] && { echo "0"; return; }
|
|
94
|
+
|
|
95
|
+
while IFS= read -r assertion; do
|
|
96
|
+
[[ -z "$assertion" ]] && continue
|
|
97
|
+
local check_passed="false"
|
|
98
|
+
|
|
99
|
+
case "$assertion" in
|
|
100
|
+
# Common assertion patterns — map names to body content checks
|
|
101
|
+
page-title-visible)
|
|
102
|
+
echo "$response_body" | grep -qi '<title>' && check_passed="true" ;;
|
|
103
|
+
websocket-connected|websocket-active)
|
|
104
|
+
echo "$response_body" | grep -qi 'websocket\|ws://' && check_passed="true" ;;
|
|
105
|
+
status-ok)
|
|
106
|
+
echo "$response_body" | grep -qi '"status"' && check_passed="true" ;;
|
|
107
|
+
response-has-version)
|
|
108
|
+
echo "$response_body" | grep -qi '"version"' && check_passed="true" ;;
|
|
109
|
+
valid-json-output|valid-json)
|
|
110
|
+
echo "$response_body" | jq empty 2>/dev/null && check_passed="true" ;;
|
|
111
|
+
has-pipeline-state)
|
|
112
|
+
echo "$response_body" | grep -qi 'pipeline\|status\|stage' && check_passed="true" ;;
|
|
113
|
+
stage-list-rendered)
|
|
114
|
+
echo "$response_body" | grep -qi 'stage\|pipeline' && check_passed="true" ;;
|
|
115
|
+
progress-indicator-visible)
|
|
116
|
+
echo "$response_body" | grep -qi 'progress\|percent\|stage' && check_passed="true" ;;
|
|
117
|
+
schema-valid|db-accessible)
|
|
118
|
+
echo "$response_body" | grep -qi 'schema\|version\|ok\|healthy' && check_passed="true" ;;
|
|
119
|
+
*)
|
|
120
|
+
# Generic: check if the assertion name (with hyphens as spaces) appears in body
|
|
121
|
+
local search_term="${assertion//-/ }"
|
|
122
|
+
echo "$response_body" | grep -qi "$search_term" && check_passed="true" ;;
|
|
123
|
+
esac
|
|
124
|
+
|
|
125
|
+
if [[ "$check_passed" != "true" ]]; then
|
|
126
|
+
warn "[assertion] '${assertion}' not satisfied" >&2
|
|
127
|
+
failed=$((failed + 1))
|
|
128
|
+
fi
|
|
129
|
+
done <<< "$assertions"
|
|
130
|
+
|
|
131
|
+
echo "$failed"
|
|
132
|
+
}
|
|
133
|
+
|
|
86
134
|
# ═════════════════════════════════════════════════════════════════════════════
|
|
87
135
|
# TYPE-SPECIFIC COLLECTORS
|
|
88
136
|
# Each returns a JSON evidence record written to EVIDENCE_DIR/<name>.json
|
|
@@ -96,7 +144,8 @@ collect_browser() {
|
|
|
96
144
|
|
|
97
145
|
local entrypoint base_url url
|
|
98
146
|
entrypoint=$(echo "$collector_json" | jq -r '.entrypoint // "/"')
|
|
99
|
-
base_url=$(echo "$collector_json" | jq -r '.baseUrl // "
|
|
147
|
+
base_url=$(echo "$collector_json" | jq -r '.baseUrl // ""')
|
|
148
|
+
[[ -z "$base_url" ]] && base_url="http://localhost:$(_config_get_int "dashboard.port" 8767)"
|
|
100
149
|
url="${base_url}${entrypoint}"
|
|
101
150
|
|
|
102
151
|
info "[browser] ${name}: ${url}"
|
|
@@ -105,8 +154,9 @@ collect_browser() {
|
|
|
105
154
|
local response_size="0"
|
|
106
155
|
local response_body=""
|
|
107
156
|
|
|
108
|
-
if command -v curl
|
|
109
|
-
local tmpfile
|
|
157
|
+
if command -v curl >/dev/null 2>&1; then
|
|
158
|
+
local tmpfile
|
|
159
|
+
tmpfile=$(mktemp "${TMPDIR:-/tmp}/sw-evidence-browser.XXXXXX")
|
|
110
160
|
http_status=$(curl -s -o "$tmpfile" -w "%{http_code}" --max-time 30 "$url" 2>/dev/null || echo "0")
|
|
111
161
|
if [[ -f "$tmpfile" ]]; then
|
|
112
162
|
response_size=$(wc -c < "$tmpfile" 2>/dev/null || echo "0")
|
|
@@ -118,9 +168,17 @@ collect_browser() {
|
|
|
118
168
|
local passed="false"
|
|
119
169
|
[[ "$http_status" -ge 200 && "$http_status" -lt 400 ]] && passed="true"
|
|
120
170
|
|
|
171
|
+
# Evaluate assertions against response body (if status check passed)
|
|
172
|
+
local assertion_failures=0
|
|
173
|
+
if [[ "$passed" == "true" && -n "$response_body" ]]; then
|
|
174
|
+
assertion_failures=$(evaluate_assertions "$collector_json" "$response_body")
|
|
175
|
+
[[ "$assertion_failures" -gt 0 ]] && passed="false"
|
|
176
|
+
fi
|
|
177
|
+
|
|
121
178
|
write_evidence_record "$name" "browser" "$passed" \
|
|
122
179
|
"$(jq -n --arg url "$url" --argjson status "$http_status" --argjson size "$response_size" \
|
|
123
|
-
|
|
180
|
+
--argjson assertion_failures "$assertion_failures" \
|
|
181
|
+
'{url: $url, http_status: $status, response_size: $size, assertion_failures: $assertion_failures}')"
|
|
124
182
|
}
|
|
125
183
|
|
|
126
184
|
# ─── API: REST/GraphQL endpoint verification ─────────────────────────────────
|
|
@@ -138,7 +196,8 @@ collect_api() {
|
|
|
138
196
|
|
|
139
197
|
if [[ -z "$url" ]]; then
|
|
140
198
|
local base_url entrypoint
|
|
141
|
-
base_url=$(echo "$collector_json" | jq -r '.baseUrl // "
|
|
199
|
+
base_url=$(echo "$collector_json" | jq -r '.baseUrl // ""')
|
|
200
|
+
[[ -z "$base_url" ]] && base_url="http://localhost:$(_config_get_int "dashboard.port" 8767)"
|
|
142
201
|
entrypoint=$(echo "$collector_json" | jq -r '.entrypoint // "/"')
|
|
143
202
|
url="${base_url}${entrypoint}"
|
|
144
203
|
fi
|
|
@@ -150,9 +209,10 @@ collect_api() {
|
|
|
150
209
|
local response_body=""
|
|
151
210
|
local content_type=""
|
|
152
211
|
|
|
153
|
-
if command -v curl
|
|
154
|
-
local tmpfile
|
|
155
|
-
|
|
212
|
+
if command -v curl >/dev/null 2>&1; then
|
|
213
|
+
local tmpfile header_file
|
|
214
|
+
tmpfile=$(mktemp "${TMPDIR:-/tmp}/sw-evidence-api.XXXXXX")
|
|
215
|
+
header_file=$(mktemp "${TMPDIR:-/tmp}/sw-evidence-api-headers.XXXXXX")
|
|
156
216
|
local curl_args=(-s -o "$tmpfile" -D "$header_file" -w "%{http_code}" -X "$method" --max-time "$timeout")
|
|
157
217
|
|
|
158
218
|
# Add custom headers
|
|
@@ -181,6 +241,7 @@ collect_api() {
|
|
|
181
241
|
content_type=$(grep -i "^content-type:" "$header_file" 2>/dev/null | head -1 | sed 's/^[^:]*: *//' | tr -d '\r' || echo "")
|
|
182
242
|
rm -f "$header_file"
|
|
183
243
|
fi
|
|
244
|
+
rm -f "$tmpfile"
|
|
184
245
|
fi
|
|
185
246
|
|
|
186
247
|
local passed="false"
|
|
@@ -192,12 +253,19 @@ collect_api() {
|
|
|
192
253
|
valid_json="true"
|
|
193
254
|
fi
|
|
194
255
|
|
|
256
|
+
# Evaluate assertions against response body (if status check passed)
|
|
257
|
+
local assertion_failures=0
|
|
258
|
+
if [[ "$passed" == "true" && -n "$response_body" ]]; then
|
|
259
|
+
assertion_failures=$(evaluate_assertions "$collector_json" "$response_body")
|
|
260
|
+
[[ "$assertion_failures" -gt 0 ]] && passed="false"
|
|
261
|
+
fi
|
|
262
|
+
|
|
195
263
|
write_evidence_record "$name" "api" "$passed" \
|
|
196
264
|
"$(jq -n --arg url "$url" --arg method "$method" \
|
|
197
265
|
--argjson status "$http_status" --argjson expected "$expected_status" \
|
|
198
266
|
--argjson size "$response_size" --arg content_type "$content_type" \
|
|
199
|
-
--arg valid_json "$valid_json" \
|
|
200
|
-
'{url: $url, method: $method, http_status: $status, expected_status: $expected, response_size: $size, content_type: $content_type, valid_json: ($valid_json == "true")}')"
|
|
267
|
+
--arg valid_json "$valid_json" --argjson assertion_failures "$assertion_failures" \
|
|
268
|
+
'{url: $url, method: $method, http_status: $status, expected_status: $expected, response_size: $size, content_type: $content_type, valid_json: ($valid_json == "true"), assertion_failures: $assertion_failures}')"
|
|
201
269
|
}
|
|
202
270
|
|
|
203
271
|
# ─── CLI: Execute a command and check exit code ──────────────────────────────
|
|
@@ -236,6 +304,13 @@ collect_cli() {
|
|
|
236
304
|
valid_json="true"
|
|
237
305
|
fi
|
|
238
306
|
|
|
307
|
+
# Evaluate assertions against command output (if exit code check passed)
|
|
308
|
+
local assertion_failures=0
|
|
309
|
+
if [[ "$passed" == "true" && -n "$output" ]]; then
|
|
310
|
+
assertion_failures=$(evaluate_assertions "$collector_json" "$output")
|
|
311
|
+
[[ "$assertion_failures" -gt 0 ]] && passed="false"
|
|
312
|
+
fi
|
|
313
|
+
|
|
239
314
|
local output_size=${#output}
|
|
240
315
|
# Truncate output for the evidence record (keep first 2000 chars)
|
|
241
316
|
local output_preview="${output:0:2000}"
|
|
@@ -274,12 +349,20 @@ collect_database() {
|
|
|
274
349
|
local passed="false"
|
|
275
350
|
[[ "$exit_code" -eq "$expected_exit" ]] && passed="true"
|
|
276
351
|
|
|
352
|
+
# Evaluate assertions against command output (if exit code check passed)
|
|
353
|
+
local assertion_failures=0
|
|
354
|
+
if [[ "$passed" == "true" && -n "$output" ]]; then
|
|
355
|
+
assertion_failures=$(evaluate_assertions "$collector_json" "$output")
|
|
356
|
+
[[ "$assertion_failures" -gt 0 ]] && passed="false"
|
|
357
|
+
fi
|
|
358
|
+
|
|
277
359
|
local output_preview="${output:0:2000}"
|
|
278
360
|
|
|
279
361
|
write_evidence_record "$name" "database" "$passed" \
|
|
280
362
|
"$(jq -n --arg cmd "$command_str" --argjson exit_code "$exit_code" \
|
|
281
363
|
--argjson expected "$expected_exit" --arg output_preview "$output_preview" \
|
|
282
|
-
|
|
364
|
+
--argjson assertion_failures "$assertion_failures" \
|
|
365
|
+
'{command: $cmd, exit_code: $exit_code, expected_exit_code: $expected, output_preview: $output_preview, assertion_failures: $assertion_failures}')"
|
|
283
366
|
}
|
|
284
367
|
|
|
285
368
|
# ─── Webhook: Issue a callback and verify response ───────────────────────────
|
|
@@ -306,8 +389,9 @@ collect_webhook() {
|
|
|
306
389
|
local http_status="0"
|
|
307
390
|
local response_body=""
|
|
308
391
|
|
|
309
|
-
if command -v curl
|
|
310
|
-
local tmpfile
|
|
392
|
+
if command -v curl >/dev/null 2>&1; then
|
|
393
|
+
local tmpfile
|
|
394
|
+
tmpfile=$(mktemp "${TMPDIR:-/tmp}/sw-evidence-webhook.XXXXXX")
|
|
311
395
|
http_status=$(curl -s -o "$tmpfile" -w "%{http_code}" -X "$method" \
|
|
312
396
|
-H "Content-Type: application/json" -d "$body" \
|
|
313
397
|
--max-time "$timeout" "$url" 2>/dev/null || echo "0")
|
|
@@ -433,7 +517,7 @@ cmd_capture() {
|
|
|
433
517
|
*) warn "Unknown collector type: ${ctype} (skipping ${cname})" ; continue ;;
|
|
434
518
|
esac
|
|
435
519
|
|
|
436
|
-
((total
|
|
520
|
+
total=$((total + 1))
|
|
437
521
|
|
|
438
522
|
local evidence_file="${EVIDENCE_DIR}/${cname}.json"
|
|
439
523
|
local cpassed="false"
|
|
@@ -442,9 +526,9 @@ cmd_capture() {
|
|
|
442
526
|
fi
|
|
443
527
|
|
|
444
528
|
if [[ "$cpassed" == "true" ]]; then
|
|
445
|
-
((passed
|
|
529
|
+
passed=$((passed + 1))
|
|
446
530
|
else
|
|
447
|
-
((failed
|
|
531
|
+
failed=$((failed + 1))
|
|
448
532
|
fi
|
|
449
533
|
|
|
450
534
|
manifest_entries=$(echo "$manifest_entries" | jq \
|
|
@@ -506,7 +590,7 @@ cmd_verify() {
|
|
|
506
590
|
if [[ "$require_fresh" == "true" && "$age_seconds" -gt "$max_age_seconds" ]]; then
|
|
507
591
|
error "Evidence is stale: captured ${age_seconds}s ago (max: ${max_age_seconds}s)"
|
|
508
592
|
all_passed="false"
|
|
509
|
-
((failed
|
|
593
|
+
failed=$((failed + 1))
|
|
510
594
|
else
|
|
511
595
|
local age_minutes=$((age_seconds / 60))
|
|
512
596
|
info "Evidence age: ${age_minutes}m (max: ${max_age_minutes}m)"
|
|
@@ -521,7 +605,7 @@ cmd_verify() {
|
|
|
521
605
|
|
|
522
606
|
while IFS= read -r entry; do
|
|
523
607
|
[[ -z "$entry" ]] && continue
|
|
524
|
-
((checked
|
|
608
|
+
checked=$((checked + 1))
|
|
525
609
|
|
|
526
610
|
local cname ctype cpassed
|
|
527
611
|
cname=$(echo "$entry" | jq -r '.name')
|
|
@@ -531,7 +615,7 @@ cmd_verify() {
|
|
|
531
615
|
if [[ "$cpassed" != "true" ]]; then
|
|
532
616
|
error "Collector '${cname}' (${ctype}) failed"
|
|
533
617
|
all_passed="false"
|
|
534
|
-
((failed
|
|
618
|
+
failed=$((failed + 1))
|
|
535
619
|
else
|
|
536
620
|
success "Collector '${cname}' (${ctype}) passed"
|
|
537
621
|
fi
|
package/scripts/sw-feedback.sh
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
9
|
-
VERSION="
|
|
9
|
+
VERSION="3.0.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
12
|
|
|
@@ -34,16 +34,6 @@ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
|
34
34
|
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
35
35
|
}
|
|
36
36
|
fi
|
|
37
|
-
CYAN="${CYAN:-\033[38;2;0;212;255m}"
|
|
38
|
-
PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
|
|
39
|
-
BLUE="${BLUE:-\033[38;2;0;102;255m}"
|
|
40
|
-
GREEN="${GREEN:-\033[38;2;74;222;128m}"
|
|
41
|
-
YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
|
|
42
|
-
RED="${RED:-\033[38;2;248;113;113m}"
|
|
43
|
-
DIM="${DIM:-\033[2m}"
|
|
44
|
-
BOLD="${BOLD:-\033[1m}"
|
|
45
|
-
RESET="${RESET:-\033[0m}"
|
|
46
|
-
|
|
47
37
|
# ─── Storage Paths ──────────────────────────────────────────────────────────
|
|
48
38
|
INCIDENTS_FILE="${HOME}/.shipwright/incidents.jsonl"
|
|
49
39
|
ERROR_THRESHOLD=5 # Create issue if error count >= threshold
|
|
@@ -262,7 +252,7 @@ git show $regression_commit
|
|
|
262
252
|
EOF
|
|
263
253
|
)
|
|
264
254
|
|
|
265
|
-
if ! command -v gh
|
|
255
|
+
if ! command -v gh >/dev/null 2>&1; then
|
|
266
256
|
error "gh CLI not found — cannot create issue"
|
|
267
257
|
return 1
|
|
268
258
|
fi
|
|
@@ -275,7 +265,7 @@ EOF
|
|
|
275
265
|
--body "$issue_body" \
|
|
276
266
|
--label "shipwright" \
|
|
277
267
|
--label "hotfix" \
|
|
278
|
-
2>&1 | tail -1)
|
|
268
|
+
2>&1 | tail -1) || true
|
|
279
269
|
|
|
280
270
|
if [[ -n "$issue_url" ]]; then
|
|
281
271
|
success "Created issue: $issue_url"
|
package/scripts/sw-fix.sh
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
9
|
-
VERSION="
|
|
9
|
+
VERSION="3.0.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
|
|
12
12
|
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
|
@@ -25,24 +25,6 @@ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
|
|
|
25
25
|
now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
|
|
26
26
|
now_epoch() { date +%s; }
|
|
27
27
|
fi
|
|
28
|
-
if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
29
|
-
emit_event() {
|
|
30
|
-
local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
|
|
31
|
-
local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
|
|
32
|
-
while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
|
|
33
|
-
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
34
|
-
}
|
|
35
|
-
fi
|
|
36
|
-
CYAN="${CYAN:-\033[38;2;0;212;255m}"
|
|
37
|
-
PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
|
|
38
|
-
BLUE="${BLUE:-\033[38;2;0;102;255m}"
|
|
39
|
-
GREEN="${GREEN:-\033[38;2;74;222;128m}"
|
|
40
|
-
YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
|
|
41
|
-
RED="${RED:-\033[38;2;248;113;113m}"
|
|
42
|
-
DIM="${DIM:-\033[2m}"
|
|
43
|
-
BOLD="${BOLD:-\033[1m}"
|
|
44
|
-
RESET="${RESET:-\033[0m}"
|
|
45
|
-
|
|
46
28
|
format_duration() {
|
|
47
29
|
local secs="$1"
|
|
48
30
|
if [[ "$secs" -ge 3600 ]]; then
|
|
@@ -341,7 +323,7 @@ fix_start() {
|
|
|
341
323
|
|
|
342
324
|
# Spawn pipeline in subshell
|
|
343
325
|
(
|
|
344
|
-
cd "$expanded"
|
|
326
|
+
cd "$expanded" || exit 1
|
|
345
327
|
|
|
346
328
|
# Determine base branch
|
|
347
329
|
local base
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
9
|
-
VERSION="
|
|
9
|
+
VERSION="3.0.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
12
|
|
|
@@ -34,16 +34,6 @@ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
|
34
34
|
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
35
35
|
}
|
|
36
36
|
fi
|
|
37
|
-
CYAN="${CYAN:-\033[38;2;0;212;255m}"
|
|
38
|
-
PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
|
|
39
|
-
BLUE="${BLUE:-\033[38;2;0;102;255m}"
|
|
40
|
-
GREEN="${GREEN:-\033[38;2;74;222;128m}"
|
|
41
|
-
YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
|
|
42
|
-
RED="${RED:-\033[38;2;248;113;113m}"
|
|
43
|
-
DIM="${DIM:-\033[2m}"
|
|
44
|
-
BOLD="${BOLD:-\033[1m}"
|
|
45
|
-
RESET="${RESET:-\033[0m}"
|
|
46
|
-
|
|
47
37
|
# ─── Help ───────────────────────────────────────────────────────────────────
|
|
48
38
|
|
|
49
39
|
show_help() {
|
package/scripts/sw-fleet-viz.sh
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
9
|
-
VERSION="
|
|
9
|
+
VERSION="3.0.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
12
|
|
|
@@ -19,6 +19,8 @@ REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
19
19
|
[[ -f "$SCRIPT_DIR/lib/helpers.sh" ]] && source "$SCRIPT_DIR/lib/helpers.sh"
|
|
20
20
|
# Fallbacks when helpers not loaded (e.g. test env with overridden SCRIPT_DIR)
|
|
21
21
|
[[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
|
|
22
|
+
# Color fallbacks when helpers not loaded
|
|
23
|
+
: "${CYAN:=}" "${BOLD:=}" "${RESET:=}" "${DIM:=}" "${GREEN:=}" "${RED:=}" "${YELLOW:=}" "${PURPLE:=}" "${WHITE:=}" "${BLUE:=}"
|
|
22
24
|
[[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
|
|
23
25
|
[[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
|
|
24
26
|
[[ "$(type -t error 2>/dev/null)" == "function" ]] || error() { echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m $*" >&2; }
|
|
@@ -34,16 +36,6 @@ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
|
34
36
|
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
35
37
|
}
|
|
36
38
|
fi
|
|
37
|
-
CYAN="${CYAN:-\033[38;2;0;212;255m}"
|
|
38
|
-
PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
|
|
39
|
-
BLUE="${BLUE:-\033[38;2;0;102;255m}"
|
|
40
|
-
GREEN="${GREEN:-\033[38;2;74;222;128m}"
|
|
41
|
-
YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
|
|
42
|
-
RED="${RED:-\033[38;2;248;113;113m}"
|
|
43
|
-
DIM="${DIM:-\033[2m}"
|
|
44
|
-
BOLD="${BOLD:-\033[1m}"
|
|
45
|
-
RESET="${RESET:-\033[0m}"
|
|
46
|
-
|
|
47
39
|
format_duration() {
|
|
48
40
|
local secs="$1"
|
|
49
41
|
if [[ "$secs" -ge 3600 ]]; then
|
|
@@ -70,7 +62,7 @@ get_health_status() {
|
|
|
70
62
|
# degraded (yellow) = some failures but recovering
|
|
71
63
|
# failing (red) = persistent failures
|
|
72
64
|
|
|
73
|
-
if ! command -v jq
|
|
65
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
74
66
|
echo "unknown"
|
|
75
67
|
return
|
|
76
68
|
fi
|
|
@@ -98,7 +90,7 @@ color_health() {
|
|
|
98
90
|
|
|
99
91
|
# ─── Overview Subcommand ───────────────────────────────────────────────────
|
|
100
92
|
show_overview() {
|
|
101
|
-
if ! command -v jq
|
|
93
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
102
94
|
error "jq is required for fleet visualization"
|
|
103
95
|
exit 1
|
|
104
96
|
fi
|
|
@@ -144,7 +136,7 @@ show_overview() {
|
|
|
144
136
|
|
|
145
137
|
# ─── Workers Subcommand ────────────────────────────────────────────────────
|
|
146
138
|
show_workers() {
|
|
147
|
-
if ! command -v jq
|
|
139
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
148
140
|
error "jq is required for fleet visualization"
|
|
149
141
|
exit 1
|
|
150
142
|
fi
|
|
@@ -197,7 +189,7 @@ show_workers() {
|
|
|
197
189
|
|
|
198
190
|
# ─── Insights Subcommand ───────────────────────────────────────────────────
|
|
199
191
|
show_insights() {
|
|
200
|
-
if ! command -v jq
|
|
192
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
201
193
|
error "jq is required for fleet visualization"
|
|
202
194
|
exit 1
|
|
203
195
|
fi
|
|
@@ -238,7 +230,7 @@ show_insights() {
|
|
|
238
230
|
|
|
239
231
|
# ─── Queue Subcommand ──────────────────────────────────────────────────────
|
|
240
232
|
show_queue() {
|
|
241
|
-
if ! command -v jq
|
|
233
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
242
234
|
error "jq is required for fleet visualization"
|
|
243
235
|
exit 1
|
|
244
236
|
fi
|
|
@@ -281,7 +273,7 @@ show_queue() {
|
|
|
281
273
|
|
|
282
274
|
# ─── Costs Subcommand ──────────────────────────────────────────────────────
|
|
283
275
|
show_costs() {
|
|
284
|
-
if ! command -v jq
|
|
276
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
285
277
|
error "jq is required for fleet visualization"
|
|
286
278
|
exit 1
|
|
287
279
|
fi
|
|
@@ -323,7 +315,7 @@ show_costs() {
|
|
|
323
315
|
|
|
324
316
|
# ─── Export Subcommand ─────────────────────────────────────────────────────
|
|
325
317
|
show_export() {
|
|
326
|
-
if ! command -v jq
|
|
318
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
327
319
|
error "jq is required for fleet visualization"
|
|
328
320
|
exit 1
|
|
329
321
|
fi
|
package/scripts/sw-fleet.sh
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
9
|
-
VERSION="
|
|
9
|
+
VERSION="3.0.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
12
|
|
|
@@ -34,16 +34,6 @@ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
|
34
34
|
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
35
35
|
}
|
|
36
36
|
fi
|
|
37
|
-
CYAN="${CYAN:-\033[38;2;0;212;255m}"
|
|
38
|
-
PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
|
|
39
|
-
BLUE="${BLUE:-\033[38;2;0;102;255m}"
|
|
40
|
-
GREEN="${GREEN:-\033[38;2;74;222;128m}"
|
|
41
|
-
YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
|
|
42
|
-
RED="${RED:-\033[38;2;248;113;113m}"
|
|
43
|
-
DIM="${DIM:-\033[2m}"
|
|
44
|
-
BOLD="${BOLD:-\033[1m}"
|
|
45
|
-
RESET="${RESET:-\033[0m}"
|
|
46
|
-
|
|
47
37
|
epoch_to_iso() {
|
|
48
38
|
local epoch="$1"
|
|
49
39
|
date -u -r "$epoch" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || \
|
|
@@ -200,7 +190,7 @@ _fleet_repo_priority() {
|
|
|
200
190
|
local repo_path="$1"
|
|
201
191
|
local priority=50 # default neutral priority
|
|
202
192
|
|
|
203
|
-
type _gh_detect_repo
|
|
193
|
+
type _gh_detect_repo >/dev/null 2>&1 || { echo "$priority"; return 0; }
|
|
204
194
|
|
|
205
195
|
# Detect repo from the repo path (run in subshell to avoid cd side-effects)
|
|
206
196
|
local gh_priority
|
|
@@ -213,7 +203,7 @@ _fleet_repo_priority() {
|
|
|
213
203
|
local p=50
|
|
214
204
|
|
|
215
205
|
# Factor: security alerts (urgent work)
|
|
216
|
-
if type gh_security_alerts
|
|
206
|
+
if type gh_security_alerts >/dev/null 2>&1; then
|
|
217
207
|
local alerts
|
|
218
208
|
alerts=$(gh_security_alerts "$owner" "$repo" 2>/dev/null | jq 'length' 2>/dev/null || echo "0")
|
|
219
209
|
if [[ "${alerts:-0}" -gt 5 ]]; then
|
|
@@ -224,7 +214,7 @@ _fleet_repo_priority() {
|
|
|
224
214
|
fi
|
|
225
215
|
|
|
226
216
|
# Factor: contributor count (more contributors = more active = higher priority)
|
|
227
|
-
if type gh_contributors
|
|
217
|
+
if type gh_contributors >/dev/null 2>&1; then
|
|
228
218
|
local contribs
|
|
229
219
|
contribs=$(gh_contributors "$owner" "$repo" 2>/dev/null | jq 'length' 2>/dev/null || echo "0")
|
|
230
220
|
if [[ "${contribs:-0}" -gt 10 ]]; then
|
|
@@ -643,6 +633,12 @@ check_machine_health() {
|
|
|
643
633
|
emit_event "fleet.machine_offline" "machine=$name" "host=$host"
|
|
644
634
|
fi
|
|
645
635
|
done
|
|
636
|
+
|
|
637
|
+
# After health checks: trigger failover to re-queue work from offline machines
|
|
638
|
+
if [[ -f "$SCRIPT_DIR/lib/fleet-failover.sh" ]]; then
|
|
639
|
+
source "$SCRIPT_DIR/lib/fleet-failover.sh" 2>/dev/null || true
|
|
640
|
+
fleet_failover_check 2>/dev/null || true
|
|
641
|
+
fi
|
|
646
642
|
}
|
|
647
643
|
|
|
648
644
|
# ─── Cross-Machine Event Aggregation ───────────────────────────────────
|
|
@@ -749,12 +745,12 @@ fleet_start() {
|
|
|
749
745
|
echo -e "${PURPLE}${BOLD}━━━ shipwright fleet v${VERSION} — start ━━━${RESET}"
|
|
750
746
|
echo ""
|
|
751
747
|
|
|
752
|
-
if ! command -v tmux
|
|
748
|
+
if ! command -v tmux >/dev/null 2>&1; then
|
|
753
749
|
error "tmux is required for fleet mode"
|
|
754
750
|
exit 1
|
|
755
751
|
fi
|
|
756
752
|
|
|
757
|
-
if ! command -v jq
|
|
753
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
758
754
|
error "jq is required. Install: brew install jq"
|
|
759
755
|
exit 1
|
|
760
756
|
fi
|
|
@@ -1146,7 +1142,7 @@ fleet_metrics() {
|
|
|
1146
1142
|
exit 1
|
|
1147
1143
|
fi
|
|
1148
1144
|
|
|
1149
|
-
if ! command -v jq
|
|
1145
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
1150
1146
|
error "jq is required. Install: brew install jq"
|
|
1151
1147
|
exit 1
|
|
1152
1148
|
fi
|
package/scripts/sw-github-app.sh
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
9
|
-
VERSION="
|
|
9
|
+
VERSION="3.0.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
12
|
|
|
@@ -34,16 +34,6 @@ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
|
34
34
|
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
35
35
|
}
|
|
36
36
|
fi
|
|
37
|
-
CYAN="${CYAN:-\033[38;2;0;212;255m}"
|
|
38
|
-
PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
|
|
39
|
-
BLUE="${BLUE:-\033[38;2;0;102;255m}"
|
|
40
|
-
GREEN="${GREEN:-\033[38;2;74;222;128m}"
|
|
41
|
-
YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
|
|
42
|
-
RED="${RED:-\033[38;2;248;113;113m}"
|
|
43
|
-
DIM="${DIM:-\033[2m}"
|
|
44
|
-
BOLD="${BOLD:-\033[1m}"
|
|
45
|
-
RESET="${RESET:-\033[0m}"
|
|
46
|
-
|
|
47
37
|
# ─── Config File Locations ────────────────────────────────────────────────
|
|
48
38
|
CONFIG_DIR="${HOME}/.shipwright"
|
|
49
39
|
CONFIG_FILE="${CONFIG_DIR}/github-app.json"
|
|
@@ -76,16 +66,16 @@ cmd_setup() {
|
|
|
76
66
|
info "GitHub App Configuration"
|
|
77
67
|
echo ""
|
|
78
68
|
|
|
79
|
-
read -
|
|
80
|
-
read -
|
|
69
|
+
read -rp "App ID: " app_id
|
|
70
|
+
read -rp "Private key file path: " key_path
|
|
81
71
|
|
|
82
72
|
if [[ ! -f "$key_path" ]]; then
|
|
83
73
|
error "Private key file not found: $key_path"
|
|
84
74
|
return 1
|
|
85
75
|
fi
|
|
86
76
|
|
|
87
|
-
read -
|
|
88
|
-
read -
|
|
77
|
+
read -rp "Installation ID: " installation_id
|
|
78
|
+
read -rp "Webhook secret (optional, press Enter to skip): " webhook_secret
|
|
89
79
|
|
|
90
80
|
# Create config atomically
|
|
91
81
|
local tmp_config
|
|
@@ -171,7 +161,7 @@ _get_installation_token() {
|
|
|
171
161
|
fi
|
|
172
162
|
|
|
173
163
|
local response
|
|
174
|
-
response=$(curl -s -H "Authorization: Bearer $jwt" \
|
|
164
|
+
response=$(curl -s --connect-timeout 10 --max-time 30 -H "Authorization: Bearer $jwt" \
|
|
175
165
|
-H "Accept: application/vnd.github+json" \
|
|
176
166
|
"https://api.github.com/app/installations/${installation_id}/access_tokens" \
|
|
177
167
|
-d '{}' -X POST 2>/dev/null) || true
|