From ce5555500b554b4e92d2a5d07b06bbe89f749898 Mon Sep 17 00:00:00 2001 From: Burak Yigit Kaya Date: Fri, 31 Jul 2026 23:40:43 +0000 Subject: [PATCH 1/2] fix(upgrade): restrict delta patch from-version to same major.minor series MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PREV_TAG loop in `generate-patches` was walking past all 0.40 nightly tags and onto the lone 0.41.0-dev.1785504113 tag because `sort -V` orders 0.41 > 0.40 lexicographically. At the time generate-patches runs, the current build's tag hasn't been pushed yet, so the loop's `if [ "$tag" = "nightly-${VERSION}" ]` break never triggers — the loop ends naturally after iterating every existing tag, and PREV_TAG becomes the 0.41 nightly. Result: patches were generated FROM the 0.41 binary but stamped with `from-version: 0.41.x-dev.Y` in the annotation. chain resolution then fails for any user on the 0.40 series (their current version is chronologically *before* 0.41.0-dev.1785504113 and the patch's from-version annotation doesn't match any chain step), so delta upgrades fall back to full-download — every time. Repro on the live registry: ``` # Inspect recent patches $ ghcr.io/getsentry/cli:patch-0.40.0-dev.1785524715 from-version: 0.41.0-dev.1785504113 ← WRONG (should be 0.40.0-dev.1785522378) $ ghcr.io/getsentry/cli:patch-0.40.0-dev.1785526951 from-version: 0.41.0-dev.1785504113 ← WRONG (should be 0.40.0-dev.1785524715) ``` Fix: filter TAGS to only those in the same major.minor series as the current build before iterating. `MAJOR_MINOR` is parsed from VERSION (`echo "${VERSION}" | cut -d. -f1,2` → e.g. `0.40`). For `0.40.0-dev.X` builds the loop only sees `nightly-0.40.x-dev.*` tags; for `0.41.0-dev.X` only `nightly-0.41.x-dev.*`. The first build of a new series (no prior tags in the same series) falls through with PREV_TAG="" and the existing HAS_PREV skip handles it cleanly. Retroactive remediation: the two mis-annotated patches above will need their `from-version` annotations re-stamped (their patch bytes are already correct — only the annotation is wrong). A separate follow-up will re-publish `patch-0.40.0-dev.1785524715` with `from-version=0.40.0-dev.1785522378` and `patch-0.40.0-dev.1785526951` with `from-version=0.40.0-dev.1785524715` so existing users on the 0.40 series can chain through them. PR #1327 already prevents annotation drift between generate-patches and publish-nightly — this fix prevents the initial PREV_TAG selection from being wrong in the first place. --- .github/workflows/ci.yml | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 31f6f9e95..731a73a59 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -488,12 +488,28 @@ jobs: echo "${{ secrets.GITHUB_TOKEN }}" | oras login ghcr.io -u ${{ github.actor }} --password-stdin - # Find previous nightly tag + # Find previous nightly tag, restricted to the same major.minor series + # as the current build. Without this filter, `sort -V` places every + # 0.41 nightly after every 0.40 nightly (0.41 > 0.40), and the loop + # below walks past all 0.40 tags and onto the 0.41 tag — even though + # the current build hasn't pushed its own tag yet, so the + # `if [ "$tag" = "nightly-${VERSION}" ]` break never triggers. + # Result: PREV_TAG ends up being the 0.41 nightly, and the resulting + # patch is generated FROM the 0.41 binary and stamped with + # `from-version: 0.41.x-dev.Y` — useless to any user on the 0.40 + # series. See commit message for the exact scenario. TAGS=$(oras repo tags "${REPO}" 2>/dev/null | grep '^nightly-[0-9]' | sort -V || echo "") + # MAJOR_MINOR is e.g. "0.40" — restricts the loop to tags that + # belong to the same series as the current build. The first build + # of a new series (no prior tags in the same series) falls through + # with PREV_TAG="" and the existing HAS_PREV skip handles it. + MAJOR_MINOR=$(echo "${VERSION}" | cut -d. -f1,2) + SAME_SERIES_TAGS=$(printf '%s\n' "$TAGS" | grep "^nightly-${MAJOR_MINOR}\." || true) PREV_TAG="" - for tag in $TAGS; do + for tag in $SAME_SERIES_TAGS; do # Current tag may not exist yet (publish-nightly hasn't run), - # but that's fine — we want the latest existing nightly tag + # but that's fine — we want the latest existing nightly tag in + # the same series as the current build. if [ "$tag" = "nightly-${VERSION}" ]; then break fi @@ -501,7 +517,7 @@ jobs: done if [ -z "$PREV_TAG" ]; then - echo "No previous versioned nightly found, skipping patches" + echo "No previous nightly tag in ${MAJOR_MINOR}.x series, skipping patches" exit 0 fi echo "HAS_PREV=true" >> "$GITHUB_ENV" From 4725a8b8deb9212f2f086ca964efd2d87f9b5834 Mon Sep 17 00:00:00 2001 From: Burak Yigit Kaya Date: Fri, 31 Jul 2026 23:47:16 +0000 Subject: [PATCH 2/2] fix(upgrade): use grep -F for same-series prefix match MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adversarial review (NIT-2): the dot in `0.40` was being interpolated into a regex `grep` pattern, where `.` matches any character. In practice this can't mis-match because `oras repo tags` is filtered to `^nightly-[0-9]` upstream and `MAJOR_MINOR` is always digits.digits — but `grep -F` (fixed-string) makes the intent explicit and matches the defensive style of the rest of the script. --- .github/workflows/ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 731a73a59..f17498f2f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -504,7 +504,11 @@ jobs: # of a new series (no prior tags in the same series) falls through # with PREV_TAG="" and the existing HAS_PREV skip handles it. MAJOR_MINOR=$(echo "${VERSION}" | cut -d. -f1,2) - SAME_SERIES_TAGS=$(printf '%s\n' "$TAGS" | grep "^nightly-${MAJOR_MINOR}\." || true) + # -F (fixed-string) avoids treating the dot in "0.40" as a regex + # metachar. In practice `oras repo tags` upstream is filtered to + # `^nightly-[0-9]` and MAJOR_MINOR is always digits.digits, so this + # can't actually mis-match — but be defensive. + SAME_SERIES_TAGS=$(printf '%s\n' "$TAGS" | grep -F "nightly-${MAJOR_MINOR}." || true) PREV_TAG="" for tag in $SAME_SERIES_TAGS; do # Current tag may not exist yet (publish-nightly hasn't run),