From 1065624c9f5a9f8e2e36864ead72fe97af833f5a Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 14 Apr 2026 00:40:01 +0200 Subject: [PATCH 1/6] feat: kubo v0.41.0-rc1 --- dists/kubo/versions | 1 + 1 file changed, 1 insertion(+) diff --git a/dists/kubo/versions b/dists/kubo/versions index 72e1471a..091ff984 100644 --- a/dists/kubo/versions +++ b/dists/kubo/versions @@ -82,3 +82,4 @@ v0.40.0-rc1 v0.40.0-rc2 v0.40.0 v0.40.1 +v0.41.0-rc1 From b98f11fee2a4a27dc8adeaae04abc3d71c46cac1 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 14 Apr 2026 00:50:39 +0200 Subject: [PATCH 2/6] chore(deps): bump github actions to latest versions --- .github/workflows/main.yml | 18 +++++++++--------- .github/workflows/nightly.yml | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 95bbdd49..28763b70 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,7 +34,7 @@ jobs: runs-on: ${{ fromJSON(vars.CI_BUILD_RUNS_ON || '"ubuntu-latest"') }} steps: - uses: actions/checkout@v5 - - uses: actions/setup-node@v5 + - uses: actions/setup-node@v6 with: node-version-file: '.tool-versions' - env: @@ -57,7 +57,7 @@ jobs: runs-on: "ubuntu-latest" steps: - uses: actions/checkout@v5 - - uses: actions/setup-node@v5 + - uses: actions/setup-node@v6 with: node-version-file: '.tool-versions' - run: npm ci --no-audit --progress=false @@ -77,7 +77,7 @@ jobs: steps: - uses: actions/checkout@v5 - name: Retrieve unsigned artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v6 with: name: releases-unsigned-diff path: releases @@ -94,7 +94,7 @@ jobs: - name: Import Keychain Certs # if this ever breaks, we should replace this magic with epxlicit security commands executed inside of it via.. nodejs # prior art: https://github.com/lando/code-sign-action/blob/f35d0b777ee592c758351252fa3f0d58f21e5129/action.yml#L106-L123 - uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2 + uses: apple-actions/import-codesign-certs@95e84a1a18f2bdbc5c6ab9b7f4429372e4b13a8b # v5.0.3 with: p12-file-base64: ${{ secrets.APPLE_CERTS_P12 }} p12-password: ${{ secrets.APPLE_CERTS_PASS }} @@ -146,11 +146,11 @@ jobs: steps: - uses: actions/checkout@v5 - name: Setup node - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version-file: '.tool-versions' - name: Retrieve signed artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v6 continue-on-error: true # skip if no releases with: name: releases-signed-macos-diff @@ -203,7 +203,7 @@ jobs: with: name: diff path: diff - - uses: actions/setup-go@v5 + - uses: actions/setup-go@v6 if: github.ref == 'refs/heads/master' with: go-version: "1.20.x" @@ -220,11 +220,11 @@ jobs: if: github.event_name == 'pull_request' runs-on: ubuntu-latest steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v6 with: name: diff - name: Create comment with the diff - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: | const fs = require('fs').promises diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 83bd6679..4d5f3519 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -47,7 +47,7 @@ jobs: - run: cd ./dists/${{ matrix.dist_name }} && make nightly - run: ./dockerized make publish - name: Create issue if build failed - uses: actions/github-script@v7 + uses: actions/github-script@v8 if: ${{ failure() }} with: script: | @@ -98,7 +98,7 @@ jobs: GIT_REVISION: ${{ github.sha }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Close any open issues about broken build - uses: actions/github-script@v7 + uses: actions/github-script@v8 if: ${{ success() }} with: script: | From 71011ecf9f141b241eef72eab8605453eb777599 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 14 Apr 2026 01:00:50 +0200 Subject: [PATCH 3/6] fix: switch asdf to go binary (v0.18.1) --- Dockerfile | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5c6b7c8d..1716f43a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,21 +12,23 @@ RUN curl -sS --retry 5 \ "https://dist.ipfs.tech/kubo/${KUBO_VER}/kubo_${KUBO_VER}_linux-amd64.tar.gz" -o /tmp/kubo.tar.gz && \ tar vzx -f /tmp/kubo.tar.gz -C /usr/local/bin/ kubo/ipfs --strip-components=1 +ARG ASDF_VERSION=v0.18.1 +RUN curl -sS --retry 5 \ + "https://github.com/asdf-vm/asdf/releases/download/${ASDF_VERSION}/asdf-${ASDF_VERSION}-linux-amd64.tar.gz" \ + | tar -xz -C /usr/local/bin asdf + RUN adduser --shell /bin/bash --home /asdf --disabled-password --gecos asdf asdf --uid $USER_UID -ENV PATH="${PATH}:/asdf/.asdf/shims:/asdf/.asdf/bin" +ENV ASDF_DATA_DIR=/asdf/.asdf +ENV PATH="${PATH}:/asdf/.asdf/shims" USER asdf WORKDIR /asdf -RUN git clone --depth 1 --branch v0.15.0 https://github.com/asdf-vm/asdf.git $HOME/.asdf && \ - echo '. $HOME/.asdf/asdf.sh' >> $HOME/.bashrc && \ - echo '. $HOME/.asdf/asdf.sh' >> $HOME/.profile - -RUN asdf plugin-add golang -RUN asdf plugin-add nodejs +RUN asdf plugin add golang +RUN asdf plugin add nodejs ADD .tool-versions . -RUN asdf install +RUN asdf install ENV IPFS_PATH=/asdf/.ipfs RUN mkdir -p ${IPFS_PATH} && echo "/ip4/127.0.0.1/tcp/5001" > ${IPFS_PATH}/api From e0c76efc2ba9d8f14842c8647be46dc0cde904ca Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 14 Apr 2026 01:06:18 +0200 Subject: [PATCH 4/6] chore: modernize Dockerfile and document glibc floor --- Dockerfile | 36 +++++++++++++++++++++++------------- build-go.sh | 5 +++++ 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1716f43a..29bcbf15 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,32 +1,41 @@ +# Pinned intentionally. Do NOT bump the base image without revisiting +# build-go.sh's glibc-check assertion (`minor < 32`). Ubuntu 20.04 ships +# glibc 2.31, which caps CGO-linked linux/amd64 binaries at GLIBC_2.31 +# symbols, keeping dist.ipfs.tech tarballs runnable on CentOS 7/RHEL 7, +# RHEL 8/9, Ubuntu 18.04/20.04, and Debian 10/11. Upgrading the base +# image is a user-facing compat break and must be announced in release +# notes. Revisit by 2026-08 (Debian 11 LTS EOL) to pick the next floor. FROM ubuntu:20.04 -ENV DEBIAN_FRONTEND noninteractive +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=Etc/UTC ARG USER_UID ARG KUBO_VER -RUN apt-get update -q && apt-get install -y git curl gnupg jq build-essential gawk zip tzdata && \ - ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime && \ - dpkg-reconfigure --frontend noninteractive tzdata +RUN apt-get update -q \ + && apt-get install -y --no-install-recommends \ + ca-certificates git curl gnupg jq build-essential gawk zip unzip tzdata \ + && rm -rf /var/lib/apt/lists/* -RUN curl -sS --retry 5 \ - "https://dist.ipfs.tech/kubo/${KUBO_VER}/kubo_${KUBO_VER}_linux-amd64.tar.gz" -o /tmp/kubo.tar.gz && \ +RUN curl -fsSL --retry 5 \ + "https://github.com/ipfs/kubo/releases/download/${KUBO_VER}/kubo_${KUBO_VER}_linux-amd64.tar.gz" -o /tmp/kubo.tar.gz && \ tar vzx -f /tmp/kubo.tar.gz -C /usr/local/bin/ kubo/ipfs --strip-components=1 ARG ASDF_VERSION=v0.18.1 -RUN curl -sS --retry 5 \ +RUN curl -fsSL --retry 5 \ "https://github.com/asdf-vm/asdf/releases/download/${ASDF_VERSION}/asdf-${ASDF_VERSION}-linux-amd64.tar.gz" \ | tar -xz -C /usr/local/bin asdf RUN adduser --shell /bin/bash --home /asdf --disabled-password --gecos asdf asdf --uid $USER_UID -ENV ASDF_DATA_DIR=/asdf/.asdf -ENV PATH="${PATH}:/asdf/.asdf/shims" +ENV ASDF_DATA_DIR=/asdf/.asdf \ + PATH="${PATH}:/asdf/.asdf/shims" USER asdf WORKDIR /asdf -RUN asdf plugin add golang -RUN asdf plugin add nodejs -ADD .tool-versions . +RUN asdf plugin add golang \ + && asdf plugin add nodejs +COPY .tool-versions . RUN asdf install @@ -37,7 +46,8 @@ RUN go install github.com/guseggert/glibc-check/cmd/glibc-check@v0.0.0-202111301 ARG CACHEBUST=undefined USER root -RUN apt-get update && apt-get -y upgrade +RUN apt-get update && apt-get -y upgrade \ + && rm -rf /var/lib/apt/lists/* USER asdf WORKDIR /build diff --git a/build-go.sh b/build-go.sh index a0ec8574..527361df 100755 --- a/build-go.sh +++ b/build-go.sh @@ -115,6 +115,11 @@ function goBuild() { return 1 fi + # Enforce the glibc ABI floor: linux/amd64 tarballs published to + # dist.ipfs.tech must not require glibc symbols newer than GLIBC_2.31, + # so they keep running on hosts as old as CentOS 7-era glibc (~2.14). + # The Dockerfile is pinned to ubuntu:20.04 (glibc 2.31) to bound this; + # raising the base image without raising this assertion breaks users. if [ -x "$(which glibc-check)" ] && [ "$GOOS" == "linux" ] && [ "$GOARCH" == "amd64" ]; then echo "GLIBC versions:" glibc-check list-versions "$output" From 8efe0ca4f0bf4bcebc9ee44acd5e9c89581eedaa Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 14 Apr 2026 14:18:26 +0200 Subject: [PATCH 5/6] ci: dhtclient + Provide.Strategy=pinned+mfs --- .github/actions/setup-ipfs/action.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/actions/setup-ipfs/action.yml b/.github/actions/setup-ipfs/action.yml index fafd4fe9..1bedc048 100644 --- a/.github/actions/setup-ipfs/action.yml +++ b/.github/actions/setup-ipfs/action.yml @@ -16,9 +16,10 @@ runs: # fix resolv - DNS provided by Github is unreliable for DNSLik/dnsaddr sudo sed -i -e 's/nameserver 127.0.0.*/nameserver 1.1.1.1/g' /etc/resolv.conf ipfs init --profile server,randomports,unixfs-v1-2025 - ipfs config Routing.Type autoclient - # only provide roots, making it easier for ipfs-cluster to pick them up - ipfs config Provide.Strategy roots + ipfs config Routing.Type dhtclient + # advertise pinned content and MFS so ipfs-cluster peers can fetch + # the full DAG without waiting on bitswap-only discovery + ipfs config Provide.Strategy pinned+mfs shell: bash - uses: ipfs/start-ipfs-daemon-action@v1 with: From 97b1bc5acd83dda02bcc7b44bd244ada13d1b7b7 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 14 Apr 2026 14:59:32 +0200 Subject: [PATCH 6/6] ci: harden publish pipeline - github-preview-link.sh: interpolate GIT_REVISION in status URL - nightly.yml: replace deprecated ::set-output with $GITHUB_OUTPUT - setup-ipfs, pin-to-cluster: wait on background swarm dials - pin-to-cluster: trap-based unpin of additions, 2h expiry, drop --local --- .github/actions/setup-ipfs/action.yml | 2 ++ .github/workflows/nightly.yml | 4 +-- scripts/ci/github-preview-link.sh | 2 +- scripts/ci/pin-to-cluster.sh | 37 +++++++++++++++++++-------- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/.github/actions/setup-ipfs/action.yml b/.github/actions/setup-ipfs/action.yml index 1bedc048..985705ba 100644 --- a/.github/actions/setup-ipfs/action.yml +++ b/.github/actions/setup-ipfs/action.yml @@ -34,6 +34,8 @@ runs: ipfs swarm peering add $maddr ipfs swarm connect $maddr || true & done + # best-effort: wait for background dials, never fail the step + wait || true shell: bash - name: List swarm peers run: ipfs swarm peers diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 4d5f3519..e42966a5 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -26,7 +26,7 @@ jobs: - uses: actions/checkout@v5 - run: awk 'NR == FNR {f1[$0] = 1; next}; !($0 in f1)' ignored-during-nightly <(ls ./dists -1) > nightlies-to-run - id: set-matrix - run: echo "::set-output name=matrix::$(jq -nc '$ARGS.positional' --args $(cat nightlies-to-run))" + run: echo "matrix=$(jq -nc '$ARGS.positional' --args $(cat nightlies-to-run))" >> $GITHUB_OUTPUT outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} @@ -80,7 +80,7 @@ jobs: run: git status && ls -Rhl ./releases - name: Read CID of updated DAG id: cid-reader - run: echo "::set-output name=CID::$(tail -1 ./versions)" + run: echo "CID=$(tail -1 ./versions)" >> $GITHUB_OUTPUT - name: Pin new website to ipfs-websites.collab.ipfscluster.io run: ./scripts/ci/pin-to-cluster.sh env: diff --git a/scripts/ci/github-preview-link.sh b/scripts/ci/github-preview-link.sh index 18d2c348..769725d6 100755 --- a/scripts/ci/github-preview-link.sh +++ b/scripts/ci/github-preview-link.sh @@ -10,5 +10,5 @@ API_PARAMS=$(jq --monochrome-output --null-input \ '{ state: $state, target_url: $target_url, description: $description, context: $context }' ) curl --output /dev/null --silent --show-error \ -X POST -H "Authorization: Bearer $GITHUB_TOKEN" -H 'Content-Type: application/json' \ - --data "$API_PARAMS" 'https://api.github.com/repos/ipfs/distributions/statuses/${GIT_REVISION}' + --data "$API_PARAMS" "https://api.github.com/repos/ipfs/distributions/statuses/${GIT_REVISION}" echo "Pinned to IPFS - $PREVIEW_URL" diff --git a/scripts/ci/pin-to-cluster.sh b/scripts/ci/pin-to-cluster.sh index 6d90df7f..33fc918a 100755 --- a/scripts/ci/pin-to-cluster.sh +++ b/scripts/ci/pin-to-cluster.sh @@ -24,16 +24,37 @@ count_pinned() { jq '[.peer_map[].status] | map(select(. == "pinned")) | length' || echo 0 } -# Upload additions .car to pre-seed new blocks before the full root pin. -# This goes directly to the cluster API (--local), no peering needed. +# Remove the temporary additions pin on any exit path (success or failure) +# so we never leak it on the cluster when pin add or status polling fails. ADDITIONS_CID="" +cleanup_additions() { + local exit_code=$? + if [[ -n "$ADDITIONS_CID" ]]; then + echo "::group::unpin additions" + echo "Removing temporary additions pin '$ADDITIONS_CID'" + # best-effort: never let cleanup mask the script's real exit code, + # the additions pin will expire in 2h regardless + cluster_ctl pin rm "$ADDITIONS_CID" 2>/dev/null \ + || echo "warn: failed to remove additions pin (will expire in 2h)" + echo "::endgroup::" + fi + exit "$exit_code" +} +trap cleanup_additions EXIT + +# Upload additions .car to pre-seed new blocks before the full root pin. +# Note: we do NOT pass --local. Without it, cluster fans the blocks out to +# the peers allocated for this pin during the add itself, so the new blocks +# land on the eventual pin holders directly instead of relying on bitswap +# replication from a single entry peer afterwards. if [[ -n "${ADDITIONS_CAR:-}" ]]; then car_file=$(ls $ADDITIONS_CAR 2>/dev/null | head -1) || true if [[ -n "$car_file" ]]; then echo "::group::upload additions .car" echo "Uploading $car_file as pin '${ADDITIONS_PIN_NAME}'" - add_output=$(cluster_ctl add --format=car --local \ + add_output=$(cluster_ctl add --format=car \ --name "$ADDITIONS_PIN_NAME" \ + --expire-in 2h \ "$car_file") echo "$add_output" ADDITIONS_CID=$(echo "$add_output" | jq -rs '.[-1].cid' 2>/dev/null) || true @@ -52,6 +73,8 @@ for maddr in $(jq -r '.ipfs.addresses[]?' cluster-peers-ls); do ipfs swarm peering add "$maddr" ipfs swarm connect "$maddr" || true & done +# best-effort: wait for background dials, never fail the script +wait || true echo "::endgroup::" # Submit pin request (returns immediately, does not wait for completion). @@ -75,10 +98,4 @@ while true; do done echo "::endgroup::" -# Unpin the temporary additions pin (full root CID subsumes it). -if [[ -n "$ADDITIONS_CID" ]]; then - echo "::group::unpin additions" - echo "Removing temporary additions pin '$ADDITIONS_CID'" - cluster_ctl pin rm "$ADDITIONS_CID" 2>/dev/null || true - echo "::endgroup::" -fi +# Temporary additions pin is removed by the EXIT trap registered above.