From 2e2c7ebdc14960614f30717f8ba8a64c9b093115 Mon Sep 17 00:00:00 2001 From: qnbs <155236708+qnbs@users.noreply.github.com> Date: Wed, 19 Aug 2026 18:09:12 +0200 Subject: [PATCH 1/2] fix(ci): apt-archive cache restore was silently failing on every run Real CI evidence from main's post-#402-merge Build job: the cache restore genuinely found the key ("Cache hit for: apt-playwright-chromium-deps-v1") but tar extraction then failed with "Permission denied" on every single .deb file, immediately followed by "Cache not found for input keys" -- actions/cache's restore step runs as the unprivileged runner user, but /var/cache/apt/archives is root-owned by default. This means every apt-cache added in PR #398 across all 7 sites (6 in ci.yml, 1 in cef-learning-harness.yml) has been silently degrading to a full cache miss on every single run since it was introduced, undermining the whole point of that fix and very plausibly contributing to several of today's apt-mirror-timeout failures that were attributed purely to external throughput. Fix: chmod the archive directory world-writable (sudo, matching how the actual apt-get install steps already need sudo) immediately before each cache-restore step, so tar's unprivileged extraction can actually write into it. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/cef-learning-harness.yml | 7 ++++ .github/workflows/ci.yml | 42 ++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/.github/workflows/cef-learning-harness.yml b/.github/workflows/cef-learning-harness.yml index e3f29cd49..a962f4dcd 100644 --- a/.github/workflows/cef-learning-harness.yml +++ b/.github/workflows/cef-learning-harness.yml @@ -144,6 +144,13 @@ jobs: with: workspaces: apps/desktop-cef/rust-core -> target + # QNBS-v3: /var/cache/apt/archives is root-owned by default — actions/cache's restore + # (tar extraction) runs as the unprivileged runner user and fails with "Permission denied" + # on every file without this, silently degrading to a cache miss every time (real CI + # evidence: "Cache hit" followed immediately by "Cache not found" from the failed tar). + - name: Make apt archive cache dir writable by the runner user + run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chmod -R 777 /var/cache/apt/archives + - name: Cache apt packages (CEF host + Wayland build deps) uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4b25da7c..b4b5cc0fb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -237,6 +237,13 @@ jobs: - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 with: workspaces: src-tauri -> target + # QNBS-v3: /var/cache/apt/archives is root-owned by default — actions/cache's restore + # (tar extraction) runs as the unprivileged runner user and fails with "Permission denied" + # on every file without this, silently degrading to a cache miss every time (real CI + # evidence: "Cache hit" followed immediately by "Cache not found" from the failed tar). + - name: Make apt archive cache dir writable by the runner user + run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chmod -R 777 /var/cache/apt/archives + - name: Cache apt packages (Tauri Linux build deps) uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: @@ -291,6 +298,13 @@ jobs: # key across every `playwright install --with-deps` call site in this workflow, since they all # install the same system packages on the same runner image. actions/cache/save on a fresh # write here also warms the cache for e2e/e2e-deep/storybook/vrt below. + # QNBS-v3: /var/cache/apt/archives is root-owned by default — actions/cache's restore + # (tar extraction) runs as the unprivileged runner user and fails with "Permission denied" + # on every file without this, silently degrading to a cache miss every time (real CI + # evidence: "Cache hit" followed immediately by "Cache not found" from the failed tar). + - name: Make apt archive cache dir writable by the runner user + run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chmod -R 777 /var/cache/apt/archives + - name: Cache apt packages (Playwright system deps) uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: @@ -432,6 +446,13 @@ jobs: path: ~/.cache/ms-playwright key: playwright-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} + # QNBS-v3: /var/cache/apt/archives is root-owned by default — actions/cache's restore + # (tar extraction) runs as the unprivileged runner user and fails with "Permission denied" + # on every file without this, silently degrading to a cache miss every time (real CI + # evidence: "Cache hit" followed immediately by "Cache not found" from the failed tar). + - name: Make apt archive cache dir writable by the runner user + run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chmod -R 777 /var/cache/apt/archives + - name: Cache apt packages (Playwright system deps) uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: @@ -492,6 +513,13 @@ jobs: path: ~/.cache/ms-playwright key: playwright-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} + # QNBS-v3: /var/cache/apt/archives is root-owned by default — actions/cache's restore + # (tar extraction) runs as the unprivileged runner user and fails with "Permission denied" + # on every file without this, silently degrading to a cache miss every time (real CI + # evidence: "Cache hit" followed immediately by "Cache not found" from the failed tar). + - name: Make apt archive cache dir writable by the runner user + run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chmod -R 777 /var/cache/apt/archives + - name: Cache apt packages (Playwright system deps) uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: @@ -589,6 +617,13 @@ jobs: - name: Build Storybook run: pnpm exec storybook build --output-dir storybook-static + # QNBS-v3: /var/cache/apt/archives is root-owned by default — actions/cache's restore + # (tar extraction) runs as the unprivileged runner user and fails with "Permission denied" + # on every file without this, silently degrading to a cache miss every time (real CI + # evidence: "Cache hit" followed immediately by "Cache not found" from the failed tar). + - name: Make apt archive cache dir writable by the runner user + run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chmod -R 777 /var/cache/apt/archives + - name: Cache apt packages (Playwright system deps) uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: @@ -658,6 +693,13 @@ jobs: name: dist path: dist/ + # QNBS-v3: /var/cache/apt/archives is root-owned by default — actions/cache's restore + # (tar extraction) runs as the unprivileged runner user and fails with "Permission denied" + # on every file without this, silently degrading to a cache miss every time (real CI + # evidence: "Cache hit" followed immediately by "Cache not found" from the failed tar). + - name: Make apt archive cache dir writable by the runner user + run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chmod -R 777 /var/cache/apt/archives + - name: Cache apt packages (Playwright system deps) uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: From 142c086c047abcc87e72208f0deb63cd23719f75 Mon Sep 17 00:00:00 2001 From: qnbs <155236708+qnbs@users.noreply.github.com> Date: Wed, 19 Aug 2026 18:14:25 +0200 Subject: [PATCH 2/2] fix(ci): tighten apt-cache dir fix from chmod 777 to chown+755 (least privilege) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Real, valid finding from both Amazon Q and Graphite, independently, on all 7 sites: chmod 777 (world-writable) is unnecessarily permissive — it lets any process/user on the runner tamper with cached .deb packages, not just the runner user that actually needs write access. chown runner:runner + chmod 755 grants the same functional access (restore's tar extraction and the save post-hook both run as the runner user; the actual apt-get install steps run as root via sudo, unaffected by ownership since root bypasses permission checks) with tighter scope, matching least-privilege practice even on an ephemeral CI runner. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/cef-learning-harness.yml | 2 +- .github/workflows/ci.yml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/cef-learning-harness.yml b/.github/workflows/cef-learning-harness.yml index a962f4dcd..da6027590 100644 --- a/.github/workflows/cef-learning-harness.yml +++ b/.github/workflows/cef-learning-harness.yml @@ -149,7 +149,7 @@ jobs: # on every file without this, silently degrading to a cache miss every time (real CI # evidence: "Cache hit" followed immediately by "Cache not found" from the failed tar). - name: Make apt archive cache dir writable by the runner user - run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chmod -R 777 /var/cache/apt/archives + run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chown -R runner:runner /var/cache/apt/archives && sudo chmod -R 755 /var/cache/apt/archives - name: Cache apt packages (CEF host + Wayland build deps) uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b4b5cc0fb..8b683b1a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -242,7 +242,7 @@ jobs: # on every file without this, silently degrading to a cache miss every time (real CI # evidence: "Cache hit" followed immediately by "Cache not found" from the failed tar). - name: Make apt archive cache dir writable by the runner user - run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chmod -R 777 /var/cache/apt/archives + run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chown -R runner:runner /var/cache/apt/archives && sudo chmod -R 755 /var/cache/apt/archives - name: Cache apt packages (Tauri Linux build deps) uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 @@ -303,7 +303,7 @@ jobs: # on every file without this, silently degrading to a cache miss every time (real CI # evidence: "Cache hit" followed immediately by "Cache not found" from the failed tar). - name: Make apt archive cache dir writable by the runner user - run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chmod -R 777 /var/cache/apt/archives + run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chown -R runner:runner /var/cache/apt/archives && sudo chmod -R 755 /var/cache/apt/archives - name: Cache apt packages (Playwright system deps) uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 @@ -451,7 +451,7 @@ jobs: # on every file without this, silently degrading to a cache miss every time (real CI # evidence: "Cache hit" followed immediately by "Cache not found" from the failed tar). - name: Make apt archive cache dir writable by the runner user - run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chmod -R 777 /var/cache/apt/archives + run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chown -R runner:runner /var/cache/apt/archives && sudo chmod -R 755 /var/cache/apt/archives - name: Cache apt packages (Playwright system deps) uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 @@ -518,7 +518,7 @@ jobs: # on every file without this, silently degrading to a cache miss every time (real CI # evidence: "Cache hit" followed immediately by "Cache not found" from the failed tar). - name: Make apt archive cache dir writable by the runner user - run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chmod -R 777 /var/cache/apt/archives + run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chown -R runner:runner /var/cache/apt/archives && sudo chmod -R 755 /var/cache/apt/archives - name: Cache apt packages (Playwright system deps) uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 @@ -622,7 +622,7 @@ jobs: # on every file without this, silently degrading to a cache miss every time (real CI # evidence: "Cache hit" followed immediately by "Cache not found" from the failed tar). - name: Make apt archive cache dir writable by the runner user - run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chmod -R 777 /var/cache/apt/archives + run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chown -R runner:runner /var/cache/apt/archives && sudo chmod -R 755 /var/cache/apt/archives - name: Cache apt packages (Playwright system deps) uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 @@ -698,7 +698,7 @@ jobs: # on every file without this, silently degrading to a cache miss every time (real CI # evidence: "Cache hit" followed immediately by "Cache not found" from the failed tar). - name: Make apt archive cache dir writable by the runner user - run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chmod -R 777 /var/cache/apt/archives + run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chown -R runner:runner /var/cache/apt/archives && sudo chmod -R 755 /var/cache/apt/archives - name: Cache apt packages (Playwright system deps) uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0