From f0c22d87637fcfcad8b2393317f64c1affcc3f22 Mon Sep 17 00:00:00 2001 From: garethgeorge Date: Wed, 8 Jul 2026 23:11:18 -0700 Subject: [PATCH] chore: fix github actions runner for e2e tests covering rclone --- .github/workflows/test.yml | 23 +++++++++++++++++++++++ webui/e2e/specs/rclone-repo.spec.ts | 22 ++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 215592e9..047710f5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -73,6 +73,8 @@ jobs: test-e2e-web: runs-on: ubuntu-latest + env: + RCLONE_VERSION: v1.69.1 steps: - uses: actions/checkout@v4 @@ -121,6 +123,27 @@ jobs: - name: Install playwright chromium run: cd webui && pnpm exec playwright install --with-deps chromium + # restic's rclone backend spawns the `rclone` binary; the rclone-repo + # e2e spec skips itself if rclone is absent, so provision it for coverage. + - name: Cache rclone + id: cache-rclone + uses: actions/cache@v4 + with: + path: webui/e2e/.cache/rclone-bin + key: rclone-${{ env.RCLONE_VERSION }} + + - name: Install rclone + run: | + dest="$PWD/webui/e2e/.cache/rclone-bin" + if [ ! -x "$dest/rclone" ]; then + mkdir -p "$dest" + zip="rclone-${RCLONE_VERSION}-linux-amd64" + curl -fsSL "https://downloads.rclone.org/${RCLONE_VERSION}/${zip}.zip" -o /tmp/rclone.zip + unzip -j -o /tmp/rclone.zip "${zip}/rclone" -d "$dest" + chmod +x "$dest/rclone" + fi + echo "$dest" >> "$GITHUB_PATH" + - name: Run e2e tests run: | cd webui diff --git a/webui/e2e/specs/rclone-repo.spec.ts b/webui/e2e/specs/rclone-repo.spec.ts index 114bcb8e..324b789f 100644 --- a/webui/e2e/specs/rclone-repo.spec.ts +++ b/webui/e2e/specs/rclone-repo.spec.ts @@ -1,4 +1,5 @@ import * as fs from 'node:fs/promises'; +import { constants as fsConstants } from 'node:fs'; import * as path from 'node:path'; import { create } from '@bufbuild/protobuf'; import { test, expect } from '../harness/fixtures'; @@ -59,11 +60,32 @@ async function runBackupViaApi( throw new Error(`backup for plan ${planId} did not succeed within ${timeoutMs}ms`); } +/** + * True if an `rclone` executable is resolvable on PATH. restic's rclone backend + * shells out to it, so without it this whole spec cannot exercise anything; we + * skip rather than fail on hosts that don't provide it (CI installs it; the nix + * dev shell provides it). + */ +async function rcloneAvailable(): Promise { + const dirs = (process.env.PATH ?? '').split(path.delimiter).filter(Boolean); + for (const dir of dirs) { + try { + await fs.access(path.join(dir, 'rclone'), fsConstants.X_OK); + return true; + } catch { + // keep looking + } + } + return false; +} + test.describe('rclone-backed repo', () => { test('adds an rclone repo through the UI, backs up, and is usable end-to-end', async ({ page, backrest, }) => { + test.skip(!(await rcloneAvailable()), 'rclone not found on PATH'); + // rclone init + test + backup each spawn a `rclone serve restic` process; // give the whole flow a generous budget on a possibly-loaded machine. test.setTimeout(420_000);