Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions .github/workflows/e2e-web-wallet-nightly.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Real-MetaMask-Flask E2E suite for the web-wallet — SIGNAL ONLY, NON-GATING.
#
# This runs on a schedule (not per-PR) purely to surface rot in the on-demand E2E infrastructure
# (see packages/web-wallet/tests/e2e/e2e.md). It is NOT a required check and MUST NOT be added to
# branch protection. A red run means "a real-MetaMask journey didn't pass on the runner", which is
# often a slow cold-start rather than a broken product — triage via the uploaded artifacts.
name: E2E Web Wallet (nightly, non-gating)

on:
schedule:
- cron: '0 6 * * *' # 06:00 UTC daily
workflow_dispatch: {} # allow manual runs from the Actions tab

concurrency:
group: e2e-web-wallet-nightly
cancel-in-progress: false

permissions:
contents: read

jobs:
e2e:
runs-on: ubuntu-latest
# Non-gating: never fail the workflow status in a way that could be mistaken for a gate.
# The step-level result + artifacts carry the signal; a red job here blocks nobody.
timeout-minutes: 60
env:
# Pin the Flask build the selectors are verified against (driver/selectors.ts).
E2E_FLASK_VERSION: '13.31.0'
# Stretch every driver + page-object deadline for a slower shared runner.
E2E_TIMEOUT_SCALE: '2'
# MetaMask password for the throwaway wallet the driver onboards/imports each run. NOT a
# secret — it guards a fresh local MetaMask created per run; the funded seed is already public
# in wallets.config.json. The driver defaults to DEFAULT_PASSWORD ('Test1234!Test') if unset;
# 'Hathor@123' matches the owner's local .env.e2e so CI reproduces local runs 1:1. Override via
# a repo Actions variable `vars.E2E_PASSWORD` if desired.
E2E_PASSWORD: ${{ vars.E2E_PASSWORD || 'Hathor@123' }}
steps:
- name: Checkout code
uses: actions/checkout@v4
Comment on lines +39 to +40

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workflow excerpt =="
sed -n '1,140p' .github/workflows/e2e-web-wallet-nightly.yml

echo
echo "== checkout usages and git refs/triggers/actions in workflow =="
rg -n "actions/checkout|persist-credentials|git (fetch|clone|clone|add|push|submodule|config|lfs|push|fetch)|github\.|GITHUB_TOKEN|permissions|pull_request|workflows|packages" .github/workflows/e2e-web-wallet-nightly.yml

Repository: HathorNetwork/hathor-rpc-lib

Length of output: 4289


🌐 Web query:

actions/checkout v4 persist-credentials default true README

💡 Result:

In the actions/checkout action, the persist-credentials input has a default value of true [1][2][3]. When set to true (the default), the action automatically configures the repository's local git configuration with the provided token or SSH key [1][3]. This allows subsequent git commands (such as git fetch or git push) run within the same job to be authenticated automatically [4][3]. This token is removed during the post-job cleanup process [4][3]. To opt out of this behavior, you can explicitly set persist-credentials to false in your workflow file: - uses: actions/checkout@v4 with: persist-credentials: false Note that in newer versions (v6.0.0 and later), the implementation of this feature was updated to store credentials in a file under $RUNNER_TEMP rather than directly in the local.git/config file, which improves security [3][5][6].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== exact checkout line and next lines =="
nl -ba .github/workflows/e2e-web-wallet-nightly.yml | sed -n '35,43p'

Repository: HathorNetwork/hathor-rpc-lib

Length of output: 247


🌐 Web query:

site:github.com/actions/checkout v4 persist-credentials default true README

💡 Result:

In the actions/checkout action, the persist-credentials input controls whether the authentication token (or SSH key) is configured in the local git repository's configuration [1][2]. As of the current version (v4), the default value for persist-credentials remains true [1][2]. When set to true, the action persists the credentials in the local git config, allowing subsequent steps in your workflow to execute authenticated git commands (e.g., git fetch, git push) [1][3]. These credentials are automatically removed during the post-job cleanup phase [1]. If you wish to opt out of this behavior for security reasons or because it is not required, you can explicitly set the input to false in your workflow YAML [1]: - name: Checkout uses: actions/checkout@v4 with: persist-credentials: false While there has been community discussion and proposed pull requests (such as PR #1687) to change this default to false to reduce the risk of credential exposure, the official action.yml and documentation for actions/checkout@v4 continue to list true as the default [1][2][4].

Citations:


Disable checkout credential persistence.

actions/checkout@v4 persists the read-only GITHUB_TOKEN in .git/config by default. This workflow’s remaining steps only install dependencies, build, install Playwright, and run/upload E2E artifacts, so authenticated Git commands are not needed.

Proposed fix
       - name: Checkout code
         uses: actions/checkout@v4
+        with:
+          persist-credentials: false
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Checkout code
uses: actions/checkout@v4
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
🧰 Tools
🪛 zizmor (1.28.0)

[warning] 39-40: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/e2e-web-wallet-nightly.yml around lines 39 - 40, Update
the actions/checkout@v4 step in the workflow to disable credential persistence
by setting persist-credentials to false, leaving the checkout behavior and
subsequent E2E steps unchanged.

Source: Linters/SAST tools


- name: Install Nix
uses: cachix/install-nix-action@ba0dd844c9180cbf77aa72a116d6fbc515d0e87b
with:
nix_path: nixpkgs=channel:nixos-unstable
extra_nix_config: |
experimental-features = nix-command flakes

- name: Cache Nix
uses: DeterminateSystems/magic-nix-cache-action@v7

- name: Install dependencies
run: nix develop . -c yarn install
Comment on lines +52 to +53

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files .github/workflows package.json yarn.lock | sed -n '1,120p'

echo
echo "== workflow context =="
if [ -f ".github/workflows/e2e-web-wallet-nightly.yml" ]; then
  nl -ba .github/workflows/e2e-web-wallet-nightly.yml | sed -n '45,95p'
fi

echo
echo "== main workflow searches =="
rg -n "yarn install|immutable|e2e-web-wallet-nightly|nix develop.*yarn install" .github/workflows || true

echo
echo "== root package scripts =="
if [ -f package.json ]; then nl -ba package.json | sed -n '1,140p'; fi

echo
echo "== package manager metadata =="
if [ -f .yarnrc.yml ]; then nl -ba .yarnrc.yml | sed -n '1,160p'; fi
if [ -f corepack/package.json ]; then nl -ba corepack/package.json | sed -n '1,120p'; fi

echo
echo "== deterministic config probe =="
python3 - <<'PY'
import json, pathlib, re
p=pl.Path('.github/workflows/e2e-web-wallet-nightly.yml')
if not p.exists():
    print("no nightly workflow")
else:
    text=p.read_text()
    for i,line in enumerate(text.splitlines(),1):
        if 'yarn install' in line:
            print(f"{i}: {line.strip()}")
PY

Repository: HathorNetwork/hathor-rpc-lib

Length of output: 427


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== read workflow file =="
awk '{printf "%4d\t%s\n", NR, $0}' .github/workflows/e2e-web-wallet-nightly.yml

echo
echo "== compare workflows for yarn installation =="
python3 - <<'PY'
import pathlib, re
for path in pathlib.Path(".github/workflows").glob("*.yml"):
    text = path.read_text()
    print(f"\n## {path}")
    for i, line in enumerate(text.splitlines(), 1):
        if "yarn install" in line or "immutable" in line or re.search(r"yarn\s+(install|set-version|enable-corepack)", line):
            print(f"{i}: {line}")
PY

echo
echo "== root package/config files =="
awk '{printf "%4d\t%s\n", NR, $0}' package.json
echo
if [ -f .yarnrc.yml ]; then awk '{printf "%4d\t%s\n", NR, $0}' .yarnrc.yml; else echo "no .yarnrc.yml"; fi

echo
echo "== lockfile first bytes =="
awk 'NR<=40 {printf "%4d\t%s\n", NR, $0}' yarn.lock

Repository: HathorNetwork/hathor-rpc-lib

Length of output: 7291


Enforce the workspace lockfile during dependency installation.

Yarn 4’s plain yarn install can update stale/missing entries from package.json while leaving the lockfile behind; this nightly job then runs from a different dependency graph than the committed lockfile. Use an immutable install, e.g. yarn install --immutable, or change the workflow spec if non-locked installs are intentional.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/e2e-web-wallet-nightly.yml around lines 52 - 53, Update
the “Install dependencies” step in the nightly workflow to run Yarn with
immutable lockfile enforcement, using the existing yarn install command with the
--immutable option. Preserve the current Nix development-shell invocation while
ensuring installation fails instead of modifying or bypassing the committed
lockfile.


# Independent of PR 1: build the workspace dependency the snap dev build consumes, so this
# workflow is self-sufficient whether or not PR 1's self-building `yarn e2e` has merged.
- name: Build hathor-rpc-handler (snap dependency)
run: nix develop . -c yarn workspace @hathor/hathor-rpc-handler build

- name: Install Playwright Chromium
run: nix develop . -c yarn workspace @hathor/web-wallet exec playwright install chromium --with-deps

- name: Install xvfb
run: sudo apt-get update && sudo apt-get install -y xvfb

# Headed Chromium under a virtual display (MV3 extensions need a real display). The Playwright
# config's webServer boots the local snap (:8080) and the dApp dev server (:5173) itself.
- name: Run real-MetaMask E2E suite
run: |
xvfb-run -a nix develop . -c \
yarn workspace @hathor/web-wallet exec \
playwright test --config playwright.e2e.config.ts

- name: Upload Playwright report
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: packages/web-wallet/playwright-report/
retention-days: 14
if-no-files-found: ignore

- name: Upload traces & videos
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: playwright-traces
path: packages/web-wallet/test-results/
retention-days: 14
if-no-files-found: ignore
177 changes: 177 additions & 0 deletions docs/superpowers/specs/2026-07-28-web-wallet-e2e-pr3-nightly-ci.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# PR 3 — Nightly non-gating real-MetaMask E2E CI (issue #177, item 2c)

Status: **ready to implement (autonomous)**
Issue: HathorNetwork/hathor-rpc-lib#177 (item 2c)
Design: `docs/superpowers/specs/2026-07-09-web-wallet-e2e-followups-177-design.md`
Base branch: **`master`** (independent — the workflow builds the handler explicitly, so it does not
depend on PR 1's self-sufficient `yarn e2e`)
Feature branch: `raul-oliveira/feat/web-wallet-e2e-followups-pr3`

> **For the implementing subagent:** git is authorized for this branch (commit + push + open-PR
> autopilot for this sequence). No `Co-Authored-By`. English artifacts. Full green requires a real
> scheduled GitHub run; locally you validate by **linting the YAML and dry-running the individual
> steps** — do **not** run the full headed suite here (that browser slot is PR 2's).

## Problem

Nothing exercises the real-MetaMask suite in CI. `main.yml` runs on `[push]` and only runs the
lightweight smoke (`playwright test --project=chromium`), not `playwright.e2e.config.ts`. So rot like
item 1 (clean checkout can't run the suite) goes unnoticed until someone runs it by hand. The suite
is **explicitly non-gating**, so the fix is a **scheduled, signal-only** run — never a required
check, never blocking a PR.

## Decision

A new **scheduled** GitHub Actions workflow, separate from `main.yml`, that runs the real-MetaMask
suite for signal only. A scheduled workflow never attaches as a PR status check, so it is inherently
non-gating; additionally it must **not** be added to branch protection / required checks. It reuses
the Nix toolchain from `main.yml`, boots a real headed Chromium under **xvfb** (MV3 extension loading
needs a real display; true `--headless` is deferred per the design's out-of-scope), and uploads the
HTML report + traces + videos as artifacts for triage.

## Change — `.github/workflows/e2e-web-wallet-nightly.yml` (new)

```yaml
# Real-MetaMask-Flask E2E suite for the web-wallet — SIGNAL ONLY, NON-GATING.
#
# This runs on a schedule (not per-PR) purely to surface rot in the on-demand E2E infrastructure
# (see packages/web-wallet/tests/e2e/e2e.md). It is NOT a required check and MUST NOT be added to
# branch protection. A red run means "a real-MetaMask journey didn't pass on the runner", which is
# often a slow cold-start rather than a broken product — triage via the uploaded artifacts.
name: E2E Web Wallet (nightly, non-gating)

on:
schedule:
- cron: '0 6 * * *' # 06:00 UTC daily
workflow_dispatch: {} # allow manual runs from the Actions tab

concurrency:
group: e2e-web-wallet-nightly
cancel-in-progress: false

permissions:
contents: read

jobs:
e2e:
runs-on: ubuntu-latest
# Non-gating: never fail the workflow status in a way that could be mistaken for a gate.
# The step-level result + artifacts carry the signal; a red job here blocks nobody.
timeout-minutes: 60
env:
# Pin the Flask build the selectors are verified against (driver/selectors.ts).
E2E_FLASK_VERSION: '13.31.0'
# Stretch every driver + page-object deadline for a slower shared runner.
E2E_TIMEOUT_SCALE: '2'
# MetaMask password for the throwaway wallet the driver onboards/imports each run. NOT a
# secret — it guards a fresh local MetaMask created per run; the funded seed is already public
# in wallets.config.json. The driver defaults to DEFAULT_PASSWORD ('Test1234!Test') if unset;
# 'Hathor@123' matches the owner's local .env.e2e so CI reproduces local runs 1:1. Override via
# a repo Actions variable `vars.E2E_PASSWORD` if desired.
E2E_PASSWORD: ${{ vars.E2E_PASSWORD || 'Hathor@123' }}
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install Nix
uses: cachix/install-nix-action@ba0dd844c9180cbf77aa72a116d6fbc515d0e87b
with:
nix_path: nixpkgs=channel:nixos-unstable
extra_nix_config: |
experimental-features = nix-command flakes

- name: Cache Nix
uses: DeterminateSystems/magic-nix-cache-action@v7

- name: Install dependencies
run: nix develop . -c yarn install --immutable

# Independent of PR 1: build the workspace dependency the snap dev build consumes, so this
# workflow is self-sufficient whether or not PR 1's self-building `yarn e2e` has merged.
- name: Build hathor-rpc-handler (snap dependency)
run: nix develop . -c yarn workspace @hathor/hathor-rpc-handler build

- name: Install Playwright Chromium
run: nix develop . -c yarn workspace @hathor/web-wallet exec playwright install chromium --with-deps

- name: Install xvfb
run: sudo apt-get update && sudo apt-get install -y xvfb

# Headed Chromium under a virtual display (MV3 extensions need a real display). The Playwright
# config's webServer boots the local snap (:8080) and the dApp dev server (:5173) itself.
- name: Run real-MetaMask E2E suite
run: |
xvfb-run -a nix develop . -c \
yarn workspace @hathor/web-wallet exec \
playwright test --config playwright.e2e.config.ts

- name: Upload Playwright report
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: packages/web-wallet/playwright-report/
retention-days: 14
if-no-files-found: ignore

- name: Upload traces & videos
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: playwright-traces
path: packages/web-wallet/test-results/
retention-days: 14
if-no-files-found: ignore
```

### Notes for the subagent (verify, don't assume)

- **`--immutable`** on install: confirm the repo's yarn install is clean in CI; if `main.yml` uses a
plain `yarn install`, match that instead to avoid lockfile-churn failures. Prefer matching
`main.yml`'s exact invocation.
- **`E2E_PASSWORD`** (confirmed): the driver reads it at `MetaMaskDriver.ts:128,229` with fallback
`DEFAULT_PASSWORD = 'Test1234!Test'`. The env line is therefore optional, but keep it set to
`Hathor@123` (the owner's local value) so CI reproduces local runs 1:1. Any valid MetaMask password
works — the wallet is created fresh each run, so the value only needs to be internally consistent.
- **No PIN is consumed.** The E2E suite reads only `E2E_PASSWORD` (plus `E2E_FLASK_VERSION`,
`E2E_TIMEOUT_SCALE`, `E2E_HEADED/HEADLESS`, `E2E_METAMASK_PATH`, `E2E_DEBUG`, `E2E_SPLIT_WINDOWS`).
There is no PIN env var and no page object types a PIN — the Snap signs off MetaMask's unlocked
seed, so the standalone-wallet PIN (`123123`) is not needed anywhere in these workflows. Do not wire
an unused `E2E_PIN`.
- **Funded journeys**: `import`, `feature-example`, `token-lifecycle` use the committed `funded`
testnet stub seed; they pass only while that testnet address has balance. On a dry wallet they fail
at their first funded step — acceptable for a non-gating signal run. If the team wants a clean
always-green signal, a follow-up can restrict the schedule to `--project=onboarding` (dry); leave
the full matrix here and document the caveat in the PR body.
- **xvfb availability**: `xvfb-run` comes from the `xvfb` apt package (installed above);
`playwright install --with-deps` does not guarantee it. Keep the explicit apt step.
- **Pin actions** the way `main.yml` does (it pins `install-nix-action` by SHA, uses
`magic-nix-cache-action@v7`). Match those exact refs.

## Validation

- **Lint the YAML**: `nix develop . -c npx --yes @action-validator/cli .github/workflows/e2e-web-wallet-nightly.yml`
(or `actionlint` if available in the shell); at minimum, parse it with a YAML loader to confirm it
is well-formed. Confirm indentation and that every `uses:` ref matches `main.yml`'s style.
- **Dry-run the load-bearing steps locally** (inside `nix develop`): `yarn install`, the handler
build, `playwright install chromium` — confirm each succeeds. Do **not** run the full headed suite
locally (PR 2 owns the browser slot during the sequence).
- State plainly in the PR that **full green requires a real scheduled/`workflow_dispatch` run on
GitHub**; the local checks only prove the workflow is well-formed and its steps resolve.

## Files

| File | Change |
|------|--------|
| `.github/workflows/e2e-web-wallet-nightly.yml` | **new** scheduled non-gating workflow |
| `packages/web-wallet/tests/e2e/e2e.md` | one line pointing at the nightly workflow as the CI signal (optional, keep tiny) |
| `docs/superpowers/specs/2026-07-28-web-wallet-e2e-pr3-nightly-ci.md` | copy this spec into the worktree |

## PR

- **Single commit.** Message: `ci(web-wallet): nightly non-gating real-MetaMask E2E workflow`
- **Title:** `ci(web-wallet): nightly non-gating E2E workflow (#177 item 2c)`
- **Template** feature-branch: Motivation / Acceptance Criteria (**plain bullets**) / Checklist.
- **Assignee** `raul-oliveira`; **Board** project #15 → In Progress (Done). REST for
title/body/assignee. In the body: PR 3 of the #177 sequence, closes item 2c; note it is
intentionally non-gating and must not be added to required checks.
2 changes: 2 additions & 0 deletions packages/web-wallet/tests/e2e/e2e.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,8 @@ tests/e2e/

- `packages/web-wallet/docs/qa-automation-strategy.md` — tooling, how to run the suite, and
which layer (unit / component / E2E) owns each case.
- `.github/workflows/e2e-web-wallet-nightly.yml` — scheduled, **non-gating** CI run of this suite
(signal only; never a required check). Triage failures via its uploaded Playwright artifacts.

> **Security:** `wallets.config.json` holds **public testnet stub seeds only** — never a
> mainnet seed or any wallet with real value.
Loading