@elmundi/ship-cli 0.12.2 → 0.14.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.
@@ -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 lanes and write
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.
@@ -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 lanes.
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 lanes execute.
734
- // Single-pattern lanes ignore it (it's a no-op for them); we emit a
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elmundi/ship-cli",
3
- "version": "0.12.2",
3
+ "version": "0.14.0",
4
4
  "type": "module",
5
5
  "description": "Ship CLI: bootstrap a repo, sync the catalog, run process routines, report outcomes.",
6
6
  "license": "Apache-2.0",