From 1e2c41d4a7d9cca32ad723e48374441dbc4b158c Mon Sep 17 00:00:00 2001 From: zhom <2717306+zhom@users.noreply.github.com> Date: Fri, 31 Jul 2026 20:45:45 +0400 Subject: [PATCH] chore: linting --- .github/workflows/app-e2e.yml | 81 +++++-------- .github/workflows/issue-compliance.yml | 16 ++- .github/workflows/issue-validation.yml | 72 ++++++++--- .github/workflows/pr-ai-policy.yml | 17 ++- .github/workflows/pr-checks.yml | 1 - .gitignore | 1 + AGENTS.md | 3 +- e2e/README.md | 32 +++-- e2e/app/Cargo.lock | 161 +++++++++++++++++++++---- e2e/app/Cargo.toml | 2 +- e2e/run.mjs | 73 ++++++++--- pnpm-lock.yaml | 45 +++---- 12 files changed, 340 insertions(+), 164 deletions(-) diff --git a/.github/workflows/app-e2e.yml b/.github/workflows/app-e2e.yml index dba9bd0..094dffd 100644 --- a/.github/workflows/app-e2e.yml +++ b/.github/workflows/app-e2e.yml @@ -9,9 +9,6 @@ on: default: true type: boolean secrets: - TAURI_WEBDRIVER_TOKEN: - description: Read-only token for the test-driver repository - required: false WAYFERN_TEST_TOKEN: description: Token used by the full Wayfern integration suite required: false @@ -64,15 +61,6 @@ jobs: - name: Checkout Donut Browser uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - path: donutbrowser - persist-credentials: false - - - name: Checkout test driver - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - repository: ${{ vars.TAURI_WEBDRIVER_REPOSITORY }} - token: ${{ secrets.TAURI_WEBDRIVER_TOKEN || github.token }} - path: tauri-wd persist-credentials: false - name: Install pnpm @@ -83,9 +71,9 @@ jobs: - name: Set up Node.js uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version-file: donutbrowser/.node-version + node-version-file: .node-version cache: pnpm - cache-dependency-path: donutbrowser/pnpm-lock.yaml + cache-dependency-path: pnpm-lock.yaml - name: Install Rust uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master @@ -96,9 +84,14 @@ jobs: uses: swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: workspaces: | - donutbrowser/src-tauri -> target - donutbrowser/e2e/app -> target - tauri-wd -> target + src-tauri -> target + e2e/app -> target + + - name: Cache WebDriver test driver + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: e2e/.driver + key: tauri-wd-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('e2e/app/Cargo.lock') }} - name: Install Tauri dependencies if: runner.os == 'Linux' @@ -107,11 +100,9 @@ jobs: sudo apt-get install -y libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf libxdo-dev xvfb openvpn - name: Install app dependencies - working-directory: donutbrowser run: pnpm install --frozen-lockfile - name: Run isolated smoke suite - working-directory: donutbrowser shell: bash run: | if [[ "${RUNNER_OS}" == "Linux" ]]; then @@ -139,15 +130,6 @@ jobs: - name: Checkout Donut Browser uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - path: donutbrowser - persist-credentials: false - - - name: Checkout test driver - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - repository: ${{ vars.TAURI_WEBDRIVER_REPOSITORY }} - token: ${{ secrets.TAURI_WEBDRIVER_TOKEN || github.token }} - path: tauri-wd persist-credentials: false - name: Install pnpm @@ -158,9 +140,9 @@ jobs: - name: Set up Node.js uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version-file: donutbrowser/.node-version + node-version-file: .node-version cache: pnpm - cache-dependency-path: donutbrowser/pnpm-lock.yaml + cache-dependency-path: pnpm-lock.yaml - name: Install Rust uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master @@ -171,16 +153,19 @@ jobs: uses: swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: workspaces: | - donutbrowser/src-tauri -> target - donutbrowser/e2e/app -> target - tauri-wd -> target + src-tauri -> target + e2e/app -> target + + - name: Cache WebDriver test driver + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: e2e/.driver + key: tauri-wd-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('e2e/app/Cargo.lock') }} - name: Install app dependencies - working-directory: donutbrowser run: pnpm install --frozen-lockfile - name: Run full isolated app suite - working-directory: donutbrowser env: DONUT_E2E_SUITE: ${{ inputs.suite || 'full' }} DONUT_E2E_SKIP_NETWORK_TEST: "1" @@ -206,15 +191,6 @@ jobs: - name: Checkout Donut Browser uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - path: donutbrowser - persist-credentials: false - - - name: Checkout test driver - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - repository: ${{ vars.TAURI_WEBDRIVER_REPOSITORY }} - token: ${{ secrets.TAURI_WEBDRIVER_TOKEN || github.token }} - path: tauri-wd persist-credentials: false - name: Install pnpm @@ -225,9 +201,9 @@ jobs: - name: Set up Node.js uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version-file: donutbrowser/.node-version + node-version-file: .node-version cache: pnpm - cache-dependency-path: donutbrowser/pnpm-lock.yaml + cache-dependency-path: pnpm-lock.yaml - name: Install Rust uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master @@ -238,9 +214,14 @@ jobs: uses: swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: workspaces: | - donutbrowser/src-tauri -> target - donutbrowser/e2e/app -> target - tauri-wd -> target + src-tauri -> target + e2e/app -> target + + - name: Cache WebDriver test driver + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: e2e/.driver + key: tauri-wd-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('e2e/app/Cargo.lock') }} - name: Install Tauri dependencies run: | @@ -248,11 +229,9 @@ jobs: sudo apt-get install -y libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf libxdo-dev xvfb openvpn - name: Install app dependencies - working-directory: donutbrowser run: pnpm install --frozen-lockfile - name: Run isolated real-network suite - working-directory: donutbrowser env: WAYFERN_TEST_TOKEN: ${{ secrets.WAYFERN_TEST_TOKEN }} RESIDENTIAL_PROXY_URL_ONE_SOCKS: ${{ secrets.RESIDENTIAL_PROXY_URL_ONE_SOCKS }} diff --git a/.github/workflows/issue-compliance.yml b/.github/workflows/issue-compliance.yml index 35b1de9..a090c91 100644 --- a/.github/workflows/issue-compliance.yml +++ b/.github/workflows/issue-compliance.yml @@ -84,14 +84,24 @@ jobs: response_format: { type: "json_object" } }') - RESPONSE=$(curl -fsSL https://models.github.ai/inference/chat/completions \ + # Never use curl -f here: a transport or quota error (402 once the repo's + # GitHub Models allowance is spent) must not abort the job. The whole + # step is fail-open, so capture the status and degrade instead. + STATUS=$(curl -sSL -o /tmp/response.json -w '%{http_code}' \ + https://models.github.ai/inference/chat/completions \ -H "Authorization: Bearer $GH_MODELS_TOKEN" \ -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2026-03-10" \ -H "Content-Type: application/json" \ - -d "$PAYLOAD") + -d "$PAYLOAD" || echo "000") - jq -r '.choices[0].message.content // empty' <<< "$RESPONSE" > /tmp/raw.txt + if [ "$STATUS" != "200" ]; then + echo "::warning::GitHub Models returned HTTP $STATUS; treating as compliant" + echo '{"is_compliant": true, "non_compliance_reasons": []}' > /tmp/result.json + exit 0 + fi + + jq -r '.choices[0].message.content // empty' /tmp/response.json > /tmp/raw.txt || : > /tmp/raw.txt # Strip accidental markdown fences and parse. On parse failure, fall back # to a compliant result so a flaky model never closes a legitimate issue. diff --git a/.github/workflows/issue-validation.yml b/.github/workflows/issue-validation.yml index 4fec731..38cafcc 100644 --- a/.github/workflows/issue-validation.yml +++ b/.github/workflows/issue-validation.yml @@ -243,14 +243,23 @@ jobs: ] }') - RESPONSE=$(curl -fsSL https://models.github.ai/inference/chat/completions \ + # Never use curl -f here: a transport or quota error (402 once the repo's + # GitHub Models allowance is spent) must not abort the job. Capture the + # status and fall through to the safe classification below. + STATUS=$(curl -sSL -o /tmp/triage-response.json -w '%{http_code}' \ + https://models.github.ai/inference/chat/completions \ -H "Authorization: Bearer $GH_MODELS_TOKEN" \ -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2026-03-10" \ -H "Content-Type: application/json" \ - -d "$PAYLOAD") + -d "$PAYLOAD" || echo "000") - jq -r '.choices[0].message.content // empty' <<< "$RESPONSE" > /tmp/triage-raw.txt + if [ "$STATUS" = "200" ]; then + jq -r '.choices[0].message.content // empty' /tmp/triage-response.json > /tmp/triage-raw.txt || : > /tmp/triage-raw.txt + else + echo "::warning::GitHub Models returned HTTP $STATUS for triage" + : > /tmp/triage-raw.txt + fi # Normalize optional markdown fences before parsing. sed -E 's/^```(json)?$//; s/```$//' /tmp/triage-raw.txt > /tmp/triage.json @@ -375,6 +384,7 @@ jobs: wc -c /tmp/composer-system.txt - name: Stage 2 (compose response) + id: compose env: GH_MODELS_TOKEN: ${{ secrets.GITHUB_TOKEN }} ISSUE_AUTHOR: ${{ github.event.issue.user.login }} @@ -415,21 +425,34 @@ jobs: ] }') - RESPONSE=$(curl -fsSL https://models.github.ai/inference/chat/completions \ + # Same as triage: a quota or transport error must not fail the run. When + # no comment can be composed the remaining steps are skipped instead. + STATUS=$(curl -sSL -o /tmp/compose-response.json -w '%{http_code}' \ + https://models.github.ai/inference/chat/completions \ -H "Authorization: Bearer $GH_MODELS_TOKEN" \ -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2026-03-10" \ -H "Content-Type: application/json" \ - -d "$PAYLOAD") + -d "$PAYLOAD" || echo "000") - jq -r '.choices[0].message.content // empty' <<< "$RESPONSE" > /tmp/ai-comment.txt - - if [ ! -s /tmp/ai-comment.txt ]; then - echo "::error::Composer returned empty response" - exit 1 + if [ "$STATUS" != "200" ]; then + echo "::warning::GitHub Models returned HTTP $STATUS; skipping the triage comment" + echo "has_comment=false" >> "$GITHUB_OUTPUT" + exit 0 fi + jq -r '.choices[0].message.content // empty' /tmp/compose-response.json > /tmp/ai-comment.txt || : > /tmp/ai-comment.txt + + if [ ! -s /tmp/ai-comment.txt ]; then + echo "::warning::Composer returned empty response; skipping the triage comment" + echo "has_comment=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + echo "has_comment=true" >> "$GITHUB_OUTPUT" + - name: Strip forbidden sections (defense in depth) + if: steps.compose.outputs.has_comment == 'true' run: | # LLMs still emit "Possible cause" and friends despite the prompt rules. # Strip any such heading and its block, plus stray `Label:` lines left @@ -452,6 +475,7 @@ jobs: EOF - name: Post comment (no labeling) + if: steps.compose.outputs.has_comment == 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} ISSUE_NUMBER: ${{ github.event.issue.number }} @@ -524,6 +548,7 @@ jobs: mv /tmp/pr-file-context.safe.txt /tmp/pr-file-context.txt - name: Analyze PR with AI + id: analyze env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_MODELS_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -583,21 +608,34 @@ jobs: ] }') - RESPONSE=$(curl -fsSL https://models.github.ai/inference/chat/completions \ + # A quota or transport error must not fail the run; skip the review + # comment instead of red-crossing an otherwise healthy pull request. + STATUS=$(curl -sSL -o /tmp/pr-response.json -w '%{http_code}' \ + https://models.github.ai/inference/chat/completions \ -H "Authorization: Bearer $GH_MODELS_TOKEN" \ -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2026-03-10" \ -H "Content-Type: application/json" \ - -d "$PAYLOAD") + -d "$PAYLOAD" || echo "000") - jq -r '.choices[0].message.content // empty' <<< "$RESPONSE" > /tmp/ai-comment.txt - - if [ ! -s /tmp/ai-comment.txt ]; then - echo "::error::AI response was empty" - exit 1 + if [ "$STATUS" != "200" ]; then + echo "::warning::GitHub Models returned HTTP $STATUS; skipping the review comment" + echo "has_comment=false" >> "$GITHUB_OUTPUT" + exit 0 fi + jq -r '.choices[0].message.content // empty' /tmp/pr-response.json > /tmp/ai-comment.txt || : > /tmp/ai-comment.txt + + if [ ! -s /tmp/ai-comment.txt ]; then + echo "::warning::AI response was empty; skipping the review comment" + echo "has_comment=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + echo "has_comment=true" >> "$GITHUB_OUTPUT" + - name: Post comment + if: steps.analyze.outputs.has_comment == 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_NUMBER: ${{ github.event.pull_request.number }} diff --git a/.github/workflows/pr-ai-policy.yml b/.github/workflows/pr-ai-policy.yml index 0e9be0e..daed47d 100644 --- a/.github/workflows/pr-ai-policy.yml +++ b/.github/workflows/pr-ai-policy.yml @@ -121,14 +121,25 @@ jobs: response_format: { type: "json_object" } }') - RESPONSE=$(curl -fsSL https://models.github.ai/inference/chat/completions \ + # Never use curl -f here: a transport or quota error (402 once the repo's + # GitHub Models allowance is spent) must not abort the job. The model + # half of this check is fail-open, and the deterministic trailer scan + # below still runs regardless. + STATUS=$(curl -sSL -o /tmp/response.json -w '%{http_code}' \ + https://models.github.ai/inference/chat/completions \ -H "Authorization: Bearer $GH_MODELS_TOKEN" \ -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2026-03-10" \ -H "Content-Type: application/json" \ - -d "$PAYLOAD") + -d "$PAYLOAD" || echo "000") - jq -r '.choices[0].message.content // empty' <<< "$RESPONSE" > /tmp/raw.txt + if [ "$STATUS" != "200" ]; then + echo "::warning::GitHub Models returned HTTP $STATUS; treating as compliant" + echo '{"compliant": true, "violations": []}' > /tmp/result.json + exit 0 + fi + + jq -r '.choices[0].message.content // empty' /tmp/response.json > /tmp/raw.txt || : > /tmp/raw.txt # Strip accidental markdown fences and parse. On parse failure, fall # back to compliant so a flaky model never closes a legitimate PR. diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 485f6cb..c058acd 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -46,7 +46,6 @@ jobs: name: Cross-platform App E2E uses: ./.github/workflows/app-e2e.yml secrets: - TAURI_WEBDRIVER_TOKEN: ${{ secrets.TAURI_WEBDRIVER_TOKEN }} WAYFERN_TEST_TOKEN: ${{ secrets.WAYFERN_TEST_TOKEN }} permissions: contents: read diff --git a/.gitignore b/.gitignore index e0f0a6e..1e994fc 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ # testing /coverage /e2e/app/target/ +/e2e/.driver/ # next.js /.next/ diff --git a/AGENTS.md b/AGENTS.md index c10c760..009c52d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -91,7 +91,8 @@ donutbrowser/ ### Native app E2E tests are mandatory for affected behavior -The native suites use a sibling private test-driver repository and launch an `e2e`-feature build. +The native suites use the published `tauri-wd` driver (pinned in `e2e/app/Cargo.toml`, installed +into the ignored `e2e/.driver` root) and launch an `e2e`-feature build. Every session gets its own temporary Donut data/cache/log root, home directory, WebView store, ports, and sync bucket. Never point a suite at production or development data. diff --git a/e2e/README.md b/e2e/README.md index d2e5eb9..6a1f4c2 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -1,6 +1,7 @@ # Donut Browser native E2E tests -These tests exercise the actual Tauri application through a sibling native test driver. They do +These tests exercise the actual Tauri application through the published +[`tauri-wd`](https://crates.io/crates/tauri-wd) native test driver. They do not replace Rust or React unit tests; they cover the process boundaries those tests cannot: WKWebView/WebView2/WebKitGTK UI, Tauri invokes, REST and MCP servers, two-device sync, S3 payload encryption, Wayfern, CDP, and child-process @@ -8,15 +9,9 @@ cleanup. ## Local setup -Place both repositories beside each other: - -```text -Code/ -├── donutbrowser/ -└── / -``` - -Install Donut dependencies with `pnpm install`. The browser suite also needs +Install Donut dependencies with `pnpm install`. The runner installs the driver itself with +`cargo install`, so a working Rust toolchain is the only extra requirement. The browser suite also +needs `WAYFERN_TEST_TOKEN`. The runner reads it from the environment or Donut's ignored `.env` without printing it. When a local browser fixture is configured, the runner copies it into the test data root (using an isolated APFS clone on macOS); otherwise the browser suite downloads the current @@ -42,10 +37,13 @@ pnpm e2e:sync pnpm e2e:browser ``` -Run everything with `pnpm e2e`. A normal run builds the Next frontend, `donut-proxy`, the -private harness in `e2e/app`, and `tauri-wd`. The harness enables Donut's `e2e` feature and injects -the sibling WebDriver plugin without making the production crate depend on a private filesystem -path. Add `--no-build` to `node e2e/run.mjs --suite=` only when all four outputs are current. +Run everything with `pnpm e2e`. A normal run builds the Next frontend, `donut-proxy`, and the +harness in `e2e/app`, then installs the `tauri-wd` CLI into the ignored `e2e/.driver` root when the +version pinned by `e2e/app/Cargo.lock` is not already there. The harness enables Donut's `e2e` +feature and injects the WebDriver plugin so the production crate never depends on it. Both the +plugin and the CLI come from the same pinned crates.io release, so they cannot drift apart. Bump +the pin in `e2e/app/Cargo.toml` to move to a newer driver. Add `--no-build` to +`node e2e/run.mjs --suite=` only when all four outputs are current. `DONUT_E2E_KEEP_ARTIFACTS=1` retains successful local runs; failed runs are always retained and their location is printed. Raw screenshots, captured HTML, logs, and isolated app state stay local. The runner also creates a text-only `diagnostics/` directory whose logs are redacted and checked @@ -87,7 +85,7 @@ requests. Pushes to `main`, weekly schedules, and manual runs execute the full m including MinIO-backed sync and real Wayfern automation, plus a Linux/Docker job for residential proxy and local WireGuard browser traffic. -CI needs a `TAURI_WEBDRIVER_TOKEN` secret with read-only access and a -`TAURI_WEBDRIVER_REPOSITORY` repository variable identifying the test-driver checkout. The full -job also requires the `WAYFERN_TEST_TOKEN` secret. The network job requires that secret plus +Every job restores the compiled driver from an `actions/cache` entry keyed by `e2e/app/Cargo.lock`, +the same file the runner reads the version from, so only a driver bump pays for a rebuild. The full job requires the +`WAYFERN_TEST_TOKEN` secret. The network job requires that secret plus `RESIDENTIAL_PROXY_URL_ONE_HTTP` and `RESIDENTIAL_PROXY_URL_ONE_SOCKS`. diff --git a/e2e/app/Cargo.lock b/e2e/app/Cargo.lock index f39426a..85eab14 100644 --- a/e2e/app/Cargo.lock +++ b/e2e/app/Cargo.lock @@ -1131,9 +1131,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.1" +version = "4.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +checksum = "d91e0c145792ef73a6ad36d27c75ac09f1832222a3c209689d90f534685ee5b7" dependencies = [ "clap_builder", "clap_derive", @@ -1141,9 +1141,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.6.0" +version = "4.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b" dependencies = [ "anstream", "anstyle", @@ -1153,14 +1153,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.6.1" +version = "4.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.3", ] [[package]] @@ -2370,9 +2370,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" dependencies = [ "futures-core", "futures-sink", @@ -2380,9 +2380,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" [[package]] name = "futures-executor" @@ -2397,9 +2397,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" [[package]] name = "futures-lite" @@ -2416,9 +2416,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" dependencies = [ "proc-macro2", "quote", @@ -2427,21 +2427,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" dependencies = [ "futures-channel", "futures-core", @@ -3819,6 +3819,15 @@ dependencies = [ "web_atoms", ] +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + [[package]] name = "matchit" version = "0.8.4" @@ -4066,6 +4075,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "num-bigint" version = "0.4.8" @@ -4950,6 +4968,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "process-wrap" +version = "9.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e842efad9119158434d193c6682e2ebee4b44d6ad801d7b349623b3f57cdf55" +dependencies = [ + "futures", + "indexmap 2.14.0", + "nix", + "tokio", + "windows 0.62.2", +] + [[package]] name = "profiling" version = "1.0.18" @@ -6019,6 +6050,15 @@ dependencies = [ "sha1", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shared_child" version = "1.1.1" @@ -6327,6 +6367,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "sync_wrapper" version = "1.0.2" @@ -6907,13 +6958,17 @@ dependencies = [ [[package]] name = "tauri-wd" -version = "0.1.0" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "535cd782aac407a0bbf593013306e89e821017e3862ddd7b36925aa80a29e649" dependencies = [ "async-trait", "axum", "base64 0.23.0", "block2", "cairo-rs", + "clap", + "futures-util", "glib 0.21.5", "gtk", "image", @@ -6922,13 +6977,17 @@ dependencies = [ "objc2-app-kit", "objc2-foundation", "objc2-web-kit", + "process-wrap", + "reqwest", "serde", "serde_json", "tauri", "tauri-plugin", "tempfile", + "thiserror 2.0.18", "tokio", "tracing", + "tracing-subscriber", "uuid", "webkit2gtk", "webview2-com", @@ -7009,6 +7068,15 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "thread_local" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" +dependencies = [ + "cfg-if", +] + [[package]] name = "tiff" version = "0.11.3" @@ -7418,6 +7486,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", ] [[package]] @@ -7789,6 +7900,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "value-bag" version = "1.12.0" diff --git a/e2e/app/Cargo.toml b/e2e/app/Cargo.toml index 1ba09dc..0555d60 100644 --- a/e2e/app/Cargo.toml +++ b/e2e/app/Cargo.toml @@ -6,4 +6,4 @@ publish = false [dependencies] donutbrowser-lib = { package = "donutbrowser", path = "../../src-tauri", features = ["e2e"] } -tauri-wd = { path = "../../../tauri-wd/crates/tauri-wd" } +tauri-wd = "=0.1.11" diff --git a/e2e/run.mjs b/e2e/run.mjs index a58ad5a..71fedbb 100644 --- a/e2e/run.mjs +++ b/e2e/run.mjs @@ -5,6 +5,7 @@ import { createReadStream, createWriteStream, existsSync, + readFileSync, statSync, } from "node:fs"; import { @@ -28,21 +29,19 @@ import { createSafeDiagnostics } from "./lib/diagnostics.mjs"; const dirname = path.dirname(fileURLToPath(import.meta.url)); const projectRoot = path.resolve(dirname, ".."); -const webdriverRoot = path.resolve(projectRoot, "../tauri-wd"); const isWindows = process.platform === "win32"; const executableSuffix = isWindows ? ".exe" : ""; +const appManifestDir = path.join(projectRoot, "e2e", "app"); const appBinary = path.join( - projectRoot, - "e2e", - "app", + appManifestDir, "target", "debug", `donutbrowser-e2e${executableSuffix}`, ); +const driverRoot = path.join(projectRoot, "e2e", ".driver"); const driverBinary = path.join( - webdriverRoot, - "target", - "debug", + driverRoot, + "bin", `tauri-wd${executableSuffix}`, ); @@ -238,10 +237,55 @@ async function loadLocalValues(names) { return values; } -function buildAll() { - if (!existsSync(webdriverRoot)) { - throw new Error(`Missing sibling webdriver repository: ${webdriverRoot}`); +function lockedDriverVersion() { + const lockfile = readFileSync( + path.join(appManifestDir, "Cargo.lock"), + "utf8", + ); + const match = lockfile.match( + /\[\[package\]\]\s*\nname = "tauri-wd"\s*\nversion = "([^"]+)"/, + ); + if (!match) { + throw new Error("e2e/app/Cargo.lock does not resolve a tauri-wd version"); } + return match[1]; +} + +function installedDriverVersion() { + if (!existsSync(driverBinary)) { + return null; + } + const result = spawnSync(driverBinary, ["--version"], { encoding: "utf8" }); + if (result.status !== 0) { + return null; + } + return result.stdout.trim().split(/\s+/).pop() ?? null; +} + +function ensureDriver() { + const version = lockedDriverVersion(); + if (installedDriverVersion() === version) { + log(`tauri-wd ${version} already installed at ${driverBinary}`); + return; + } + run( + "cargo", + [ + "install", + "tauri-wd", + "--version", + version, + "--locked", + "--debug", + "--force", + "--root", + driverRoot, + ], + projectRoot, + ); +} + +function buildAll() { run("pnpm", ["build"], projectRoot); run("pnpm", ["copy-proxy-binary"], projectRoot); run(process.execPath, ["src-tauri/download-xray.mjs"], projectRoot); @@ -250,11 +294,7 @@ function buildAll() { ["build", "--locked", "--manifest-path", "e2e/app/Cargo.toml"], projectRoot, ); - run( - "cargo", - ["build", "--package", "tauri-cross-platform-webdriver"], - webdriverRoot, - ); + ensureDriver(); } function startFixtureServer(geoIpFixture) { @@ -866,7 +906,7 @@ async function main() { options.verbose ? "debug" : "info", ], { - cwd: webdriverRoot, + cwd: projectRoot, env: process.env, runRoot, verbose: options.verbose, @@ -928,7 +968,6 @@ async function main() { ...process.env, DONUT_E2E_RUN_ROOT: runRoot, DONUT_E2E_PROJECT_ROOT: projectRoot, - DONUT_E2E_WEBDRIVER_ROOT: webdriverRoot, DONUT_E2E_APP: appBinary, DONUT_E2E_DRIVER_URL: `http://127.0.0.1:${driverPort}`, DONUT_E2E_FIXTURE_URL: `http://127.0.0.1:${fixture.port}`, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e5bda4c..83db260 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -700,8 +700,8 @@ packages: '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} - '@emnapi/runtime@1.11.3': - resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} @@ -2837,8 +2837,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.11.3: - resolution: {integrity: sha512-sbT0Ui/CZwyAyy7icT1Gw5P1LKRlFaHwaF6tDCW5YHq2X5SeeZFphBuIagopSfwSSZq3sQcbmEL072yphxm7ew==} + baseline-browser-mapping@2.10.38: + resolution: {integrity: sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==} engines: {node: '>=6.0.0'} hasBin: true @@ -2905,8 +2905,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001806: - resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==} + caniuse-lite@1.0.30001799: + resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==} canvas-confetti@1.9.4: resolution: {integrity: sha512-yxQbJkAVrFXWNbTUjPqjF7G+g6pDotOUHGbkZq2NELZUMDpiJ85rIEazVb8GTaAptNW2miJAXbs1BtioA251Pw==} @@ -4098,11 +4098,6 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanoid@3.3.16: - resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - napi-postinstall@0.3.4: resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -4291,10 +4286,6 @@ packages: resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.23: - resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==} - engines: {node: ^10 || ^12 || >=14} - pretty-format@30.4.1: resolution: {integrity: sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -5761,7 +5752,7 @@ snapshots: tslib: 2.8.1 optional: true - '@emnapi/runtime@1.11.3': + '@emnapi/runtime@1.11.1': dependencies: tslib: 2.8.1 optional: true @@ -5878,7 +5869,7 @@ snapshots: '@img/sharp-wasm32@0.35.3': dependencies: - '@emnapi/runtime': 1.11.3 + '@emnapi/runtime': 1.11.1 optional: true '@img/sharp-webcontainers-wasm32@0.35.3': @@ -8023,7 +8014,7 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.11.3: {} + baseline-browser-mapping@2.10.38: {} bl@4.1.0: dependencies: @@ -8053,8 +8044,8 @@ snapshots: browserslist@4.28.4: dependencies: - baseline-browser-mapping: 2.11.3 - caniuse-lite: 1.0.30001806 + baseline-browser-mapping: 2.10.38 + caniuse-lite: 1.0.30001799 electron-to-chromium: 1.5.376 node-releases: 2.0.48 update-browserslist-db: 1.2.3(browserslist@4.28.4) @@ -8098,7 +8089,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001806: {} + caniuse-lite@1.0.30001799: {} canvas-confetti@1.9.4: {} @@ -9382,8 +9373,6 @@ snapshots: nanoid@3.3.16: {} - nanoid@3.3.16: {} - napi-postinstall@0.3.4: {} natural-compare@1.4.0: {} @@ -9401,8 +9390,8 @@ snapshots: dependencies: '@next/env': 16.2.11 '@swc/helpers': 0.5.15 - baseline-browser-mapping: 2.11.3 - caniuse-lite: 1.0.30001806 + baseline-browser-mapping: 2.10.38 + caniuse-lite: 1.0.30001799 postcss: 8.5.23 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -9544,12 +9533,6 @@ snapshots: pluralize@8.0.0: {} - postcss@8.5.23: - dependencies: - nanoid: 3.3.16 - picocolors: 1.1.1 - source-map-js: 1.2.1 - postcss@8.5.23: dependencies: nanoid: 3.3.16