From bec1af5c88e910858113e5a700ae81d8fa8008be Mon Sep 17 00:00:00 2001 From: Brett Kinny Date: Thu, 16 Jul 2026 19:06:17 +1000 Subject: [PATCH 1/2] Fix v1.1 release candidate blockers --- .github/workflows/e2e.yml | 14 ++++++++++++ CHANGELOG.md | 5 ++++- CLAUDE.md | 9 +++++--- CONTRIBUTING.md | 12 ++++++++--- Dockerfile | 18 ++++++++++++---- README.md | 2 +- ROADMAP.md | 2 +- checksums.txt | 3 --- demo/setup-demo.sh | 2 +- docs/releases/v1.1.0.md | 19 +++++++++-------- scripts/e2e-evidence.sh | 4 ++++ scripts/lib/tools.yaml | 16 ++++---------- scripts/squarebox-update.sh | 1 - tests/test-e2e-evidence.sh | 8 ++++++- tests/test-gum-image-policy.sh | 39 ++++++++++++++++++++++++++++++++++ tests/test-tool-lib.sh | 38 +++++++++++++++++++++++++++++++++ 16 files changed, 152 insertions(+), 40 deletions(-) create mode 100755 tests/test-gum-image-policy.sh diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index c808843..c94fb98 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -419,9 +419,22 @@ jobs: SQUAREBOX_WORKSPACE=$(mktemp -d) export SQUAREBOX_WORKSPACE cleanup() { docker compose down -v --remove-orphans >/dev/null 2>&1 || true; } + wait_for_compose_workspace() { + local attempt + for attempt in {1..30}; do + if docker compose exec -T -u "$PUID:$PGID" squarebox sh -c \ + 'test "$(stat -c %u /proc/1)" = "$1" && test -w /workspace' _ "$PUID"; then + return 0 + fi + sleep 1 + done + docker compose logs squarebox >&2 + return 1 + } trap cleanup EXIT docker compose config >/dev/null docker compose up -d + wait_for_compose_workspace original_box=$(docker compose ps -q squarebox) test -n "$original_box" test "$(docker inspect --format '{{.Config.Image}}' "$original_box")" = "$SQUAREBOX_IMAGE_REF" @@ -432,6 +445,7 @@ jobs: test -f "$SQUAREBOX_WORKSPACE/from-compose" docker compose up -d --force-recreate --no-deps squarebox + wait_for_compose_workspace replacement_box=$(docker compose ps -q squarebox) test -n "$replacement_box" test "$replacement_box" != "$original_box" diff --git a/CHANGELOG.md b/CHANGELOG.md index dfaf06a..6f84a2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## v1.1.0 — unreleased +## v1.1.0 — 2026-07-16 ### Added @@ -57,6 +57,9 @@ state, and image-tier updates advertised without Candidate-authorized bytes. - Fish derived aliases becoming stale after section-only setup reruns. - CI reports claiming behavior that was not executed. +- Release-candidate failures caused by a Compose UID-remap startup race, + cross-UID Evidence file permissions, LazyGit's lowercase Linux asset name, + and Gum release binaries built with a vulnerable Go standard library. ### Removed diff --git a/CLAUDE.md b/CLAUDE.md index d9b756b..d53c610 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -106,9 +106,12 @@ edit, fresh, Helix (`hx`), and Neovim/LazyVim. ## Tool registry and verified installation -`scripts/lib/tools.yaml` owns artifact metadata, Tool tier, verification policy, -and Docker ARG mapping. Observed version probes remain adapter functions in -`scripts/squarebox-update.sh`. The awk reader avoids requiring yq to install yq. +`scripts/lib/tools.yaml` owns release-asset metadata, Tool tier, verification +policy, and Docker ARG mapping. Observed version probes remain adapter functions +in `scripts/squarebox-update.sh`. The awk reader avoids requiring yq to install +yq. Gum is the sole OCI-sourced exception: Dockerfile pins its exact upstream +multi-architecture digest and asserts the reviewed version/commit, while +`tests/test-gum-image-policy.sh` rejects mutable or unreviewed references. `scripts/lib/tool-lib.sh` is the deep artifact-install module. Its interface must remain fail-closed across metadata validation, download, verification, diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3211287..031a020 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -104,15 +104,21 @@ Every registry entry declares its Tool tier and verification policy. - Image-tier direct downloads use pinned versions and `sha256`; missing or mismatched checksums must fail closed. +- Image-tier OCI inputs use an exact multi-architecture digest and assert their + reviewed binary identity during the image build. Gum currently uses this path + because its latest stable release tar embeds a vulnerable Go standard library; + `tests/test-gum-image-policy.sh` locks the reviewed exception. - Managed-home GitHub release downloads use the SHA-256 digest on the exact release asset. Missing, malformed, duplicate, or mismatched digests fail before installation mutates the destination. - Box-tier packages must be reconciled after Box replacement; a saved Selection is not proof the package exists. -Use `scripts/update-versions.sh` for image-tier refreshes. It generates and -validates checksums and Dockerfile pins transactionally. Review upstream release -notes and the complete diff before accepting a refresh. +Use `scripts/update-versions.sh` for release-asset image-tier refreshes. It +generates and validates checksums and Dockerfile pins transactionally. Review +upstream release notes and the complete diff before accepting a refresh. Review +OCI-sourced exceptions by digest, per-architecture binary identity, and scan +result instead of adding a mutable tag to the release-asset registry. Adding a tool requires updating every behavior represented by registry metadata or adding metadata so derived inventories remain consistent. Add deterministic diff --git a/Dockerfile b/Dockerfile index 6819161..22a85f7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,10 @@ +# Gum v0.17.0's release binaries embed vulnerable Go 1.25.1. Upstream has not +# published a newer stable release, so copy the fixed development binary from +# its exact multi-architecture OCI manifest. The build below asserts the +# expected v0.17.1-devel commit identity before the binary enters the Box. +ARG GUM_IMAGE=ghcr.io/charmbracelet/gum@sha256:426c1e40739f11083e06d58ffaac910289eeace709a3d9bddcb8d4566140c93c +FROM ${GUM_IMAGE} AS gum-source + FROM ubuntu:24.04 # 1. Base Packages & Rust CLI Tools (consolidated, slimmed) @@ -59,10 +66,11 @@ ARG YQ_VERSION=4.53.3 ARG XH_VERSION=0.26.1 ARG STARSHIP_VERSION=1.26.0 ARG GLOW_VERSION=2.1.2 -ARG GUM_VERSION=0.17.0 ARG JUST_VERSION=1.56.0 ARG DIFFTASTIC_VERSION=0.69.0 ARG MISE_VERSION=2026.7.5 +ARG GUM_EXPECTED_VERSION=v0.17.1-devel +ARG GUM_EXPECTED_COMMIT=591ded2 # Validate version ARGs are non-empty RUN test -n "$DELTA_VERSION" || { echo "Error: DELTA_VERSION is empty" >&2; exit 1; } \ @@ -70,10 +78,11 @@ RUN test -n "$DELTA_VERSION" || { echo "Error: DELTA_VERSION is empty" >&2 && test -n "$XH_VERSION" || { echo "Error: XH_VERSION is empty" >&2; exit 1; } \ && test -n "$STARSHIP_VERSION" || { echo "Error: STARSHIP_VERSION is empty" >&2; exit 1; } \ && test -n "$GLOW_VERSION" || { echo "Error: GLOW_VERSION is empty" >&2; exit 1; } \ - && test -n "$GUM_VERSION" || { echo "Error: GUM_VERSION is empty" >&2; exit 1; } \ && test -n "$JUST_VERSION" || { echo "Error: JUST_VERSION is empty" >&2; exit 1; } \ && test -n "$DIFFTASTIC_VERSION" || { echo "Error: DIFFTASTIC_VERSION is empty" >&2; exit 1; } \ - && test -n "$MISE_VERSION" || { echo "Error: MISE_VERSION is empty" >&2; exit 1; } + && test -n "$MISE_VERSION" || { echo "Error: MISE_VERSION is empty" >&2; exit 1; } \ + && test -n "$GUM_EXPECTED_VERSION" || { echo "Error: GUM_EXPECTED_VERSION is empty" >&2; exit 1; } \ + && test -n "$GUM_EXPECTED_COMMIT" || { echo "Error: GUM_EXPECTED_COMMIT is empty" >&2; exit 1; } # Checksum verification infrastructure COPY checksums.txt /tmp/checksums.txt @@ -117,7 +126,8 @@ RUN . /tmp/sb-init.sh && sb_install delta "$DELTA_VERSION" RUN . /tmp/sb-init.sh && sb_install yq "$YQ_VERSION" RUN . /tmp/sb-init.sh && sb_install xh "$XH_VERSION" RUN . /tmp/sb-init.sh && sb_install glow "$GLOW_VERSION" -RUN . /tmp/sb-init.sh && sb_install gum "$GUM_VERSION" +COPY --from=gum-source /usr/local/bin/gum /usr/local/bin/gum +RUN test "$(gum --version)" = "gum version ${GUM_EXPECTED_VERSION} (${GUM_EXPECTED_COMMIT})" RUN . /tmp/sb-init.sh && sb_install starship "$STARSHIP_VERSION" RUN . /tmp/sb-init.sh && sb_install just "$JUST_VERSION" RUN . /tmp/sb-init.sh && sb_install difftastic "$DIFFTASTIC_VERSION" diff --git a/README.md b/README.md index 394f930..cb3b12c 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ Flags: `--build` (build from source), `--edge` (latest `main`), `--adopt` |----------|---------|---------| | `SQUAREBOX_DIR` | `~/squarebox` | Install location (repo + workspace). Point at durable storage on hosts where `$HOME` is volatile — e.g. Unraid `/mnt/user/appdata/squarebox`. | | `SQUAREBOX_WORKSPACE` | `$SQUAREBOX_DIR/workspace` | Host path mounted as `/workspace`. | -| `SQUAREBOX_TAG` | latest published stable | Published Release to install (for example `v1.1.0-rc5`). Tags use `vMAJOR.MINOR.PATCH[-prerelease]`; build metadata is not published. | +| `SQUAREBOX_TAG` | latest published stable | Published Release to install (for example `v1.1.0`). Tags use `vMAJOR.MINOR.PATCH[-prerelease]`; build metadata is not published. | | `SQUAREBOX_IMAGE` | value from `release.json` | Optional image-repository override for development/testing. | | `SQUAREBOX_BUILD` | `0` | `1` is equivalent to `--build`. | | `PUID` / `PGID` | invoking Linux user | Host uid/gid that should own bind-mounted files. Docker/rootful hosts may override these (Unraid/NAS: `99` / `100`); rootless Podman requires the invoking host identity. | diff --git a/ROADMAP.md b/ROADMAP.md index 67ba57f..8f73472 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -6,7 +6,7 @@ - Complete the real-host matrix in `uat-checklist.md`, especially Windows, macOS, Fedora rootless Podman/SELinux, Dev Containers/Codespaces, and physical arm64 (tracked by GitHub issues #99–#105). -- Cut `v1.1.0-rc5` as the automatic publication rehearsal. +- `v1.1.0-rc5` completed the automatic publication rehearsal. - Keep GitHub immutable Releases enabled, keep the no-bypass `v*` update and deletion ruleset active, and verify the stable/prerelease environment protections before creating a final tag. diff --git a/checksums.txt b/checksums.txt index 4204905..af1531d 100644 --- a/checksums.txt +++ b/checksums.txt @@ -14,9 +14,6 @@ c411f07a0b204ca07858a67473d0f5c77e332226430d4d84d1d2afd351a425f2 xh-v0.26.1-x86 # Glow 2.1.2 6063d4f2af8a82a5f4bba0831e165de9381660aa8b41df4816d0106a265b07d5 glow_2.1.2_Linux_x86_64.tar.gz cf63abebcb50b72909db965d78290e7cecbf17a900e84705dc84addbb6952099 glow_2.1.2_Linux_arm64.tar.gz -# Gum 0.17.0 -69ee169bd6387331928864e94d47ed01ef649fbfe875baed1bbf27b5377a6fdb gum_0.17.0_Linux_x86_64.tar.gz -b0b9ed95cbf7c8b7073f17b9591811f5c001e33c7cfd066ca83ce8a07c576f9c gum_0.17.0_Linux_arm64.tar.gz # Starship 1.26.0 b7c232b0e8249d8e55a40beb79c5c43a7d370f3f9408bd215deb0170daeaadf3 starship-x86_64-unknown-linux-musl.tar.gz dc30189378d2f2e287384e8a692d3f95ad1df64cf0e8c36aa9201516028aed6b starship-aarch64-unknown-linux-musl.tar.gz diff --git a/demo/setup-demo.sh b/demo/setup-demo.sh index 7436809..294c7d3 100755 --- a/demo/setup-demo.sh +++ b/demo/setup-demo.sh @@ -149,7 +149,7 @@ echo printf '\e[1;38;5;208m' toilet -f smblock --metal "squarebox" printf '\e[0m' -printf '\e[38;5;208m 🟧📦 You'\''re in the box.\e[0m\e[38;5;245m (v1.1.0-rc5)\e[0m\n' +printf '\e[38;5;208m 🟧📦 You'\''re in the box.\e[0m\e[38;5;245m (v1.1.0)\e[0m\n' printf '\e[38;5;172m %s\e[0m\n' "$(date '+%A, %B %d %Y %H:%M')" printf '\e[38;5;245m Node 22.x ◆ Python 3.x\e[0m\n' printf '\e[38;5;208m ✦ sqrbx-help\e[0m\e[38;5;245m — commands and keyboard shortcuts\e[0m\n' diff --git a/docs/releases/v1.1.0.md b/docs/releases/v1.1.0.md index eb36345..b565930 100644 --- a/docs/releases/v1.1.0.md +++ b/docs/releases/v1.1.0.md @@ -1,7 +1,8 @@ # Squarebox v1.1.0 migration guide -v1.1 changes the lifecycle model as well as adding tools. Test an upgrade with -`v1.1.0-rc5` before promoting the Release used for important work. +v1.1 changes the lifecycle model as well as adding tools. Test the published +`v1.1.0` Release against a non-production installation before upgrading +important work. ## Before upgrading @@ -68,15 +69,15 @@ Native PowerShell exposes `%USERPROFILE%\.ssh` read-only; it does not forward | Image-tier binaries | Candidate image | Replaced by the published digest | | Manually installed, unselected APT packages | Disposable Box filesystem | Lost | -## Release-candidate installation +## Version-pinned installation -Use the Candidate's published tag, which resolves matching source and image -identity from its Release assets: +Use the Release's published tag, which resolves matching source and image +identity from its assets: ```bash ( set -euo pipefail - tag=v1.1.0-rc5 + tag=v1.1.0 tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT curl -fsSLo "$tmp/install.sh" \ @@ -94,9 +95,9 @@ identity from its Release assets: ) ``` -This deliberately downloads the Candidate's installer instead of invoking the -old adapter already present in a v1.0 checkout. Do not combine an RC image with -a stable checkout. Verify `release.json`, the MOTD version, and +This deliberately downloads the Release's installer instead of invoking the +old adapter already present in a v1.0 checkout. Do not combine an image with a +checkout from a different Release. Verify `release.json`, the MOTD version, and `/usr/local/lib/squarebox/VERSION` agree after installation. ## Rollback diff --git a/scripts/e2e-evidence.sh b/scripts/e2e-evidence.sh index 244674a..3aac6cb 100755 --- a/scripts/e2e-evidence.sh +++ b/scripts/e2e-evidence.sh @@ -46,5 +46,9 @@ printf '%s\t%s\t%s\t%s\n' \ "$status" \ "$(clean_field "$description")" \ "$(clean_field "$detail")" > "$tmp" +# Evidence crosses the container/runner UID boundary before artifact upload. +# mktemp honours the container's restrictive umask, so normalize the completed +# file while it is still staged and preserve the atomic rename below. +chmod 0644 "$tmp" mv -f "$tmp" "$evidence_dir/${safe_id}.evidence" trap - EXIT diff --git a/scripts/lib/tools.yaml b/scripts/lib/tools.yaml index edb8d82..338265a 100644 --- a/scripts/lib/tools.yaml +++ b/scripts/lib/tools.yaml @@ -65,17 +65,9 @@ tools: verification: sha256 docker_arg: GLOW_VERSION - gum: - repo: charmbracelet/gum - version_prefix: v - artifact: gum_{version}_Linux_{larch}.tar.gz - method: tar.gz - binaries: gum - find_binary: true - dest: system - group: dockerfile - verification: sha256 - docker_arg: GUM_VERSION + # Gum is copied from an immutable, multi-architecture upstream OCI manifest + # in Dockerfile. It is intentionally outside the release-tar updater because + # upstream's latest stable tar embeds a vulnerable Go standard library. starship: repo: starship/starship @@ -129,7 +121,7 @@ tools: lazygit: repo: jesseduffield/lazygit version_prefix: v - artifact: lazygit_{version}_Linux_{larch}.tar.gz + artifact: lazygit_{version}_linux_{larch}.tar.gz method: tar.gz binaries: lazygit tar_extract: lazygit diff --git a/scripts/squarebox-update.sh b/scripts/squarebox-update.sh index 7c13d0d..64642a1 100755 --- a/scripts/squarebox-update.sh +++ b/scripts/squarebox-update.sh @@ -297,7 +297,6 @@ yazi_current() { yazi --version 2>/dev/null | _extract_first_version; } starship_current() { starship --version 2>/dev/null | _extract_first_version; } ghdash_current() { gh-dash --version 2>/dev/null | _extract_first_version; } glow_current() { glow --version 2>/dev/null | _extract_first_version; } -gum_current() { gum --version 2>/dev/null | _extract_first_version; } micro_current() { micro --version 2>/dev/null | _extract_first_version; } fresh_current() { fresh --version 2>/dev/null | _extract_first_version; } edit_current() { edit --version 2>/dev/null | _extract_first_version; } diff --git a/tests/test-e2e-evidence.sh b/tests/test-e2e-evidence.sh index 1373286..bc88e45 100755 --- a/tests/test-e2e-evidence.sh +++ b/tests/test-e2e-evidence.sh @@ -20,7 +20,11 @@ report() { "$ROOT/scripts/e2e-report.sh" "$TMP/evidence" } -record pass alpha "Alpha requirement" +(umask 077; record pass alpha "Alpha requirement") +test "$(stat -c %a "$TMP/evidence/alpha.evidence")" = 644 || { + echo "FAIL: container-created Evidence is not readable by the artifact runner" >&2 + exit 1 +} if report > "$TMP/missing.md"; then echo "FAIL: report accepted missing required evidence" >&2 @@ -107,6 +111,8 @@ grep -Fq 'interactive prompt detected' "$RUNNER" # Compose Evidence must observe actual replacement and both persistent stores. grep -Fq 'docker compose up -d --force-recreate --no-deps squarebox' "$WORKFLOW" grep -Fq 'test "$replacement_box" != "$original_box"' "$WORKFLOW" +test "$(grep -Fc 'wait_for_compose_workspace' "$WORKFLOW")" -eq 3 +grep -Fq 'test "$(stat -c %u /proc/1)" = "$1" && test -w /workspace' "$WORKFLOW" grep -Fq '~/.squarebox-compose-e2e' "$WORKFLOW" grep -Fq '$SQUAREBOX_WORKSPACE/from-compose' "$WORKFLOW" grep -Fq -- '--cap-drop=ALL --cap-add=CHOWN --cap-add=DAC_OVERRIDE' "$WORKFLOW" diff --git a/tests/test-gum-image-policy.sh b/tests/test-gum-image-policy.sh new file mode 100755 index 0000000..b3a68a2 --- /dev/null +++ b/tests/test-gum-image-policy.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +DOCKERFILE="$ROOT/Dockerfile" +EXPECTED_IMAGE='ghcr.io/charmbracelet/gum@sha256:426c1e40739f11083e06d58ffaac910289eeace709a3d9bddcb8d4566140c93c' + +fail() { + echo "FAIL: $*" >&2 + exit 1 +} + +mapfile -t gum_images < <(sed -n 's/^ARG GUM_IMAGE=//p' "$DOCKERFILE") +[ "${#gum_images[@]}" -eq 1 ] || fail "Dockerfile must declare exactly one Gum OCI image" +[ "${gum_images[0]}" = "$EXPECTED_IMAGE" ] \ + || fail "Gum OCI image must remain pinned to the reviewed multi-architecture digest" +[[ "${gum_images[0]}" =~ ^ghcr\.io/charmbracelet/gum@sha256:[0-9a-f]{64}$ ]] \ + || fail "Gum OCI image must not use a mutable tag" + +grep -Fqx 'FROM ${GUM_IMAGE} AS gum-source' "$DOCKERFILE" \ + || fail "Dockerfile does not consume the pinned Gum manifest" +grep -Fqx 'COPY --from=gum-source /usr/local/bin/gum /usr/local/bin/gum' "$DOCKERFILE" \ + || fail "Gum binary is not copied from the pinned upstream image" +grep -Fqx 'ARG GUM_EXPECTED_VERSION=v0.17.1-devel' "$DOCKERFILE" \ + || fail "expected Gum development version is not explicit" +grep -Fqx 'ARG GUM_EXPECTED_COMMIT=591ded2' "$DOCKERFILE" \ + || fail "expected Gum source commit is not explicit" +grep -Fq 'gum version ${GUM_EXPECTED_VERSION} (${GUM_EXPECTED_COMMIT})' "$DOCKERFILE" \ + || fail "image build does not assert Gum version and commit identity" + +if grep -Eq '^ARG GUM_VERSION=|sb_install gum' "$DOCKERFILE"; then + fail "obsolete Gum release-tar install path remains in Dockerfile" +fi +if grep -Eq '^ gum:|gum_[^ ]*Linux_(x86_64|arm64)[.]tar[.]gz' \ + "$ROOT/scripts/lib/tools.yaml" "$ROOT/checksums.txt"; then + fail "obsolete Gum release-tar registry or checksum metadata remains" +fi + +echo "PASS: Gum uses the reviewed immutable OCI manifest and asserted build identity" diff --git a/tests/test-tool-lib.sh b/tests/test-tool-lib.sh index a11b7a2..07961ae 100755 --- a/tests/test-tool-lib.sh +++ b/tests/test-tool-lib.sh @@ -286,6 +286,44 @@ else not_ok "exact release tag and exact asset name select the authoritative digest" fi +LAZYGIT_CASE="$TMP/lazygit-linux-asset" +mkdir -p "$LAZYGIT_CASE/home" "$LAZYGIT_CASE/cache" "$LAZYGIT_CASE/archive" +printf '#!/usr/bin/env bash\necho "lazygit version 0.63.0"\n' > "$LAZYGIT_CASE/archive/lazygit" +chmod +x "$LAZYGIT_CASE/archive/lazygit" +tar -czf "$LAZYGIT_CASE/lazygit_0.63.0_linux_x86_64.tar.gz" -C "$LAZYGIT_CASE/archive" lazygit +LAZYGIT_HASH=$(sha256sum "$LAZYGIT_CASE/lazygit_0.63.0_linux_x86_64.tar.gz" | awk '{print $1}') +if ( + export HOME="$LAZYGIT_CASE/home" SB_TOOLS_YAML="$REPO_ROOT/scripts/lib/tools.yaml" SB_DPKG_ARCH=amd64 + export SB_GITHUB_API_BASE=https://api.test SB_GH_METADATA_CACHE_DIR="$LAZYGIT_CASE/cache" + export LAZYGIT_CASE LAZYGIT_HASH + source "$REPO_ROOT/scripts/lib/tool-lib.sh" + curl() { + local output="" url="" + while [ "$#" -gt 0 ]; do + case "$1" in + -w) shift 2 ;; + -o) output=$2; shift 2 ;; + -*o) output=$2; shift 2 ;; + -*) shift ;; + *) url=$1; shift ;; + esac + done + printf '%s\n' "$url" >> "$LAZYGIT_CASE/curl.log" + if [[ "$url" == https://api.test/* ]]; then + printf '{"tag_name":"v0.63.0","assets":[{"name":"lazygit_0.63.0_linux_x86_64.tar.gz","digest":"sha256:%s"}]}\n200\n' "$LAZYGIT_HASH" + else + cp "$LAZYGIT_CASE/lazygit_0.63.0_linux_x86_64.tar.gz" "$output" + fi + } + sb_install lazygit 0.63.0 >/dev/null \ + && [ "$("$HOME/.local/bin/lazygit")" = 'lazygit version 0.63.0' ] \ + && grep -q '/releases/download/v0.63.0/lazygit_0.63.0_linux_x86_64.tar.gz$' "$LAZYGIT_CASE/curl.log" +); then + ok "production LazyGit registry installs the lowercase Linux release asset" +else + not_ok "production LazyGit registry installs the lowercase Linux release asset" +fi + run_github_digest_case duplicate-asset 1.0.0 \ '{"tag_name":"v1.0.0","assets":[{"name":"sample-1.0.0-amd64","digest":"sha256:'"$PAYLOAD_HASH"'"},{"name":"sample-1.0.0-amd64","digest":"sha256:'"$PAYLOAD_HASH"'"}]}' if [ "$(<"$TMP/duplicate-asset/rc")" -ne 0 ] \ From bb5cd3d3b1e7ee6881386bb1283cef65f8e0b3d5 Mon Sep 17 00:00:00 2001 From: Brett Kinny Date: Thu, 16 Jul 2026 19:11:40 +1000 Subject: [PATCH 2/2] Fix Compose retry lint --- .github/workflows/e2e.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index c94fb98..8438c73 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -420,8 +420,8 @@ jobs: export SQUAREBOX_WORKSPACE cleanup() { docker compose down -v --remove-orphans >/dev/null 2>&1 || true; } wait_for_compose_workspace() { - local attempt - for attempt in {1..30}; do + local _ + for _ in {1..30}; do if docker compose exec -T -u "$PUID:$PGID" squarebox sh -c \ 'test "$(stat -c %u /proc/1)" = "$1" && test -w /workspace' _ "$PUID"; then return 0