@elmundi/ship-cli 0.12.2 → 0.14.1
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 +70 -653
- package/bin/shipctl.mjs +3 -1
- package/lib/agents/cursor.mjs +143 -0
- package/lib/agents/index.mjs +51 -0
- package/lib/commands/help.mjs +7 -6
- package/lib/commands/lanes.mjs +74 -35
- package/lib/commands/run.mjs +469 -784
- package/lib/commands/sync.mjs +1 -1
- package/lib/config/schema.mjs +3 -3
- package/lib/vendor/run-agent.workflow.yml +254 -0
- package/package.json +1 -1
package/lib/commands/sync.mjs
CHANGED
|
@@ -51,7 +51,7 @@ FLAGS
|
|
|
51
51
|
version. Use when intentionally bumping a pin.
|
|
52
52
|
--dry-run Print the resolution plan; do not write or fetch.
|
|
53
53
|
--lock After sync, materialise every pattern referenced
|
|
54
|
-
by the declared
|
|
54
|
+
by the declared routines and write
|
|
55
55
|
.ship/shipctl.lock.json (used by
|
|
56
56
|
'shipctl run --offline').
|
|
57
57
|
--json Emit a structured JSON summary on stdout.
|
package/lib/config/schema.mjs
CHANGED
|
@@ -33,7 +33,7 @@ export const LANE_EVENT_TYPES = Object.freeze([
|
|
|
33
33
|
export const LANE_IDEMPOTENCY_STORES = Object.freeze(["file", "backend"]);
|
|
34
34
|
export const LANE_IDEMPOTENCY_RESET_ON = Object.freeze(["version-change", "manual"]);
|
|
35
35
|
|
|
36
|
-
/* RFC-0008 C3.2 — fan-out strategy for multi-pattern
|
|
36
|
+
/* RFC-0008 C3.2 — fan-out strategy for multi-pattern routines.
|
|
37
37
|
*
|
|
38
38
|
* matrix — GitHub Actions matrix: one runner per pattern, parallel.
|
|
39
39
|
* sequential — Single runner, `shipctl run` iterates patterns in order.
|
|
@@ -730,8 +730,8 @@ function validateLane(laneId, lane, errors, warnings) {
|
|
|
730
730
|
) {
|
|
731
731
|
errors.push(`${prefix}.pattern_version: must be a non-empty semver string when set`);
|
|
732
732
|
}
|
|
733
|
-
// RFC-0008 C3.2 — `fanout` picks how multi-pattern
|
|
734
|
-
// Single-pattern
|
|
733
|
+
// RFC-0008 C3.2 — `fanout` picks how multi-pattern routines execute.
|
|
734
|
+
// Single-pattern routines ignore it (it's a no-op for them); we emit a
|
|
735
735
|
// warning rather than an error so schedule templates that set it
|
|
736
736
|
// blindly remain portable across single/multi-pattern use.
|
|
737
737
|
if (lane.fanout !== undefined) {
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# ship-cli: run-agent v1 — generated by `shipctl lanes install`.
|
|
2
|
+
# Regenerate via: shipctl lanes install
|
|
3
|
+
# Template ships with @elmundi/ship-cli; hand edits outside this banner may be overwritten.
|
|
4
|
+
name: Ship · run-agent (reusable)
|
|
5
|
+
# RFC-0007 Phase 3 + RFC-0008 C3.2 — reusable workflow for lane wrappers.
|
|
6
|
+
# Caller repos use: uses: ./.github/workflows/run-agent.yml (same repository).
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
workflow_call:
|
|
10
|
+
inputs:
|
|
11
|
+
lane:
|
|
12
|
+
description: "Lane id as declared in .ship/config.yml"
|
|
13
|
+
type: string
|
|
14
|
+
required: true
|
|
15
|
+
trigger:
|
|
16
|
+
description: "Override trigger (once|event|schedule|manual). Defaults to github.event_name."
|
|
17
|
+
type: string
|
|
18
|
+
required: false
|
|
19
|
+
default: ""
|
|
20
|
+
shipctl_version:
|
|
21
|
+
description: "npm dist-tag or semver for @elmundi/ship-cli. Default: latest."
|
|
22
|
+
type: string
|
|
23
|
+
required: false
|
|
24
|
+
default: "latest"
|
|
25
|
+
node_version:
|
|
26
|
+
description: "Node.js major version for the runner"
|
|
27
|
+
type: string
|
|
28
|
+
required: false
|
|
29
|
+
default: "20"
|
|
30
|
+
dry_run:
|
|
31
|
+
description: "Resolve the lane but neither call the callback nor write the idempotency marker."
|
|
32
|
+
type: boolean
|
|
33
|
+
required: false
|
|
34
|
+
default: false
|
|
35
|
+
offline:
|
|
36
|
+
description: "Do not talk to the methodology API; read patterns from the local .ship/cache."
|
|
37
|
+
type: boolean
|
|
38
|
+
required: false
|
|
39
|
+
default: false
|
|
40
|
+
ship_run_id:
|
|
41
|
+
description: "Pipeline-run id the callback should report against (set by Ship dispatch)."
|
|
42
|
+
type: string
|
|
43
|
+
required: false
|
|
44
|
+
default: ""
|
|
45
|
+
ship_callback_url:
|
|
46
|
+
description: "Ship callback URL (set by Ship dispatch)."
|
|
47
|
+
type: string
|
|
48
|
+
required: false
|
|
49
|
+
default: ""
|
|
50
|
+
upload_prompt:
|
|
51
|
+
description: "Upload the rendered prompt as a workflow artifact named `ship-prompt`."
|
|
52
|
+
type: boolean
|
|
53
|
+
required: false
|
|
54
|
+
default: true
|
|
55
|
+
secrets:
|
|
56
|
+
SHIP_API_TOKEN:
|
|
57
|
+
description: "Methodology API token (optional; required for private patterns / callbacks)."
|
|
58
|
+
required: false
|
|
59
|
+
SHIP_API_BASE:
|
|
60
|
+
description: "Ship API origin for shipctl (e.g. https://api.example.com). Wizard seeds this from SHIP_PUBLIC_URL."
|
|
61
|
+
required: false
|
|
62
|
+
SHIP_RUN_TOKEN:
|
|
63
|
+
description: "Short-lived bearer token Ship issued when dispatching this run."
|
|
64
|
+
required: false
|
|
65
|
+
|
|
66
|
+
permissions:
|
|
67
|
+
contents: read
|
|
68
|
+
|
|
69
|
+
jobs:
|
|
70
|
+
plan:
|
|
71
|
+
name: "ship plan --lane ${{ inputs.lane }}"
|
|
72
|
+
runs-on: ubuntu-latest
|
|
73
|
+
timeout-minutes: 5
|
|
74
|
+
outputs:
|
|
75
|
+
matrix: ${{ steps.plan.outputs.matrix }}
|
|
76
|
+
patterns: ${{ steps.plan.outputs.patterns }}
|
|
77
|
+
fanout: ${{ steps.plan.outputs.fanout }}
|
|
78
|
+
steps:
|
|
79
|
+
- name: Checkout caller repo
|
|
80
|
+
uses: actions/checkout@v4
|
|
81
|
+
|
|
82
|
+
- name: Setup Node.js
|
|
83
|
+
uses: actions/setup-node@v4
|
|
84
|
+
with:
|
|
85
|
+
node-version: ${{ inputs.node_version }}
|
|
86
|
+
|
|
87
|
+
- name: Install shipctl
|
|
88
|
+
run: npm install -g @elmundi/ship-cli@${{ inputs.shipctl_version }}
|
|
89
|
+
|
|
90
|
+
- name: Resolve patterns + fanout
|
|
91
|
+
id: plan
|
|
92
|
+
env:
|
|
93
|
+
LANE: ${{ inputs.lane }}
|
|
94
|
+
run: |
|
|
95
|
+
set -euo pipefail
|
|
96
|
+
JSON=$(shipctl lanes list --json)
|
|
97
|
+
ROW=$(echo "$JSON" | jq -c --arg lane "$LANE" '.lanes[] | select(.lane == $lane)')
|
|
98
|
+
if [ -z "$ROW" ] || [ "$ROW" = "null" ]; then
|
|
99
|
+
echo "::error::lane '$LANE' not declared in .ship/config.yml"
|
|
100
|
+
exit 1
|
|
101
|
+
fi
|
|
102
|
+
PATTERNS=$(echo "$ROW" | jq -c '.patterns // []')
|
|
103
|
+
FANOUT=$(echo "$ROW" | jq -r '.fanout // "matrix"')
|
|
104
|
+
N=$(echo "$PATTERNS" | jq 'length')
|
|
105
|
+
if [ "$N" -lt 1 ]; then
|
|
106
|
+
echo "::error::lane '$LANE' declares no patterns"
|
|
107
|
+
exit 1
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
if [ "$FANOUT" = "matrix" ] && [ "$N" -gt 1 ]; then
|
|
111
|
+
MATRIX=$(echo "$PATTERNS" | jq -c '[.[] | {pattern: ., fanout: "matrix"}]')
|
|
112
|
+
elif [ "$N" -gt 1 ]; then
|
|
113
|
+
MATRIX=$(jq -cn --arg fanout "$FANOUT" '[{pattern: "", fanout: $fanout}]')
|
|
114
|
+
else
|
|
115
|
+
MATRIX='[{"pattern": "", "fanout": "matrix"}]'
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
echo "patterns=$PATTERNS" >> "$GITHUB_OUTPUT"
|
|
119
|
+
echo "fanout=$FANOUT" >> "$GITHUB_OUTPUT"
|
|
120
|
+
echo "matrix=$MATRIX" >> "$GITHUB_OUTPUT"
|
|
121
|
+
{
|
|
122
|
+
echo "### Plan"
|
|
123
|
+
echo "- lane: \`$LANE\`"
|
|
124
|
+
echo "- patterns: $(echo "$PATTERNS" | jq -r '. | join(", ")')"
|
|
125
|
+
echo "- fanout: $FANOUT"
|
|
126
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
|
127
|
+
|
|
128
|
+
run:
|
|
129
|
+
name: "ship run --lane ${{ inputs.lane }} (pattern=${{ matrix.entry.pattern }} fanout=${{ matrix.entry.fanout }})"
|
|
130
|
+
needs: plan
|
|
131
|
+
runs-on: ubuntu-latest
|
|
132
|
+
timeout-minutes: 20
|
|
133
|
+
strategy:
|
|
134
|
+
fail-fast: false
|
|
135
|
+
matrix:
|
|
136
|
+
entry: ${{ fromJSON(needs.plan.outputs.matrix) }}
|
|
137
|
+
steps:
|
|
138
|
+
- name: Checkout caller repo
|
|
139
|
+
uses: actions/checkout@v4
|
|
140
|
+
|
|
141
|
+
- name: Setup Node.js
|
|
142
|
+
uses: actions/setup-node@v4
|
|
143
|
+
with:
|
|
144
|
+
node-version: ${{ inputs.node_version }}
|
|
145
|
+
|
|
146
|
+
- name: Install shipctl
|
|
147
|
+
run: npm install -g @elmundi/ship-cli@${{ inputs.shipctl_version }}
|
|
148
|
+
|
|
149
|
+
- name: Resolve trigger
|
|
150
|
+
id: trigger
|
|
151
|
+
env:
|
|
152
|
+
OVERRIDE: ${{ inputs.trigger }}
|
|
153
|
+
EVENT: ${{ github.event_name }}
|
|
154
|
+
run: |
|
|
155
|
+
set -euo pipefail
|
|
156
|
+
if [ -n "${OVERRIDE:-}" ]; then
|
|
157
|
+
echo "value=$OVERRIDE" >> "$GITHUB_OUTPUT"
|
|
158
|
+
else
|
|
159
|
+
case "$EVENT" in
|
|
160
|
+
workflow_dispatch) echo "value=manual" >> "$GITHUB_OUTPUT" ;;
|
|
161
|
+
schedule) echo "value=schedule" >> "$GITHUB_OUTPUT" ;;
|
|
162
|
+
*) echo "value=event" >> "$GITHUB_OUTPUT" ;;
|
|
163
|
+
esac
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
- name: shipctl run
|
|
167
|
+
id: run
|
|
168
|
+
env:
|
|
169
|
+
SHIP_API_TOKEN: ${{ secrets.SHIP_API_TOKEN }}
|
|
170
|
+
SHIP_API_BASE: ${{ secrets.SHIP_API_BASE }}
|
|
171
|
+
SHIP_RUN_TOKEN: ${{ secrets.SHIP_RUN_TOKEN }}
|
|
172
|
+
SHIP_RUN_ID: ${{ inputs.ship_run_id }}
|
|
173
|
+
MATRIX_PATTERN: ${{ matrix.entry.pattern }}
|
|
174
|
+
MATRIX_FANOUT: ${{ matrix.entry.fanout }}
|
|
175
|
+
run: |
|
|
176
|
+
set -euo pipefail
|
|
177
|
+
mkdir -p .ship/run-output
|
|
178
|
+
ARGS=(run --lane "${{ inputs.lane }}" --trigger "${{ steps.trigger.outputs.value }}")
|
|
179
|
+
[ "${{ inputs.dry_run }}" = "true" ] && ARGS+=(--dry-run)
|
|
180
|
+
[ "${{ inputs.offline }}" = "true" ] && ARGS+=(--offline)
|
|
181
|
+
[ -n "${MATRIX_PATTERN:-}" ] && ARGS+=(--pattern "$MATRIX_PATTERN")
|
|
182
|
+
[ -n "${MATRIX_FANOUT:-}" ] && ARGS+=(--fanout "$MATRIX_FANOUT")
|
|
183
|
+
[ -n "${SHIP_RUN_ID:-}" ] && ARGS+=(--ship-run-id "$SHIP_RUN_ID")
|
|
184
|
+
|
|
185
|
+
set +e
|
|
186
|
+
shipctl "${ARGS[@]}" \
|
|
187
|
+
1> .ship/run-output/prompt.md \
|
|
188
|
+
2> .ship/run-output/shipctl.log
|
|
189
|
+
STATUS=$?
|
|
190
|
+
set -e
|
|
191
|
+
|
|
192
|
+
cat .ship/run-output/shipctl.log >&2 || true
|
|
193
|
+
echo "status=$STATUS" >> "$GITHUB_OUTPUT"
|
|
194
|
+
if [ "$STATUS" -ne 0 ]; then
|
|
195
|
+
echo "::error::shipctl run exited with $STATUS"
|
|
196
|
+
exit "$STATUS"
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
- name: Upload prompt artifact
|
|
200
|
+
if: ${{ inputs.upload_prompt && (success() || failure()) }}
|
|
201
|
+
uses: actions/upload-artifact@v4
|
|
202
|
+
with:
|
|
203
|
+
name: ship-prompt-${{ inputs.lane }}${{ matrix.entry.pattern != '' && format('-{0}', matrix.entry.pattern) || '' }}
|
|
204
|
+
path: .ship/run-output/
|
|
205
|
+
if-no-files-found: warn
|
|
206
|
+
retention-days: 14
|
|
207
|
+
|
|
208
|
+
aggregate:
|
|
209
|
+
name: "ship aggregate --lane ${{ inputs.lane }}"
|
|
210
|
+
if: ${{ always() && inputs.ship_callback_url != '' }}
|
|
211
|
+
needs: [plan, run]
|
|
212
|
+
runs-on: ubuntu-latest
|
|
213
|
+
timeout-minutes: 5
|
|
214
|
+
steps:
|
|
215
|
+
- name: Checkout caller repo
|
|
216
|
+
uses: actions/checkout@v4
|
|
217
|
+
|
|
218
|
+
- name: Setup Node.js
|
|
219
|
+
uses: actions/setup-node@v4
|
|
220
|
+
with:
|
|
221
|
+
node-version: ${{ inputs.node_version }}
|
|
222
|
+
|
|
223
|
+
- name: Install shipctl
|
|
224
|
+
run: npm install -g @elmundi/ship-cli@${{ inputs.shipctl_version }}
|
|
225
|
+
|
|
226
|
+
- name: Report rollup to Ship
|
|
227
|
+
env:
|
|
228
|
+
SHIP_RUN_ID: ${{ inputs.ship_run_id }}
|
|
229
|
+
SHIP_CALLBACK_URL: ${{ inputs.ship_callback_url }}
|
|
230
|
+
SHIP_RUN_TOKEN: ${{ secrets.SHIP_RUN_TOKEN }}
|
|
231
|
+
GH_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
232
|
+
RUN_RESULT: ${{ needs.run.result }}
|
|
233
|
+
PLAN_PATTERNS: ${{ needs.plan.outputs.patterns }}
|
|
234
|
+
PLAN_FANOUT: ${{ needs.plan.outputs.fanout }}
|
|
235
|
+
run: |
|
|
236
|
+
set -euo pipefail
|
|
237
|
+
case "$RUN_RESULT" in
|
|
238
|
+
success)
|
|
239
|
+
CB=ok
|
|
240
|
+
SUMMARY="Ship lane ${{ inputs.lane }} completed ($PLAN_FANOUT over $PLAN_PATTERNS)."
|
|
241
|
+
;;
|
|
242
|
+
failure|cancelled|skipped|*)
|
|
243
|
+
CB=fail
|
|
244
|
+
SUMMARY="Ship lane ${{ inputs.lane }} $RUN_RESULT ($PLAN_FANOUT over $PLAN_PATTERNS)."
|
|
245
|
+
;;
|
|
246
|
+
esac
|
|
247
|
+
shipctl callback --status "$CB" \
|
|
248
|
+
--summary "$SUMMARY" \
|
|
249
|
+
--metric "lane_id=${{ inputs.lane }}" \
|
|
250
|
+
--metric "fanout=$PLAN_FANOUT" \
|
|
251
|
+
--metric "patterns=$(echo "$PLAN_PATTERNS" | jq -r '. | join(",")')" \
|
|
252
|
+
--metric "gh_workflow_run_id=${{ github.run_id }}" \
|
|
253
|
+
--metric "gh_html_url=$GH_RUN_URL" \
|
|
254
|
+
--metric "gh_event=${{ github.event_name }}" || true
|