diff --git a/.github/workflows/android-e2e.yml b/.github/workflows/android-e2e.yml index 32725339..b1b6df22 100644 --- a/.github/workflows/android-e2e.yml +++ b/.github/workflows/android-e2e.yml @@ -107,7 +107,7 @@ jobs: - name: create AVD snapshot for caching if: steps.avd-cache.outputs.cache-hit != 'true' - uses: reactivecircus/android-emulator-runner@v2 + uses: reactivecircus/android-emulator-runner@e89f39f1abbbd05b1113a29cf4db69e7540cae5a # v2 with: api-level: 34 arch: x86_64 @@ -118,7 +118,7 @@ jobs: script: echo "AVD snapshot created" - name: run Android e2e lane - uses: reactivecircus/android-emulator-runner@v2 + uses: reactivecircus/android-emulator-runner@e89f39f1abbbd05b1113a29cf4db69e7540cae5a # v2 with: api-level: 34 arch: x86_64 diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 25ee4805..d00a1ba2 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -77,13 +77,13 @@ jobs: run: | pnpm install --frozen-lockfile --prefer-offline && pnpm setup-vendors - - name: install LuaJIT (for koplugin lint) - run: sudo apt-get update && sudo apt-get install -y luajit - - name: run format check run: | pnpm format:check || (pnpm format && git diff && exit 1) + # pnpm lint here is web-only (tsgo + biome). The koplugin syntax check + # (lint:lua) runs in the test_extensions job, which installs LuaJIT only + # when the koplugin sources changed. - name: run lint working-directory: apps/readest-app run: | @@ -123,8 +123,16 @@ jobs: path: apps/readest-app/playwright-report/ retention-days: 7 + # The jsdom unit suite is the slowest part of the PR checks, so it is split + # across two parallel shards (vitest --shard). The browser tests need + # Playwright and run only on shard 1; koplugin + browser-extension tests + # moved to the test_extensions job. test_web_app: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [1, 2] steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 with: @@ -144,16 +152,91 @@ jobs: run: | pnpm install --frozen-lockfile --prefer-offline && pnpm setup-vendors + # Playwright is only needed by the browser tests, which run on shard 1. + - name: cache playwright browsers + if: matrix.shard == 1 + id: playwright-cache + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 + with: + path: ~/.cache/ms-playwright + key: playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} + + - name: install playwright browsers + if: matrix.shard == 1 + working-directory: apps/readest-app + run: | + if [ "${{ steps.playwright-cache.outputs.cache-hit }}" = 'true' ]; then + npx playwright install-deps chromium + else + npx playwright install --with-deps chromium + fi + + - name: run web unit tests (shard ${{ matrix.shard }}/2) + working-directory: apps/readest-app + run: pnpm test:pr:web:unit --shard=${{ matrix.shard }}/2 + + - name: run web browser tests + if: matrix.shard == 1 + working-directory: apps/readest-app + run: pnpm test:browser + + # Browser-extension tests + build always run. The koplugin lint + Lua tests + # (and the ~45s LuaJIT/busted install they need) only run when the koplugin + # sources changed, so most PRs skip that cost entirely. + test_extensions: + runs-on: ubuntu-latest + # pull-requests: read lets dorny/paths-filter list a PR's changed files + # via the REST API (the top-level grant is contents: read only). + permissions: + contents: read + pull-requests: read + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + with: + submodules: 'true' + + - name: detect koplugin changes + id: changes + uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4 + with: + filters: | + koplugin: + - 'apps/readest.koplugin/**' + + - name: setup pnpm + uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6 + + - name: setup node + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: 24 + cache: pnpm + + - name: install Dependencies + working-directory: apps/readest-app + run: | + pnpm install --frozen-lockfile --prefer-offline && pnpm setup-vendors + + - name: run extension tests + working-directory: apps/readest-app + run: pnpm test:extension + + - name: build browser extension + working-directory: apps/readest-app + run: pnpm build-browser-ext + - name: cache apt packages + if: steps.changes.outputs.koplugin == 'true' uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 with: path: /var/cache/apt/archives - key: apt-test-web-${{ runner.os }} + key: apt-test-koplugin-${{ runner.os }} - - name: install LuaJIT + busted (for koplugin tests) + - name: install LuaJIT + busted (for koplugin lint + tests) + if: steps.changes.outputs.koplugin == 'true' run: | sudo apt-get update - # luajit — pnpm test:lua + # luajit — pnpm lint:lua + pnpm test:lua # luarocks/libsqlite3-dev — required to build lsqlite3complete sudo apt-get install -y luajit luarocks libsqlite3-dev # Install busted + the SQLite binding the LibraryStore specs use, @@ -163,27 +246,13 @@ jobs: sudo luarocks --lua-version=5.1 install busted sudo luarocks --lua-version=5.1 install lsqlite3complete - - name: cache playwright browsers - id: playwright-cache - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 - with: - path: ~/.cache/ms-playwright - key: playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} - - - name: install playwright browsers + - name: lint koplugin + if: steps.changes.outputs.koplugin == 'true' working-directory: apps/readest-app - run: | - if [ "${{ steps.playwright-cache.outputs.cache-hit }}" = 'true' ]; then - npx playwright install-deps chromium - else - npx playwright install --with-deps chromium - fi - - - name: run web tests - working-directory: apps/readest-app - run: pnpm test:pr:web + run: pnpm lint:lua - name: run koplugin tests + if: steps.changes.outputs.koplugin == 'true' working-directory: apps/readest-app run: pnpm test:lua diff --git a/apps/readest-app/.claude/rules/verification.md b/apps/readest-app/.claude/rules/verification.md index 90f1ba49..cd74bd92 100644 --- a/apps/readest-app/.claude/rules/verification.md +++ b/apps/readest-app/.claude/rules/verification.md @@ -3,8 +3,8 @@ Before marking work complete, all applicable checks must pass: 1. `pnpm test` — unit tests (vitest) -2. `pnpm lint` — Biome + tsgo (also runs `pnpm lint:lua` if luajit is installed) -3. `pnpm test:lua` — busted unit tests for `apps/readest.koplugin/spec/` (only when koplugin Lua files changed; soft-skips when busted/luajit not installed) +2. `pnpm lint` — Biome + tsgo (web only) +3. `pnpm lint:lua` + `pnpm test:lua` — koplugin LuaJIT syntax check + busted unit tests for `apps/readest.koplugin/spec/` (only when koplugin Lua files changed; soft-skip when luajit/busted not installed) 4. `pnpm fmt:check` — Rust format check (only when `src-tauri/` files changed) 5. `pnpm clippy:check` — Rust lint (only when `src-tauri/` files changed) 6. `pnpm test:rust` — Rust unit tests (`cargo test -p Readest --lib`; only when `src-tauri/` files changed); also run in the CI `rust_lint` job diff --git a/apps/readest-app/package.json b/apps/readest-app/package.json index db38425d..bd8d2d91 100644 --- a/apps/readest-app/package.json +++ b/apps/readest-app/package.json @@ -19,7 +19,7 @@ "dev-ios": "tauri ios build -- --features devtools && ideviceinstaller install src-tauri/gen/apple/build/arm64/Readest.ipa", "i18n:extract": "i18next-scanner --config i18next-scanner.config.cjs", "i18n:extract:ext": "pnpm --filter @readest/send-to-readest-extension i18n:extract", - "lint": "tsgo --noEmit && biome lint . && pnpm lint:lua", + "lint": "tsgo --noEmit && biome lint .", "lint:lua": "node ../readest.koplugin/scripts/lint-koplugin.mjs", "test:lua": "node ../readest.koplugin/scripts/test-koplugin.mjs", "test": "dotenv -e .env -e .env.test.local -- vitest", @@ -28,7 +28,8 @@ "test:browser": "dotenv -e .env -e .env.test.local -- vitest run --config vitest.browser.config.mts", "test:tauri": "bash scripts/test-tauri.sh", "test:android": "bash scripts/test-android.sh", - "test:pr:web": "dotenv -e .env -e .env.test.local -- vitest run --maxWorkers=4 && pnpm test:browser && pnpm test:extension && pnpm build-browser-ext", + "test:pr:web:unit": "dotenv -e .env -e .env.test.local -- vitest run --maxWorkers=4", + "test:pr:web": "pnpm test:pr:web:unit && pnpm test:browser && pnpm test:extension && pnpm build-browser-ext", "test:pr:tauri": "bash scripts/test-tauri.sh", "test:all": "pnpm test -- --watch=false && pnpm test:browser && bash scripts/test-tauri.sh", "bench": "node --experimental-strip-types --no-warnings bench/index.ts",