From d7e0104a72e937757f1082a44bf240969c4e41eb Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Wed, 22 Jul 2026 16:45:59 +0800 Subject: [PATCH] ci: unpack bubblewrap without package transaction --- .github/workflows/ci.yml | 61 +++++++------------------------- scripts/prepare-ci-bubblewrap.sh | 30 ++++++++++++++++ 2 files changed, 43 insertions(+), 48 deletions(-) create mode 100755 scripts/prepare-ci-bubblewrap.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 80a158210e..9aa20e929b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -277,23 +277,16 @@ jobs: # re-executing their bash calls under a real runner. ubuntu-latest has # no bubblewrap preinstalled and no built Landlock launcher, so without # this the confined executions fail closed (SANDBOX_UNAVAILABLE). The - # install retries after refreshing stale indexes and applies the Ubuntu - # 24.04 AppArmor userns knob. Bubblewrap preparation is independent of - # dependency installation and the build, so it runs beside both. - - name: Install and prepare built snapshot runtime and bubblewrap + # pinned Ubuntu payload is verified and extracted into the ephemeral + # runner instead of paying for a system package transaction. Bubblewrap + # preparation is independent of dependency installation and the build, + # so it runs beside both. + - name: Prepare built snapshot runtime and bubblewrap if: startsWith(matrix.lane, 'snapshot-') run: | pnpm install --frozen-lockfile & install_pid=$! - ( - if ! sudo apt-get install -yq --no-install-recommends bubblewrap; then - echo "initial bubblewrap install failed; refreshing APT indexes and retrying" - sudo apt-get update -q - sudo apt-get install -yq --no-install-recommends bubblewrap - fi - sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 \ - || echo "apparmor userns knob absent — the functional probe decides" - ) & + bash scripts/prepare-ci-bubblewrap.sh & sandbox_pid=$! install_status=0 wait "$install_pid" || install_status=$? @@ -356,19 +349,11 @@ jobs: restore-keys: | ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-eslint-full- - - name: Install and prepare bubblewrap + - name: Install dependencies and prepare bubblewrap run: | pnpm install --frozen-lockfile & install_pid=$! - ( - if ! sudo apt-get install -yq --no-install-recommends bubblewrap; then - echo "initial bubblewrap install failed; refreshing APT indexes and retrying" - sudo apt-get update -q - sudo apt-get install -yq --no-install-recommends bubblewrap - fi - sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 \ - || echo "apparmor userns knob absent — the functional probe decides" - ) & + bash scripts/prepare-ci-bubblewrap.sh & sandbox_pid=$! install_status=0 wait "$install_pid" || install_status=$? @@ -473,20 +458,12 @@ jobs: if: matrix.primary_cpu != true run: pnpm install --frozen-lockfile - - name: Install and prepare bubblewrap + - name: Install dependencies and prepare bubblewrap if: matrix.primary_cpu == true run: | pnpm install --frozen-lockfile & install_pid=$! - ( - if ! sudo apt-get install -yq --no-install-recommends bubblewrap; then - echo "initial bubblewrap install failed; refreshing APT indexes and retrying" - sudo apt-get update -q - sudo apt-get install -yq --no-install-recommends bubblewrap - fi - sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 \ - || echo "apparmor userns knob absent — the functional probe decides" - ) & + bash scripts/prepare-ci-bubblewrap.sh & sandbox_pid=$! install_status=0 wait "$install_pid" || install_status=$? @@ -731,12 +708,8 @@ jobs: - name: Install (immutable) run: pnpm install --frozen-lockfile - - name: Install bubblewrap (unrestrict userns) - run: | - sudo apt-get update -q - sudo apt-get install -yq --no-install-recommends bubblewrap - sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 \ - || echo "apparmor userns knob absent — the functional probe decides" + - name: Prepare bubblewrap (unrestrict userns) + run: bash scripts/prepare-ci-bubblewrap.sh - name: Run complete unsharded primary Node CI serially env: @@ -1016,15 +989,7 @@ jobs: run: | pnpm install --frozen-lockfile & install_pid=$! - ( - if ! sudo apt-get install -yq --no-install-recommends bubblewrap; then - echo "initial bubblewrap install failed; refreshing APT indexes and retrying" - sudo apt-get update -q - sudo apt-get install -yq --no-install-recommends bubblewrap - fi - sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 \ - || echo "apparmor userns knob absent — the functional probe decides" - ) & + bash scripts/prepare-ci-bubblewrap.sh & sandbox_pid=$! install_status=0 wait "$install_pid" || install_status=$? diff --git a/scripts/prepare-ci-bubblewrap.sh b/scripts/prepare-ci-bubblewrap.sh new file mode 100755 index 0000000000..e42ee8cd2c --- /dev/null +++ b/scripts/prepare-ci-bubblewrap.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Ubuntu's package transaction scans the hosted image's full dpkg database and +# runs post-install hooks. CI needs only the signed-archive payload, so pin and +# verify that payload before extracting it into the ephemeral runner directory. +readonly BUBBLEWRAP_VERSION='0.9.0-1ubuntu0.1' +readonly BUBBLEWRAP_SHA256='1b506492bd9c7fd0cdb4f02ac822f1d3e336b0aead5113c1239baf8db5db562a' +readonly BUBBLEWRAP_URL="https://archive.ubuntu.com/ubuntu/pool/main/b/bubblewrap/bubblewrap_${BUBBLEWRAP_VERSION}_amd64.deb" + +: "${RUNNER_TEMP:?prepare-ci-bubblewrap requires RUNNER_TEMP}" +: "${GITHUB_PATH:?prepare-ci-bubblewrap requires GITHUB_PATH}" + +if [[ "$(uname -s)" != 'Linux' || "$(uname -m)" != 'x86_64' ]]; then + echo 'prepare-ci-bubblewrap supports only Linux x86_64 hosted runners' >&2 + exit 1 +fi + +archive="${RUNNER_TEMP}/bubblewrap_${BUBBLEWRAP_VERSION}_amd64.deb" +root="${RUNNER_TEMP}/dsh-bubblewrap" + +curl --fail --silent --show-error --location --retry 3 --output "$archive" "$BUBBLEWRAP_URL" +printf '%s %s\n' "$BUBBLEWRAP_SHA256" "$archive" | sha256sum --check --status +mkdir -p "$root" +dpkg-deb --extract "$archive" "$root" +printf '%s\n' "$root/usr/bin" >> "$GITHUB_PATH" + +sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 \ + || echo 'apparmor userns knob absent — the functional probe decides' +"$root/usr/bin/bwrap" --version