Skip to content

Commit 472d0d6

Browse files
authored
fix(delta-upgrade): filter non-versioned nightly tags from GHCR patch generation (#753)
## Summary A stale `nightly-test` tag in GHCR (created Feb 26 for testing, never cleaned up) broke **every** nightly delta upgrade. This PR fixes the root cause, adds guardrails, and cleans up the damage. ### Root cause `generate-patches` runs **before** `publish-nightly`, so the current version's nightly tag doesn't exist yet. The tag loop (`grep '^nightly-' | sort -V`) fell through to `nightly-test` (which sorts last) as the "previous version." Its empty binaries (44 bytes `.gz`) produced ~23 MB patches that exceeded the client's 60% size budget on every upgrade attempt. ### What was done **Immediate cleanup (manual):** - Deleted the `nightly-test` tag from GHCR (version ID 706511735) - Deleted all 82 corrupted `patch-*` tags that were generated against the empty `nightly-test` binaries (201 good patches preserved) **Preventive CI fix:** - Changed `grep '^nightly-'` → `grep '^nightly-[0-9]'` in all 3 locations (generate-patches, publish-nightly, cleanup-nightlies) to reject non-versioned tags - Added a "Validate patch sizes" CI step that rejects patches exceeding 50% of binary size, removes them from the upload, and auto-files a GitHub issue **Diagnostic improvement:** - Refactored `validateChainStep` to return a discriminated union (`ChainStepResult`) with typed failure reasons (`version-mismatch`, `missing-layer`, `size-exceeded`) instead of bare `null` - Debug log now shows the specific failure reason instead of a misleading generic `budget=X` message ### Files changed | File | Change | |------|--------| | `.github/workflows/ci.yml` | `grep '^nightly-[0-9]'` filter + patch size validation step | | `.github/workflows/cleanup-nightlies.yml` | `grep '^nightly-[0-9]'` filter | | `src/lib/delta-upgrade.ts` | `validateChainStep` returns typed failures, `formatStepFailure` helper | | `test/lib/delta-upgrade.test.ts` | 4 unit tests for `validateChainStep` |
1 parent e3ca859 commit 472d0d6

File tree

5 files changed

+261
-75
lines changed

5 files changed

+261
-75
lines changed

.github/workflows/ci.yml

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ jobs:
328328
echo "${{ secrets.GITHUB_TOKEN }}" | oras login ghcr.io -u ${{ github.actor }} --password-stdin
329329
330330
# Find previous nightly tag
331-
TAGS=$(oras repo tags "${REPO}" 2>/dev/null | grep '^nightly-' | sort -V || echo "")
331+
TAGS=$(oras repo tags "${REPO}" 2>/dev/null | grep '^nightly-[0-9]' | sort -V || echo "")
332332
PREV_TAG=""
333333
for tag in $TAGS; do
334334
# Current tag may not exist yet (publish-nightly hasn't run),
@@ -408,6 +408,56 @@ jobs:
408408
409409
echo "Generated ${GENERATED} patches"
410410
411+
- name: Validate patch sizes
412+
if: env.HAS_PREV == 'true'
413+
env:
414+
GH_TOKEN: ${{ github.token }}
415+
# Client rejects chains exceeding 60% of gzipped binary size
416+
# (SIZE_THRESHOLD_RATIO in src/lib/delta-upgrade.ts). Use 50% here
417+
# so single-step patches are caught with margin to spare.
418+
MAX_RATIO: 50
419+
run: |
420+
shopt -s nullglob
421+
OVERSIZED=""
422+
423+
for patch_file in patches/*.patch; do
424+
name=$(basename "$patch_file" .patch)
425+
gz_binary="new-binaries/${name}.gz"
426+
[ -f "$gz_binary" ] || continue
427+
428+
patch_size=$(stat --printf='%s' "$patch_file")
429+
gz_size=$(stat --printf='%s' "$gz_binary")
430+
ratio=$(awk "BEGIN { printf \"%.0f\", ($patch_size / $gz_size) * 100 }")
431+
432+
if [ "$ratio" -gt "$MAX_RATIO" ]; then
433+
echo "::error::Patch ${name}.patch is ${ratio}% of gzipped binary (limit: ${MAX_RATIO}%)"
434+
OVERSIZED="$(printf '%s\n- `%s.patch`: %s%% of gzipped binary (%s / %s bytes)' "$OVERSIZED" "$name" "$ratio" "$patch_size" "$gz_size")"
435+
rm "$patch_file"
436+
fi
437+
done
438+
439+
if [ -n "$OVERSIZED" ]; then
440+
TITLE="Delta patch generation produced oversized patches"
441+
BODY="$(cat <<ISSUE_EOF
442+
The \`generate-patches\` job produced patches exceeding ${MAX_RATIO}% of gzipped binary size:
443+
${OVERSIZED}
444+
445+
These patches were **excluded** from the artifact upload. This usually means the old binary download failed (empty/wrong file).
446+
447+
**Branch:** \`${GITHUB_REF_NAME}\`
448+
**Commit:** ${GITHUB_SHA}
449+
**Run:** ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}
450+
ISSUE_EOF
451+
)"
452+
453+
EXISTING=$(gh issue list --repo "$GITHUB_REPOSITORY" --state open --search "$TITLE" --json number --jq '.[0].number // empty')
454+
if [ -n "$EXISTING" ]; then
455+
gh issue comment "$EXISTING" --repo "$GITHUB_REPOSITORY" --body "$BODY"
456+
else
457+
gh issue create --repo "$GITHUB_REPOSITORY" --title "$TITLE" --body "$BODY" --label bug
458+
fi
459+
fi
460+
411461
- name: Upload patch artifacts
412462
if: env.HAS_PREV == 'true'
413463
uses: actions/upload-artifact@v7
@@ -531,7 +581,7 @@ jobs:
531581
fi
532582
533583
# Find from-version by listing GHCR nightly tags
534-
TAGS=$(oras repo tags "${REPO}" 2>/dev/null | grep '^nightly-' | sort -V || echo "")
584+
TAGS=$(oras repo tags "${REPO}" 2>/dev/null | grep '^nightly-[0-9]' | sort -V || echo "")
535585
PREV_TAG=""
536586
for tag in $TAGS; do
537587
if [ "$tag" = "nightly-${VERSION}" ]; then break; fi

.github/workflows/cleanup-nightlies.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
REPO="ghcr.io/getsentry/cli"
3535
3636
# List all nightly-* tags sorted by version, oldest first
37-
NIGHTLY_TAGS=$(oras repo tags "${REPO}" 2>/dev/null | grep '^nightly-' | sort -V || echo "")
37+
NIGHTLY_TAGS=$(oras repo tags "${REPO}" 2>/dev/null | grep '^nightly-[0-9]' | sort -V || echo "")
3838
if [ -z "$NIGHTLY_TAGS" ]; then
3939
NIGHTLY_COUNT=0
4040
else

0 commit comments

Comments
 (0)