Skip to content

Commit a58c2f2

Browse files
authored
fix(releasekit): ensure command output is visible in CI on failure (#4666)
1 parent 7cb7185 commit a58c2f2

File tree

4 files changed

+121
-30
lines changed

4 files changed

+121
-30
lines changed

.github/workflows/releasekit-uv.yml

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ jobs:
199199
!contains(github.event.head_commit.message, 'releasekit--release')) ||
200200
(github.event_name == 'workflow_dispatch' && inputs.action == 'prepare')
201201
runs-on: ubuntu-latest
202+
timeout-minutes: 10
202203
outputs:
203204
has_bumps: ${{ steps.prepare.outputs.has_bumps }}
204205
pr_url: ${{ steps.prepare.outputs.pr_url }}
@@ -243,12 +244,17 @@ jobs:
243244
cmd+=(--prerelease "${{ inputs.prerelease }}")
244245
fi
245246
246-
# Run prepare — fail the job immediately on error.
247-
OUTPUT=$("${cmd[@]}" 2>&1)
247+
# Run prepare — capture output even on failure so CI logs
248+
# show diagnostics (set -e would exit before echo).
249+
OUTPUT=$("${cmd[@]}" 2>&1) || EXIT_CODE=$?
248250
echo "$OUTPUT"
251+
if [ "${EXIT_CODE:-0}" -ne 0 ]; then
252+
echo "::error::releasekit prepare failed with exit code $EXIT_CODE"
253+
exit $EXIT_CODE
254+
fi
249255
250-
# Parse output for PR URL
251-
PR_URL=$(echo "$OUTPUT" | grep -oP 'Release PR: \K.*' || echo "")
256+
# Parse output for PR URL (sed is portable; grep -oP needs GNU grep).
257+
PR_URL=$(echo "$OUTPUT" | sed -n 's/.*Release PR: //p' | tail -1)
252258
if [ -n "$PR_URL" ]; then
253259
echo "has_bumps=true" >> "$GITHUB_OUTPUT"
254260
echo "pr_url=$PR_URL" >> "$GITHUB_OUTPUT"
@@ -272,6 +278,7 @@ jobs:
272278
contains(github.event.pull_request.labels.*.name, 'autorelease: pending')) ||
273279
(github.event_name == 'workflow_dispatch' && inputs.action == 'release')
274280
runs-on: ubuntu-latest
281+
timeout-minutes: 10
275282
outputs:
276283
release_url: ${{ steps.release.outputs.release_url }}
277284
steps:
@@ -319,12 +326,18 @@ jobs:
319326
DRY_RUN_FLAG="--dry-run"
320327
fi
321328
322-
# Run release — fail the job immediately on error.
323-
OUTPUT=$(uv run --directory ${{ env.RELEASEKIT_DIR }} releasekit --workspace py release $DRY_RUN_FLAG 2>&1)
329+
# Run release — capture output even on failure so CI logs
330+
# show diagnostics (set -e would exit before echo).
331+
OUTPUT=$(uv run --directory ${{ env.RELEASEKIT_DIR }} releasekit --workspace py release $DRY_RUN_FLAG 2>&1) || EXIT_CODE=$?
324332
echo "$OUTPUT"
333+
if [ "${EXIT_CODE:-0}" -ne 0 ]; then
334+
echo "::error::releasekit release failed with exit code $EXIT_CODE"
335+
exit $EXIT_CODE
336+
fi
325337
326338
# Parse release URL
327-
RELEASE_URL=$(echo "$OUTPUT" | grep -oP 'release_url=\K.*' || echo "")
339+
# Parse release URL (sed is portable; grep -oP needs GNU grep).
340+
RELEASE_URL=$(echo "$OUTPUT" | sed -n 's/.*release_url=//p' | tail -1)
328341
echo "release_url=$RELEASE_URL" >> "$GITHUB_OUTPUT"
329342
env:
330343
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -338,8 +351,11 @@ jobs:
338351
publish:
339352
name: Publish to ${{ inputs.target || 'pypi' }}
340353
needs: release
354+
# On PR-merge events, inputs.skip_publish is undefined (empty string),
355+
# which is != 'true', so publish runs. This is intentional.
341356
if: inputs.skip_publish != 'true'
342357
runs-on: ubuntu-latest
358+
timeout-minutes: 30
343359
environment: ${{ inputs.target || 'pypi' }}
344360
permissions:
345361
id-token: write # Trusted publishing (OIDC)
@@ -362,7 +378,7 @@ jobs:
362378

363379
- name: Install workspace
364380
working-directory: ${{ env.WORKSPACE_DIR }}
365-
run: uv sync
381+
run: uv sync --no-dev
366382

367383
- name: Install releasekit
368384
working-directory: ${{ env.RELEASEKIT_DIR }}
@@ -381,6 +397,8 @@ jobs:
381397
382398
- name: Run releasekit publish
383399
run: |
400+
set -euo pipefail
401+
384402
cmd=(uv run --directory ${{ env.RELEASEKIT_DIR }} releasekit --workspace py publish --force)
385403
386404
if [ "${{ env.DRY_RUN }}" = "true" ]; then
@@ -408,9 +426,15 @@ jobs:
408426
cmd+=(--max-retries "$MAX_RETRIES")
409427
fi
410428
429+
# Capture output even on failure so CI logs show diagnostics.
411430
echo "::group::Running: ${cmd[*]}"
412-
"${cmd[@]}"
431+
OUTPUT=$("${cmd[@]}" 2>&1) || EXIT_CODE=$?
432+
echo "$OUTPUT"
413433
echo "::endgroup::"
434+
if [ "${EXIT_CODE:-0}" -ne 0 ]; then
435+
echo "::error::releasekit publish failed with exit code $EXIT_CODE"
436+
exit $EXIT_CODE
437+
fi
414438
env:
415439
# For trusted publishing (OIDC), no token needed.
416440
# For API token auth, set PYPI_TOKEN / TESTPYPI_TOKEN in repo secrets.
@@ -434,10 +458,14 @@ jobs:
434458
needs: publish
435459
if: success()
436460
runs-on: ubuntu-latest
461+
timeout-minutes: 5
437462
steps:
438463
- name: Dispatch release event
439464
uses: peter-evans/repository-dispatch@v3
440465
with:
466+
# NOTE: GITHUB_TOKEN is scoped to this repo only. For cross-repo
467+
# dispatch (e.g. genkit-community-plugins), replace with a PAT
468+
# or GitHub App token stored in secrets.
441469
token: ${{ secrets.GITHUB_TOKEN }}
442470
event-type: genkit-python-release
443471
client-payload: '{"release_url": "${{ needs.release.outputs.release_url }}"}'

py/tools/releasekit/github/workflows/releasekit-pnpm.yml

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ jobs:
198198
!contains(github.event.head_commit.message, 'releasekit--release')) ||
199199
(github.event_name == 'workflow_dispatch' && inputs.action == 'prepare')
200200
runs-on: ubuntu-latest
201+
timeout-minutes: 10
201202
outputs:
202203
has_bumps: ${{ steps.prepare.outputs.has_bumps }}
203204
pr_url: ${{ steps.prepare.outputs.pr_url }}
@@ -252,11 +253,17 @@ jobs:
252253
cmd+=(--prerelease "${{ inputs.prerelease }}")
253254
fi
254255
255-
# Run prepare — fail the job immediately on error.
256-
OUTPUT=$("${cmd[@]}" 2>&1)
256+
# Run prepare — capture output even on failure so CI logs
257+
# show diagnostics (set -e would exit before echo).
258+
OUTPUT=$("${cmd[@]}" 2>&1) || EXIT_CODE=$?
257259
echo "$OUTPUT"
260+
if [ "${EXIT_CODE:-0}" -ne 0 ]; then
261+
echo "::error::releasekit prepare failed with exit code $EXIT_CODE"
262+
exit $EXIT_CODE
263+
fi
258264
259-
PR_URL=$(echo "$OUTPUT" | grep -oP 'Release PR: \K.*' || echo "")
265+
# Parse output for PR URL (sed is portable; grep -oP needs GNU grep).
266+
PR_URL=$(echo "$OUTPUT" | sed -n 's/.*Release PR: //p' | tail -1)
260267
if [ -n "$PR_URL" ]; then
261268
echo "has_bumps=true" >> "$GITHUB_OUTPUT"
262269
echo "pr_url=$PR_URL" >> "$GITHUB_OUTPUT"
@@ -274,6 +281,7 @@ jobs:
274281
!contains(github.event.head_commit.message, 'releasekit--release')) ||
275282
(github.event_name == 'workflow_dispatch' && inputs.action == 'prepare')
276283
runs-on: ubuntu-latest
284+
timeout-minutes: 10
277285
outputs:
278286
has_bumps: ${{ steps.prepare.outputs.has_bumps }}
279287
pr_url: ${{ steps.prepare.outputs.pr_url }}
@@ -328,11 +336,17 @@ jobs:
328336
cmd+=(--prerelease "${{ inputs.prerelease }}")
329337
fi
330338
331-
# Run prepare — fail the job immediately on error.
332-
OUTPUT=$("${cmd[@]}" 2>&1)
339+
# Run prepare — capture output even on failure so CI logs
340+
# show diagnostics (set -e would exit before echo).
341+
OUTPUT=$("${cmd[@]}" 2>&1) || EXIT_CODE=$?
333342
echo "$OUTPUT"
343+
if [ "${EXIT_CODE:-0}" -ne 0 ]; then
344+
echo "::error::releasekit prepare failed with exit code $EXIT_CODE"
345+
exit $EXIT_CODE
346+
fi
334347
335-
PR_URL=$(echo "$OUTPUT" | grep -oP 'Release PR: \K.*' || echo "")
348+
# Parse output for PR URL (sed is portable; grep -oP needs GNU grep).
349+
PR_URL=$(echo "$OUTPUT" | sed -n 's/.*Release PR: //p' | tail -1)
336350
if [ -n "$PR_URL" ]; then
337351
echo "has_bumps=true" >> "$GITHUB_OUTPUT"
338352
echo "pr_url=$PR_URL" >> "$GITHUB_OUTPUT"
@@ -356,6 +370,7 @@ jobs:
356370
contains(github.event.pull_request.labels.*.name, 'autorelease: pending')) ||
357371
(github.event_name == 'workflow_dispatch' && inputs.action == 'release')
358372
runs-on: ubuntu-latest
373+
timeout-minutes: 10
359374
outputs:
360375
release_url: ${{ steps.release.outputs.release_url }}
361376
steps:
@@ -403,11 +418,17 @@ jobs:
403418
DRY_RUN_FLAG="--dry-run"
404419
fi
405420
406-
# Run release — fail the job immediately on error.
407-
OUTPUT=$(uv run --directory ${{ env.RELEASEKIT_DIR }} releasekit --workspace js release $DRY_RUN_FLAG 2>&1)
421+
# Run release — capture output even on failure so CI logs
422+
# show diagnostics (set -e would exit before echo).
423+
OUTPUT=$(uv run --directory ${{ env.RELEASEKIT_DIR }} releasekit --workspace js release $DRY_RUN_FLAG 2>&1) || EXIT_CODE=$?
408424
echo "$OUTPUT"
425+
if [ "${EXIT_CODE:-0}" -ne 0 ]; then
426+
echo "::error::releasekit release failed with exit code $EXIT_CODE"
427+
exit $EXIT_CODE
428+
fi
409429
410-
RELEASE_URL=$(echo "$OUTPUT" | grep -oP 'release_url=\K.*' || echo "")
430+
# Parse release URL (sed is portable; grep -oP needs GNU grep).
431+
RELEASE_URL=$(echo "$OUTPUT" | sed -n 's/.*release_url=//p' | tail -1)
411432
echo "release_url=$RELEASE_URL" >> "$GITHUB_OUTPUT"
412433
env:
413434
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -421,8 +442,11 @@ jobs:
421442
publish-js:
422443
name: "Publish to npm"
423444
needs: release-js
445+
# On PR-merge events, inputs.skip_publish is undefined (empty string),
446+
# which is != 'true', so publish runs. This is intentional.
424447
if: inputs.skip_publish != 'true'
425448
runs-on: ubuntu-latest
449+
timeout-minutes: 30
426450
steps:
427451
- uses: actions/checkout@v6
428452
with:
@@ -470,6 +494,8 @@ jobs:
470494
471495
- name: Run releasekit publish (JS)
472496
run: |
497+
set -euo pipefail
498+
473499
cmd=(uv run --directory ${{ env.RELEASEKIT_DIR }} releasekit --workspace js publish --force)
474500
475501
if [ "${{ env.DRY_RUN }}" = "true" ]; then
@@ -490,9 +516,15 @@ jobs:
490516
cmd+=(--max-retries "$MAX_RETRIES")
491517
fi
492518
519+
# Capture output even on failure so CI logs show diagnostics.
493520
echo "::group::Running: ${cmd[*]}"
494-
"${cmd[@]}"
521+
OUTPUT=$("${cmd[@]}" 2>&1) || EXIT_CODE=$?
522+
echo "$OUTPUT"
495523
echo "::endgroup::"
524+
if [ "${EXIT_CODE:-0}" -ne 0 ]; then
525+
echo "::error::releasekit publish failed with exit code $EXIT_CODE"
526+
exit $EXIT_CODE
527+
fi
496528
env:
497529
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
498530

@@ -512,10 +544,14 @@ jobs:
512544
needs: publish-js
513545
if: success() && needs.publish-js.result == 'success'
514546
runs-on: ubuntu-latest
547+
timeout-minutes: 5
515548
steps:
516549
- name: Dispatch release event
517550
uses: peter-evans/repository-dispatch@v3
518551
with:
552+
# NOTE: GITHUB_TOKEN is scoped to this repo only. For cross-repo
553+
# dispatch (e.g. genkit-community-plugins), replace with a PAT
554+
# or GitHub App token stored in secrets.
519555
token: ${{ secrets.GITHUB_TOKEN }}
520556
event-type: genkit-js-release
521557
client-payload: '{"release_url": "${{ needs.release-js.outputs.release_url }}"}'

py/tools/releasekit/github/workflows/releasekit-uv.yml

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ jobs:
201201
!contains(github.event.head_commit.message, 'releasekit--release')) ||
202202
(github.event_name == 'workflow_dispatch' && inputs.action == 'prepare')
203203
runs-on: ubuntu-latest
204+
timeout-minutes: 10
204205
outputs:
205206
has_bumps: ${{ steps.prepare.outputs.has_bumps }}
206207
pr_url: ${{ steps.prepare.outputs.pr_url }}
@@ -245,12 +246,17 @@ jobs:
245246
cmd+=(--prerelease "${{ inputs.prerelease }}")
246247
fi
247248
248-
# Run prepare — fail the job immediately on error.
249-
OUTPUT=$("${cmd[@]}" 2>&1)
249+
# Run prepare — capture output even on failure so CI logs
250+
# show diagnostics (set -e would exit before echo).
251+
OUTPUT=$("${cmd[@]}" 2>&1) || EXIT_CODE=$?
250252
echo "$OUTPUT"
253+
if [ "${EXIT_CODE:-0}" -ne 0 ]; then
254+
echo "::error::releasekit prepare failed with exit code $EXIT_CODE"
255+
exit $EXIT_CODE
256+
fi
251257
252-
# Parse output for PR URL
253-
PR_URL=$(echo "$OUTPUT" | grep -oP 'Release PR: \K.*' || echo "")
258+
# Parse output for PR URL (sed is portable; grep -oP needs GNU grep).
259+
PR_URL=$(echo "$OUTPUT" | sed -n 's/.*Release PR: //p' | tail -1)
254260
if [ -n "$PR_URL" ]; then
255261
echo "has_bumps=true" >> "$GITHUB_OUTPUT"
256262
echo "pr_url=$PR_URL" >> "$GITHUB_OUTPUT"
@@ -274,6 +280,7 @@ jobs:
274280
contains(github.event.pull_request.labels.*.name, 'autorelease: pending')) ||
275281
(github.event_name == 'workflow_dispatch' && inputs.action == 'release')
276282
runs-on: ubuntu-latest
283+
timeout-minutes: 10
277284
outputs:
278285
release_url: ${{ steps.release.outputs.release_url }}
279286
steps:
@@ -321,12 +328,17 @@ jobs:
321328
DRY_RUN_FLAG="--dry-run"
322329
fi
323330
324-
# Run release — fail the job immediately on error.
325-
OUTPUT=$(uv run --directory ${{ env.RELEASEKIT_DIR }} releasekit --workspace py release $DRY_RUN_FLAG 2>&1)
331+
# Run release — capture output even on failure so CI logs
332+
# show diagnostics (set -e would exit before echo).
333+
OUTPUT=$(uv run --directory ${{ env.RELEASEKIT_DIR }} releasekit --workspace py release $DRY_RUN_FLAG 2>&1) || EXIT_CODE=$?
326334
echo "$OUTPUT"
335+
if [ "${EXIT_CODE:-0}" -ne 0 ]; then
336+
echo "::error::releasekit release failed with exit code $EXIT_CODE"
337+
exit $EXIT_CODE
338+
fi
327339
328-
# Parse release URL
329-
RELEASE_URL=$(echo "$OUTPUT" | grep -oP 'release_url=\K.*' || echo "")
340+
# Parse release URL (sed is portable; grep -oP needs GNU grep).
341+
RELEASE_URL=$(echo "$OUTPUT" | sed -n 's/.*release_url=//p' | tail -1)
330342
echo "release_url=$RELEASE_URL" >> "$GITHUB_OUTPUT"
331343
env:
332344
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -340,8 +352,11 @@ jobs:
340352
publish:
341353
name: Publish to ${{ inputs.target || 'pypi' }}
342354
needs: release
355+
# On PR-merge events, inputs.skip_publish is undefined (empty string),
356+
# which is != 'true', so publish runs. This is intentional.
343357
if: inputs.skip_publish != 'true'
344358
runs-on: ubuntu-latest
359+
timeout-minutes: 30
345360
environment: ${{ inputs.target || 'pypi' }}
346361
permissions:
347362
id-token: write # Trusted publishing (OIDC)
@@ -364,7 +379,7 @@ jobs:
364379

365380
- name: Install workspace
366381
working-directory: ${{ env.WORKSPACE_DIR }}
367-
run: uv sync
382+
run: uv sync --no-dev
368383

369384
- name: Install releasekit
370385
working-directory: ${{ env.RELEASEKIT_DIR }}
@@ -383,6 +398,8 @@ jobs:
383398
384399
- name: Run releasekit publish
385400
run: |
401+
set -euo pipefail
402+
386403
cmd=(uv run --directory ${{ env.RELEASEKIT_DIR }} releasekit --workspace py publish --force)
387404
388405
if [ "${{ env.DRY_RUN }}" = "true" ]; then
@@ -410,9 +427,15 @@ jobs:
410427
cmd+=(--max-retries "$MAX_RETRIES")
411428
fi
412429
430+
# Capture output even on failure so CI logs show diagnostics.
413431
echo "::group::Running: ${cmd[*]}"
414-
"${cmd[@]}"
432+
OUTPUT=$("${cmd[@]}" 2>&1) || EXIT_CODE=$?
433+
echo "$OUTPUT"
415434
echo "::endgroup::"
435+
if [ "${EXIT_CODE:-0}" -ne 0 ]; then
436+
echo "::error::releasekit publish failed with exit code $EXIT_CODE"
437+
exit $EXIT_CODE
438+
fi
416439
env:
417440
# For trusted publishing (OIDC), no token needed.
418441
# For API token auth, set PYPI_TOKEN / TESTPYPI_TOKEN in repo secrets.
@@ -437,10 +460,14 @@ jobs:
437460
needs: publish
438461
if: success()
439462
runs-on: ubuntu-latest
463+
timeout-minutes: 5
440464
steps:
441465
- name: Dispatch release event
442466
uses: peter-evans/repository-dispatch@v3
443467
with:
468+
# NOTE: GITHUB_TOKEN is scoped to this repo only. For cross-repo
469+
# dispatch (e.g. genkit-community-plugins), replace with a PAT
470+
# or GitHub App token stored in secrets.
444471
token: ${{ secrets.GITHUB_TOKEN }}
445472
event-type: genkit-python-release
446473
client-payload: '{"release_url": "${{ needs.release.outputs.release_url }}"}'

releasekit.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ repo_owner = "firebase"
8585
# Python workspace
8686
# ---------------------------------------------------------------------------
8787
[workspace.py]
88+
bootstrap_sha = "b71a3d20c74b71583edbc652e5b26117caad43f4" # py/v0.5.0
8889
changelog = true
8990
core_package = "genkit"
9091
ecosystem = "python"
@@ -94,7 +95,6 @@ plugin_dirs = ["plugins"]
9495
plugin_prefix = "genkit-plugin-"
9596
root = "py"
9697
smoke_test = true
97-
bootstrap_sha = "b71a3d20c74b71583edbc652e5b26117caad43f4" # py/v0.5.0
9898
tag_format = "{label}/{name}-v{version}"
9999
tool = "uv"
100100
umbrella_tag = "{label}/v{version}"

0 commit comments

Comments
 (0)