From e32f127a7bb372a975ca17af1de4ffe552340c17 Mon Sep 17 00:00:00 2001 From: xqxpx Date: Tue, 28 Apr 2026 12:36:41 +0200 Subject: [PATCH 1/9] features clean up --- src/backend/env/features.rs | 16 ---------------- src/backend/env/memory.rs | 4 ---- src/backend/env/mod.rs | 1 - src/backend/updates.rs | 14 -------------- 4 files changed, 35 deletions(-) delete mode 100644 src/backend/env/features.rs diff --git a/src/backend/env/features.rs b/src/backend/env/features.rs deleted file mode 100644 index f4082bd1..00000000 --- a/src/backend/env/features.rs +++ /dev/null @@ -1,16 +0,0 @@ -use std::collections::HashSet; - -use serde::{Deserialize, Serialize}; - -use super::{user::UserId, Time}; - -// Retained only so the `Memory::features` index can still be deserialized. -// The next release drops the field; serde silently ignores it once the migration -// has drained the index. -#[derive(Default, Serialize, Deserialize)] -pub struct Feature { - pub supporters: HashSet, - pub status: u8, - #[serde(default)] - pub last_activity: Time, -} diff --git a/src/backend/env/memory.rs b/src/backend/env/memory.rs index 5c1dbd83..745263fd 100644 --- a/src/backend/env/memory.rs +++ b/src/backend/env/memory.rs @@ -1,5 +1,4 @@ use super::{ - features::Feature, post::{Post, PostId}, token::Transaction, }; @@ -22,8 +21,6 @@ pub struct Api { pub struct Memory { api: Api, pub posts: ObjectManager, - // TODO: remove - pub features: ObjectManager, #[serde(default)] pub ledger: ObjectManager, #[serde(skip)] @@ -84,7 +81,6 @@ impl Memory { self.api.init(); self.api_ref = Rc::new(RefCell::new(std::mem::take(&mut self.api))); self.posts.init(Rc::clone(&self.api_ref)); - self.features.init(Rc::clone(&self.api_ref)); self.ledger.init(Rc::clone(&self.api_ref)); } diff --git a/src/backend/env/mod.rs b/src/backend/env/mod.rs index f60c4122..6ea72cb1 100644 --- a/src/backend/env/mod.rs +++ b/src/backend/env/mod.rs @@ -38,7 +38,6 @@ pub mod canisters; pub mod config; pub mod delegations; pub mod domains; -pub mod features; pub mod invite; pub mod invoices; pub mod memory; diff --git a/src/backend/updates.rs b/src/backend/updates.rs index bf630383..1fc5988c 100644 --- a/src/backend/updates.rs +++ b/src/backend/updates.rs @@ -98,24 +98,10 @@ fn post_upgrade() { #[allow(clippy::all)] fn sync_post_upgrade_fixtures() { - mutate(|state| { - // One-time cleanup: drain the features index. Each remove() frees the - // stable-memory blocks back to the allocator. The next release can then - // drop Memory::features entirely. - let ids: Vec = state.memory.features.iter().map(|(id, _)| *id).collect(); - for id in ids { - if let Err(err) = state.memory.features.remove(&id) { - state - .logger - .error(format!("couldn't drain feature {}: {}", id, err)); - } - } - }); } #[allow(clippy::all)] async fn async_post_upgrade_fixtures() { - env::storage::upgrade_buckets().await; } /* From a3330e04b98b18359049d8625b8a70dbb301736b Mon Sep 17 00:00:00 2001 From: xqxpx Date: Tue, 28 Apr 2026 12:37:16 +0200 Subject: [PATCH 2/9] simplify js serving --- src/backend/assets.rs | 36 ------------------------------- src/backend/updates.rs | 6 ++---- src/frontend/src/index.html | 4 ---- webpack.config.js | 43 ------------------------------------- 4 files changed, 2 insertions(+), 87 deletions(-) diff --git a/src/backend/assets.rs b/src/backend/assets.rs index c8ce986f..29c28669 100644 --- a/src/backend/assets.rs +++ b/src/backend/assets.rs @@ -62,42 +62,6 @@ pub fn load(domains: &HashMap) { include_bytes!("../../dist/frontend/index.js.gz").to_vec(), ); - add_asset( - &["/dfinity.js"], - vec![ - ("Content-Type".into(), "text/javascript".into()), - ("Content-Encoding".into(), "gzip".into()), - ], - include_bytes!("../../dist/frontend/dfinity.js.gz").to_vec(), - ); - - add_asset( - &["/react.js"], - vec![ - ("Content-Type".into(), "text/javascript".into()), - ("Content-Encoding".into(), "gzip".into()), - ], - include_bytes!("../../dist/frontend/react.js.gz").to_vec(), - ); - - add_asset( - &["/vendors.js"], - vec![ - ("Content-Type".into(), "text/javascript".into()), - ("Content-Encoding".into(), "gzip".into()), - ], - include_bytes!("../../dist/frontend/vendors.js.gz").to_vec(), - ); - - add_asset( - &["/app-components.js"], - vec![ - ("Content-Type".into(), "text/javascript".into()), - ("Content-Encoding".into(), "gzip".into()), - ], - include_bytes!("../../dist/frontend/app-components.js.gz").to_vec(), - ); - add_asset( &["/favicon.ico"], vec![("Content-Type".into(), "image/vnd.microsoft.icon".into())], diff --git a/src/backend/updates.rs b/src/backend/updates.rs index 1fc5988c..92985d47 100644 --- a/src/backend/updates.rs +++ b/src/backend/updates.rs @@ -97,12 +97,10 @@ fn post_upgrade() { } #[allow(clippy::all)] -fn sync_post_upgrade_fixtures() { -} +fn sync_post_upgrade_fixtures() {} #[allow(clippy::all)] -async fn async_post_upgrade_fixtures() { -} +async fn async_post_upgrade_fixtures() {} /* * UPDATES diff --git a/src/frontend/src/index.html b/src/frontend/src/index.html index 96a5336c..3a657013 100644 --- a/src/frontend/src/index.html +++ b/src/frontend/src/index.html @@ -42,11 +42,7 @@ type="font/woff2" crossorigin /> - - - - diff --git a/webpack.config.js b/webpack.config.js index 709dd691..ce19cb56 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -73,46 +73,6 @@ module.exports = { extractComments: false, }), ], - splitChunks: { - chunks: "all", - cacheGroups: { - // Vendor chunk for node_modules - vendor: { - test: /[\\/]node_modules[\\/]/, - name: "vendors", - chunks: "all", - priority: 20, - minSize: 20000, - }, - // React-specific chunk - react: { - test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/, - name: "react", - chunks: "all", - priority: 30, - }, - // DFINITY/IC libraries - dfinity: { - test: /[\\/]node_modules[\\/]@dfinity[\\/]/, - name: "dfinity", - chunks: "all", - priority: 25, - }, - // App components in single chunk - appComponents: { - test: /[\\/]src[\\/]frontend[\\/]src[\\/](?!index\.tsx$).*\.tsx$/, - name: "app-components", - chunks: "all", - priority: 15, - }, - // Default chunk for remaining code - default: { - minChunks: 2, - priority: 1, - reuseExistingChunk: true, - }, - }, - }, }, resolve: { extensions: [".js", ".ts", ".jsx", ".tsx"], @@ -122,10 +82,7 @@ module.exports = { }, output: { filename: "[name].js", - chunkFilename: "[name].chunk.js", path: path.join(__dirname, "dist", frontendDirectory), - chunkFormat: "array-push", - crossOriginLoading: "anonymous", clean: true, }, module: { From be2614e8057483607b08d43814ad5ad489e4b2ff Mon Sep 17 00:00:00 2001 From: xqxpx Date: Wed, 29 Apr 2026 11:12:41 +0200 Subject: [PATCH 3/9] make ci --- Dockerfile | 15 +++++++- Makefile | 15 +++++++- README.md | 15 ++++++++ release.sh | 109 +++++++++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 139 insertions(+), 15 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5bce024f..a12d8e90 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=linux/amd64 docker.io/library/ubuntu:22.04 +FROM --platform=linux/amd64 docker.io/library/ubuntu:22.04 AS release ENV NVM_DIR=/root/.nvm ENV NVM_VERSION=v0.39.1 @@ -12,6 +12,8 @@ RUN apt-get -yq update && \ build-essential pkg-config libssl-dev llvm-dev liblmdb-dev clang cmake rsync libunwind-dev jq xz-utils && \ rm -rf /var/lib/apt/lists/* +WORKDIR /app + # Install Node.js COPY .node-version ./ RUN curl --fail -sSf https://raw.githubusercontent.com/creationix/nvm/${NVM_VERSION}/install.sh | bash && \ @@ -36,6 +38,7 @@ RUN mkdir -p /opt/ic-wasm && \ chmod +x /opt/ic-wasm/ic-wasm # Install dfx +ENV HOME=/root COPY dfx.json ./ RUN DFXVM_INIT_YES=1 DFX_VERSION=$(cat dfx.json | jq -r .dfx) sh -ci "$(curl -fsSL https://internetcomputer.org/install.sh)" ENV PATH=${HOME}/.local/share/dfx/bin:${PATH} @@ -47,3 +50,13 @@ RUN npm ci COPY . . ENTRYPOINT [ "./release.sh" ] + +# CI image: same toolchain as release, plus Playwright (Chromium + system deps) +# and the dfx NNS extension pre-installed so e2e setup needs less network. +FROM release AS ci + +RUN npx playwright install chromium --with-deps + +RUN dfx extension install nns --version "$(cat .nns-extension-version | xargs)" + +ENTRYPOINT [ "./release.sh", "ci" ] diff --git a/Makefile b/Makefile index d3b343bd..6e029b5d 100644 --- a/Makefile +++ b/Makefile @@ -70,9 +70,20 @@ podman_machine: CONTAINERS_MACHINE_PROVIDER=qemu podman machine init --cpus 4 --memory 4096 --now release: - $(if $(PODMAN),podman,docker) build -t taggr . + $(if $(PODMAN),podman,docker) build -t taggr --target release . mkdir -p $(shell pwd)/release-artifacts - $(if $(PODMAN),podman,docker) run --rm -v $(shell pwd)/release-artifacts:/target/wasm32-unknown-unknown/release taggr + $(if $(PODMAN),podman,docker) run --rm -v $(shell pwd)/release-artifacts:/app/target/wasm32-unknown-unknown/release taggr + make hashes + +ci: + $(if $(PODMAN),podman,docker) build -t taggr-ci --target ci . + mkdir -p $(shell pwd)/release-artifacts $(shell pwd)/test-results $(shell pwd)/playwright-report + $(if $(PODMAN),podman,docker) run --rm \ + --shm-size=1g \ + -v $(shell pwd)/release-artifacts:/app/target/wasm32-unknown-unknown/release \ + -v $(shell pwd)/test-results:/app/test-results \ + -v $(shell pwd)/playwright-report:/app/playwright-report \ + taggr-ci make hashes hashes: diff --git a/README.md b/README.md index 4dc11595..06b596c9 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,21 @@ To propose a release, follow the steps above first. If they were successful, you'll find a binary `taggr.wasm.gz` in the `release-artifacts` directory. Use the printed code commit and the binary to submit a new release proposal. +## Full validation (lints, tests, e2e, release) + +`make release` only produces and verifies the release wasm. To run the full validation pipeline — lints, Rust tests, end-to-end Playwright tests, and the release verification — in a single containerized command: + + make ci + +This builds an image that extends the release image with Playwright (Chromium + system deps) and the dfx NNS extension, then runs everything inside the container. Both Docker and Podman are supported (`PODMAN=1 make ci`). + +Outputs: + +- `release-artifacts/taggr.wasm.gz` — the production wasm (same artifact as `make release`). +- `test-results/` and `playwright-report/` — Playwright traces, screenshots, and the HTML report (open `playwright-report/index.html` to inspect failures). + +Note: the first run is slow (Chromium install layer is several hundred MB) and `dfx nns install` downloads the NNS canisters on every run. + ## Backups Make sure you have [installed cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html). diff --git a/release.sh b/release.sh index 8bf69a76..3e4ceb0a 100755 --- a/release.sh +++ b/release.sh @@ -1,15 +1,100 @@ -#!/bin/sh +#!/bin/bash +set -eo pipefail export PATH=${HOME}/.local/share/dfx/bin:${PATH} -make build -dfx start --background -dfx deploy -dfx canister info taggr -OUTPUT=$(dfx canister call taggr prod_release) -if [ "$OUTPUT" != "(true)" ]; then - echo "Error: dev feature is enabled!" - exit 1 -fi -dfx stop -cp .dfx/local/canisters/taggr/taggr.wasm.gz target/wasm32-unknown-unknown/release/taggr.wasm.gz +run_release() { + make build + dfx start --background + dfx deploy + dfx canister info taggr + OUTPUT=$(dfx canister call taggr prod_release) + if [ "$OUTPUT" != "(true)" ]; then + echo "Error: dev feature is enabled!" + exit 1 + fi + dfx stop + cp .dfx/local/canisters/taggr/taggr.wasm.gz target/wasm32-unknown-unknown/release/taggr.wasm.gz +} + +prepare_artifacts() { + # Backend src/backend/assets.rs and storage.rs use include_bytes! on + # dist/frontend/* and target/wasm32-unknown-unknown/release/bucket.wasm.gz, + # so cargo cannot compile the backend (host-side, for tests/clippy) until + # the frontend and bucket canister have been built. + echo "==> Building frontend + canisters (prerequisite for cargo lint/test)" + NODE_ENV=production npm run build --quiet + ./build.sh bucket + ./build.sh taggr +} + +run_lints() { + echo "==> Lints" + cargo clippy --tests --benches -- -D clippy::all + cargo fmt --all -- --check + npm run format:check +} + +run_cargo_tests() { + echo "==> Cargo tests" + cargo test -- --test-threads 1 +} + +run_e2e() { + echo "==> e2e: dfx network config (system subnet for NNS)" + mkdir -p "$HOME/.config/dfx" + cat < "$HOME/.config/dfx/networks.json" +{ + "local": { + "bind": "127.0.0.1:8080", + "type": "ephemeral", + "replica": { + "subnet_type": "system" + } + } +} +EOF + + # NNS canisters (sgymv-...) heartbeat-print continuously; filter them out + # from the entire dfx lifecycle. dfx stop must run inside the subshell so + # the daemon dies and grep gets EOF. + ( + echo "==> e2e: dfx start + NNS + canister create" + dfx start --background -qqqq + ./e2e/import_local_minter.sh + dfx nns install + dfx canister create --all + + echo "==> e2e: dev build (needs .dfx/local/canister_ids.json from create)" + NODE_ENV=production DFX_NETWORK=local npm run build --quiet + ./build.sh bucket + FEATURES=dev ./build.sh taggr + + echo "==> e2e: deploy + cycles" + FEATURES=dev dfx deploy + dfx --identity local-minter ledger fabricate-cycles --all --cycles 1000000000000000 + + echo "==> e2e: playwright" + npm run test:e2e + + dfx stop + ) 2>&1 | grep --line-buffered -v 'sgymv' +} + +case "${1:-build}" in + build) + run_release + ;; + ci) + prepare_artifacts + run_lints + run_cargo_tests + run_e2e + rm -rf .dfx + run_release + ;; + *) + echo "unknown mode: $1 (expected: build | ci)" + exit 1 + ;; +esac From 4c87d8309f0775e520f6a49c35536d72a739770e Mon Sep 17 00:00:00 2001 From: xqxpx Date: Wed, 29 Apr 2026 11:15:19 +0200 Subject: [PATCH 4/9] refactor frames --- src/frontend/src/index.tsx | 69 ++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/src/frontend/src/index.tsx b/src/frontend/src/index.tsx index 5bd1ae50..81193844 100644 --- a/src/frontend/src/index.tsx +++ b/src/frontend/src/index.tsx @@ -74,6 +74,46 @@ const parseHash = (): string[] => { const headerRoot = createRoot(document.getElementById("header") as Element); const footerRoot = createRoot(document.getElementById("footer") as Element); const stack = document.getElementById("stack") as HTMLElement; +const stackRoot = createRoot(stack); + +type Frame = { hash: string; key: number; node: React.ReactNode }; +let frames: Frame[] = []; +let frameSeq = 0; +const frameListeners = new Set<() => void>(); +const notifyFrames = () => frameListeners.forEach((l) => l()); + +const FrameStack = () => { + const fs = React.useSyncExternalStore( + (cb) => { + frameListeners.add(cb); + return () => { + frameListeners.delete(cb); + }; + }, + () => frames, + ); + return ( + <> + {fs.map((f, i) => ( +
+ {f.node} +
+ ))} + + ); +}; + +stackRoot.render( + + + , +); const renderFrame = (content: React.ReactNode) => { document.getElementById("logo_container")?.remove(); @@ -85,21 +125,16 @@ const renderFrame = (content: React.ReactNode) => { return; } - const frames = Array.from(stack.children as HTMLCollectionOf); - frames.forEach((e) => (e.style.display = "none")); - const currentFrame = frames[frames.length - 1]; - const lastFrame = frames[frames.length - 2]; - - if (lastFrame && lastFrame.dataset.hash == location.hash) { - currentFrame.remove(); - lastFrame.style.display = "block"; - return; + const last = frames[frames.length - 2]; + if (last && last.hash == location.hash) { + frames = frames.slice(0, -1); + } else { + frames = [ + ...frames, + { hash: location.hash, key: frameSeq++, node: content }, + ]; } - - let frame = document.createElement("div"); - frame.dataset.hash = location.hash; - stack.appendChild(frame); - createRoot(frame).render(content); + notifyFrames(); }; const App = () => { @@ -274,7 +309,7 @@ const App = () => { /> , ); - renderFrame({content}); + renderFrame(content); }; const reloadCache = async () => { @@ -421,8 +456,8 @@ const bootstrap = async () => { window.setUI = setUI; window.resetUI = () => { window.uiInitialized = false; - const frames = Array.from(stack.children); - frames.forEach((frame) => frame.remove()); + frames = []; + notifyFrames(); }; const futures = [reloadCache()]; From 517b46d9b1f8204f4404b2f62b0bfd222c206d0c Mon Sep 17 00:00:00 2001 From: xqxpx Date: Wed, 29 Apr 2026 11:29:21 +0200 Subject: [PATCH 5/9] minor fix --- src/frontend/src/header.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/header.tsx b/src/frontend/src/header.tsx index 7066ba64..92699cc2 100644 --- a/src/frontend/src/header.tsx +++ b/src/frontend/src/header.tsx @@ -212,7 +212,7 @@ const UserSection = ({ user }: { user: UserType }) => { className="icon_link" href={`/#/user/${user.name}`} > - {user.name.toUpperCase()} + {user.name.toUpperCase()} Date: Wed, 29 Apr 2026 11:38:21 +0200 Subject: [PATCH 6/9] make test + make release --- Dockerfile | 12 ++++-------- Makefile | 19 +++++++++++-------- README.md | 24 +++++++++++++----------- release.sh | 24 +++++++++++++++--------- 4 files changed, 43 insertions(+), 36 deletions(-) diff --git a/Dockerfile b/Dockerfile index a12d8e90..f0cdb646 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=linux/amd64 docker.io/library/ubuntu:22.04 AS release +FROM --platform=linux/amd64 docker.io/library/ubuntu:22.04 ENV NVM_DIR=/root/.nvm ENV NVM_VERSION=v0.39.1 @@ -49,14 +49,10 @@ RUN npm ci COPY . . -ENTRYPOINT [ "./release.sh" ] - -# CI image: same toolchain as release, plus Playwright (Chromium + system deps) -# and the dfx NNS extension pre-installed so e2e setup needs less network. -FROM release AS ci - +# Test deps: Playwright (Chromium + system libs) and dfx NNS extension. +# Both are needed by the e2e step that gates every release build. RUN npx playwright install chromium --with-deps RUN dfx extension install nns --version "$(cat .nns-extension-version | xargs)" -ENTRYPOINT [ "./release.sh", "ci" ] +ENTRYPOINT [ "./release.sh" ] diff --git a/Makefile b/Makefile index 6e029b5d..01dd13c5 100644 --- a/Makefile +++ b/Makefile @@ -69,21 +69,24 @@ podman_machine: podman machine rm -f || true CONTAINERS_MACHINE_PROVIDER=qemu podman machine init --cpus 4 --memory 4096 --now -release: - $(if $(PODMAN),podman,docker) build -t taggr --target release . - mkdir -p $(shell pwd)/release-artifacts - $(if $(PODMAN),podman,docker) run --rm -v $(shell pwd)/release-artifacts:/app/target/wasm32-unknown-unknown/release taggr - make hashes +tests: + $(if $(PODMAN),podman,docker) build -t taggr . + mkdir -p $(shell pwd)/test-results $(shell pwd)/playwright-report + $(if $(PODMAN),podman,docker) run --rm \ + --shm-size=1g \ + -v $(shell pwd)/test-results:/app/test-results \ + -v $(shell pwd)/playwright-report:/app/playwright-report \ + taggr tests -ci: - $(if $(PODMAN),podman,docker) build -t taggr-ci --target ci . +release: + $(if $(PODMAN),podman,docker) build -t taggr . mkdir -p $(shell pwd)/release-artifacts $(shell pwd)/test-results $(shell pwd)/playwright-report $(if $(PODMAN),podman,docker) run --rm \ --shm-size=1g \ -v $(shell pwd)/release-artifacts:/app/target/wasm32-unknown-unknown/release \ -v $(shell pwd)/test-results:/app/test-results \ -v $(shell pwd)/playwright-report:/app/playwright-report \ - taggr-ci + taggr make hashes hashes: diff --git a/README.md b/README.md index 06b596c9..2a4a1314 100644 --- a/README.md +++ b/README.md @@ -11,26 +11,28 @@ Assume you want to verify a new upgrade proposal with code commit `` and 4. `make release` 5. Verify that the printed hash matches the `` value from the release page. +`make release` runs the full validation pipeline (lints, Rust tests, Playwright e2e) inside the container and only produces a hash if everything passes. A failing release therefore cannot be hashed — the printed hash is a signal that the wasm is both reproducible and tested. Both Docker and Podman are supported (`PODMAN=1 make release`). + +Outputs of a successful run: + +- `release-artifacts/taggr.wasm.gz` — the production wasm. +- `test-results/` and `playwright-report/` — Playwright traces and the HTML report (open `playwright-report/index.html` to inspect any failures). + +Note: the first run is slow (Chromium install layer is several hundred MB) and `dfx nns install` downloads the NNS canisters on every run. + ## Release proposal To propose a release, follow the steps above first. If they were successful, you'll find a binary `taggr.wasm.gz` in the `release-artifacts` directory. Use the printed code commit and the binary to submit a new release proposal. -## Full validation (lints, tests, e2e, release) +## Running tests during development -`make release` only produces and verifies the release wasm. To run the full validation pipeline — lints, Rust tests, end-to-end Playwright tests, and the release verification — in a single containerized command: +For day-to-day iteration, skip the prod build and just run the test suite: - make ci + make tests -This builds an image that extends the release image with Playwright (Chromium + system deps) and the dfx NNS extension, then runs everything inside the container. Both Docker and Podman are supported (`PODMAN=1 make ci`). - -Outputs: - -- `release-artifacts/taggr.wasm.gz` — the production wasm (same artifact as `make release`). -- `test-results/` and `playwright-report/` — Playwright traces, screenshots, and the HTML report (open `playwright-report/index.html` to inspect failures). - -Note: the first run is slow (Chromium install layer is several hundred MB) and `dfx nns install` downloads the NNS canisters on every run. +Same image as `make release` and the same checks (lints, Rust tests, Playwright e2e), but stops before the deterministic prod build. Use this while iterating; use `make release` when you actually want a hash. ## Backups diff --git a/release.sh b/release.sh index 3e4ceb0a..650ec5e3 100755 --- a/release.sh +++ b/release.sh @@ -81,20 +81,26 @@ EOF ) 2>&1 | grep --line-buffered -v 'sgymv' } -case "${1:-build}" in - build) - run_release +run_tests() { + prepare_artifacts + run_lints + run_cargo_tests + run_e2e +} + +case "${1:-release}" in + tests) + run_tests ;; - ci) - prepare_artifacts - run_lints - run_cargo_tests - run_e2e + release) + # Tests gate the release: a failure here aborts before run_release thanks + # to set -e, so a hash is only ever produced for a fully-tested build. + run_tests rm -rf .dfx run_release ;; *) - echo "unknown mode: $1 (expected: build | ci)" + echo "unknown mode: $1 (expected: tests | release)" exit 1 ;; esac From 538f71e870f3c66b5ef592810505893f94e02fee Mon Sep 17 00:00:00 2001 From: xqxpx Date: Wed, 29 Apr 2026 11:44:45 +0200 Subject: [PATCH 7/9] remove gh files --- .github/actions/setup-build-deps/action.yml | 30 -------- .github/actions/setup-dfx/action.yml | 45 ------------ .github/actions/setup-nodejs/action.yml | 10 --- .github/workflows/docker-image.yml | 18 ----- .github/workflows/e2e-tests.yml | 77 --------------------- .github/workflows/lint-and-test.yml | 38 ---------- 6 files changed, 218 deletions(-) delete mode 100644 .github/actions/setup-build-deps/action.yml delete mode 100644 .github/actions/setup-dfx/action.yml delete mode 100644 .github/actions/setup-nodejs/action.yml delete mode 100644 .github/workflows/docker-image.yml delete mode 100644 .github/workflows/e2e-tests.yml delete mode 100644 .github/workflows/lint-and-test.yml diff --git a/.github/actions/setup-build-deps/action.yml b/.github/actions/setup-build-deps/action.yml deleted file mode 100644 index a1fb456a..00000000 --- a/.github/actions/setup-build-deps/action.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: "Setup Build Dependencies" -description: Setup Build Dependencies - -runs: - using: "composite" - steps: - - name: Get IC WASM version - shell: bash - run: echo "ic_wasm_version=$(cat .ic-wasm-version | xargs)" >> "$GITHUB_ENV" - - - name: Cache IC WASM - uses: actions/cache@v4 - with: - path: /usr/local/bin/ic-wasm - key: ic-wasm-cache-${{ env.ic_wasm_version }} - - - name: Install IC WASM - shell: bash - run: | - if command -v ic-wasm - then - echo "IC WASM restored from cache" - else - echo "IC WASM not restored from cache, downloading:" - curl -L https://github.com/dfinity/ic-wasm/releases/download/${{ env.ic_wasm_version }}/ic-wasm-x86_64-unknown-linux-gnu.tar.xz \ - | tar -xJ --strip-components=1 -C /usr/local/bin ic-wasm-x86_64-unknown-linux-gnu/ic-wasm - chmod +x /usr/local/bin/ic-wasm - fi - echo "IC WASM version" - ic-wasm --version diff --git a/.github/actions/setup-dfx/action.yml b/.github/actions/setup-dfx/action.yml deleted file mode 100644 index 6a7b0e19..00000000 --- a/.github/actions/setup-dfx/action.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: "Setup DFX" -description: Setup DFX - -runs: - using: "composite" - steps: - - name: Get DFX version - shell: bash - run: echo "dfx_version=$(cat dfx.json | jq -r .dfx)" >> "$GITHUB_ENV" - - - name: Cache DFX - uses: actions/cache@v4 - with: - path: /usr/local/bin/dfx - key: dfx-cache-${{ env.dfx_version }} - - - name: Install DFX - shell: bash - run: | - if command -v dfx - then - echo "DFX restored from cache" - else - echo "DFX not restored from cache, running install script:" - DFXVM_INIT_YES=1 DFX_VERSION=${{ env.dfx_version }} sh -ci "$(curl -fsSL https://sdk.dfinity.org/install.sh)" - fi - echo "DFX version" - source "$HOME/.local/share/dfx/env" - dfx --version - - - name: Configure DFX - shell: bash - run: | - mkdir -p $HOME/.config/dfx - cat <$HOME/.config/dfx/networks.json - { - "local": { - "bind": "127.0.0.1:8080", - "type": "ephemeral", - "replica": { - "subnet_type": "system" - } - } - } - EOF diff --git a/.github/actions/setup-nodejs/action.yml b/.github/actions/setup-nodejs/action.yml deleted file mode 100644 index 701c6bb0..00000000 --- a/.github/actions/setup-nodejs/action.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: "Setup nodejs" -description: Setup nodejs - -runs: - using: "composite" - steps: - - uses: actions/setup-node@v4 - with: - node-version-file: ".node-version" - cache: "npm" diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml deleted file mode 100644 index 3f893463..00000000 --- a/.github/workflows/docker-image.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Docker Image CI - -on: - push: - branches: ["main"] - pull_request: - branches: ["main"] - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Build the release binary image - run: make release - - name: Print the git commit and the binary hash - run: make hashes diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml deleted file mode 100644 index 964ccdcc..00000000 --- a/.github/workflows/e2e-tests.yml +++ /dev/null @@ -1,77 +0,0 @@ -name: e2e Tests - -on: - push: - branches: ["main"] - pull_request: - branches: ["main"] - -jobs: - e2e_tests: - runs-on: ubuntu-latest - steps: - - name: Add dfx to PATH - run: echo "$HOME/.local/share/dfx/bin" >> $GITHUB_PATH - - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-nodejs - - uses: ./.github/actions/setup-dfx - - uses: ./.github/actions/setup-build-deps - - - name: Get Playwright version - id: playwright-version - run: echo "playwright_version=$(cat package-lock.json | jq -r '.dependencies."@playwright/test".version')" >> $GITHUB_ENV - - - name: Cache Playwright dependencies - uses: actions/cache@v4 - id: playwright-cache - with: - path: ~/.cache/ms-playwright - key: ${{ runner.os }}-playwright-${{ env.playwright_version }} - - - name: Install NPM dependencies - run: npm ci - - - name: Install Playwright dependencies - if: steps.playwright-cache.outputs.cache-hit != 'true' - run: npm run install:e2e - - - name: Start DFX - run: dfx start --background - - - name: Import local minter - run: ./e2e/import_local_minter.sh - - - name: Get NNS extension version - shell: bash - run: echo "nns_extension_version=$(cat .nns-extension-version | xargs)" >> "$GITHUB_ENV" - - - name: Set up NNS canisters - run: | - dfx extension install nns --version ${{ env.nns_extension_version }} - dfx nns install - - - name: Deploy canister - run: | - dfx canister create --all - make e2e_build - make local_deploy - - - name: Top up canisters - run: dfx --identity local-minter ledger fabricate-cycles --all --cycles 1000000000000000 - - - name: Run e2e tests - run: npm run test:e2e - - - name: Upload test results - uses: actions/upload-artifact@v4 - if: ${{ !cancelled() }} - with: - name: test-results - path: | - test-results/ - playwright-report/ - retention-days: 7 - - - name: Stop DFX - run: dfx stop diff --git a/.github/workflows/lint-and-test.yml b/.github/workflows/lint-and-test.yml deleted file mode 100644 index be42afc0..00000000 --- a/.github/workflows/lint-and-test.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Lint and Test - -on: - push: - branches: ["main"] - pull_request: - branches: ["main"] - -jobs: - lint_and_test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-nodejs - - uses: ./.github/actions/setup-build-deps - - - name: Install NPM dependencies - run: npm ci - - - name: Build frontend - run: NODE_ENV=production npm run build --quiet - - - name: Build canister - run: | - ./build.sh bucket - ./build.sh taggr - - - name: Rust lint - run: cargo clippy --tests --benches -- -D clippy::all - - - name: Rust test - run: cargo test -- --test-threads 1 - - - name: Check Rust formatting - run: cargo fmt --all -- --check - - - name: Check Frontend formatting - run: npm run format:check From b2e2b55750908f377d3ae66b133d5331c61e0943 Mon Sep 17 00:00:00 2001 From: xqxpx Date: Wed, 29 Apr 2026 11:52:12 +0200 Subject: [PATCH 8/9] detect podman --- Makefile | 11 +++++++---- README.md | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 01dd13c5..2e2b05df 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,6 @@ +# Prefer podman if installed, else fall back to docker. Override with CONTAINER=docker. +CONTAINER ?= $(shell command -v podman >/dev/null 2>&1 && echo podman || echo docker) + start: ulimit -n 65000 && dfx start --background -qqqq 2>&1 | grep -v sgymv & @@ -70,18 +73,18 @@ podman_machine: CONTAINERS_MACHINE_PROVIDER=qemu podman machine init --cpus 4 --memory 4096 --now tests: - $(if $(PODMAN),podman,docker) build -t taggr . + $(CONTAINER) build -t taggr . mkdir -p $(shell pwd)/test-results $(shell pwd)/playwright-report - $(if $(PODMAN),podman,docker) run --rm \ + $(CONTAINER) run --rm \ --shm-size=1g \ -v $(shell pwd)/test-results:/app/test-results \ -v $(shell pwd)/playwright-report:/app/playwright-report \ taggr tests release: - $(if $(PODMAN),podman,docker) build -t taggr . + $(CONTAINER) build -t taggr . mkdir -p $(shell pwd)/release-artifacts $(shell pwd)/test-results $(shell pwd)/playwright-report - $(if $(PODMAN),podman,docker) run --rm \ + $(CONTAINER) run --rm \ --shm-size=1g \ -v $(shell pwd)/release-artifacts:/app/target/wasm32-unknown-unknown/release \ -v $(shell pwd)/test-results:/app/test-results \ diff --git a/README.md b/README.md index 2a4a1314..1683565a 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Assume you want to verify a new upgrade proposal with code commit `` and 4. `make release` 5. Verify that the printed hash matches the `` value from the release page. -`make release` runs the full validation pipeline (lints, Rust tests, Playwright e2e) inside the container and only produces a hash if everything passes. A failing release therefore cannot be hashed — the printed hash is a signal that the wasm is both reproducible and tested. Both Docker and Podman are supported (`PODMAN=1 make release`). +`make release` runs the full validation pipeline (lints, Rust tests, Playwright e2e) inside the container and only produces a hash if everything passes. A failing release therefore cannot be hashed — the printed hash is a signal that the wasm is both reproducible and tested. Podman is used automatically if installed; otherwise Docker. Override with `CONTAINER=docker make release`. Outputs of a successful run: From 73693403698d3990ee2bbc2b58d88e2b9939d909 Mon Sep 17 00:00:00 2001 From: xqxpx Date: Wed, 29 Apr 2026 12:15:30 +0200 Subject: [PATCH 9/9] remove nns dependency from e2e tests --- .gitignore | 2 ++ .icp-ledger-version | 1 + .nns-extension-version | 1 - Dockerfile | 5 +-- Makefile | 10 +++--- dfx.json | 13 +++++++ docs/LOCAL_DEVELOPMENT.md | 24 ++++--------- e2e/install_icp_ledger.sh | 56 +++++++++++++++++++++++++++++ release.sh | 72 +++++++++++++++---------------------- src/frontend/src/common.tsx | 2 +- 10 files changed, 114 insertions(+), 72 deletions(-) create mode 100644 .icp-ledger-version delete mode 100644 .nns-extension-version create mode 100755 e2e/install_icp_ledger.sh diff --git a/.gitignore b/.gitignore index ce566dbd..1375e84d 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,8 @@ dist/ # e2e tests test-results/ playwright-report/ +e2e/icp_ledger/ledger.wasm.gz +e2e/icp_ledger/ledger.did # reviews /reviews diff --git a/.icp-ledger-version b/.icp-ledger-version new file mode 100644 index 00000000..ce0af95a --- /dev/null +++ b/.icp-ledger-version @@ -0,0 +1 @@ +920df8055260f443ac3335cc0f2b06e285a688b4 diff --git a/.nns-extension-version b/.nns-extension-version deleted file mode 100644 index 7d856835..00000000 --- a/.nns-extension-version +++ /dev/null @@ -1 +0,0 @@ -0.5.4 diff --git a/Dockerfile b/Dockerfile index f0cdb646..684c18eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -49,10 +49,7 @@ RUN npm ci COPY . . -# Test deps: Playwright (Chromium + system libs) and dfx NNS extension. -# Both are needed by the e2e step that gates every release build. +# Test deps: Playwright (Chromium + system libs) for the e2e step. RUN npx playwright install chromium --with-deps -RUN dfx extension install nns --version "$(cat .nns-extension-version | xargs)" - ENTRYPOINT [ "./release.sh" ] diff --git a/Makefile b/Makefile index 2e2b05df..101b7e93 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ CONTAINER ?= $(shell command -v podman >/dev/null 2>&1 && echo podman || echo docker) start: - ulimit -n 65000 && dfx start --background -qqqq 2>&1 | grep -v sgymv & + ulimit -n 65000 && dfx start --background -qqqq & cycles: dfx --identity local-minter ledger fabricate-cycles --all --cycles 1000000000000000 @@ -13,7 +13,7 @@ staging_deploy: FEATURES=staging dfx --identity prod deploy --network $(if $(CANISTER),$(CANISTER),staging) taggr local_deploy: - FEATURES=dev dfx deploy + FEATURES=dev dfx deploy taggr dev_build: FEATURES=dev ./build.sh bucket @@ -62,6 +62,8 @@ e2e_build: e2e_test: npm run install:e2e dfx canister create --all + ./e2e/import_local_minter.sh + ./e2e/install_icp_ledger.sh make e2e_build make start || true # don't fail if DFX is already running npm run test:e2e @@ -73,7 +75,7 @@ podman_machine: CONTAINERS_MACHINE_PROVIDER=qemu podman machine init --cpus 4 --memory 4096 --now tests: - $(CONTAINER) build -t taggr . + $(CONTAINER) build --quiet -t taggr . >/dev/null mkdir -p $(shell pwd)/test-results $(shell pwd)/playwright-report $(CONTAINER) run --rm \ --shm-size=1g \ @@ -82,7 +84,7 @@ tests: taggr tests release: - $(CONTAINER) build -t taggr . + $(CONTAINER) build --quiet -t taggr . >/dev/null mkdir -p $(shell pwd)/release-artifacts $(shell pwd)/test-results $(shell pwd)/playwright-report $(CONTAINER) run --rm \ --shm-size=1g \ diff --git a/dfx.json b/dfx.json index bfe01aa8..0f6dba12 100644 --- a/dfx.json +++ b/dfx.json @@ -13,6 +13,19 @@ "networks": ["local", "ic", "staging", "staging2"] } ] + }, + "icp_ledger": { + "type": "custom", + "candid": "e2e/icp_ledger/ledger.did", + "wasm": "e2e/icp_ledger/ledger.wasm.gz", + "specified_id": "ryjl3-tyaaa-aaaaa-aaaba-cai", + "remote": { + "id": { + "ic": "ryjl3-tyaaa-aaaaa-aaaba-cai", + "staging": "ryjl3-tyaaa-aaaaa-aaaba-cai", + "staging2": "ryjl3-tyaaa-aaaaa-aaaba-cai" + } + } } }, "networks": { diff --git a/docs/LOCAL_DEVELOPMENT.md b/docs/LOCAL_DEVELOPMENT.md index d204d578..b8290cf2 100644 --- a/docs/LOCAL_DEVELOPMENT.md +++ b/docs/LOCAL_DEVELOPMENT.md @@ -45,21 +45,7 @@ Install DFX DFX_VERSION=$(cat dfx.json | jq -r .dfx) sh -ci "$(curl -fsSL https://internetcomputer.org/install.sh)" ``` -The remaining steps are only necessary for deploying NNS canisters locally. This makes it easier to test new account creation with Internet Identity, to make ICP transfers to those accounts or to run Taggr e2e tests without a Docker container. Alternatively, you can [create a backup](#creating-and-restoring-backups) and then refer to the [command reference](#command-reference) to build and deploy. - -Create or edit `~/.config/dfx/networks.json`, and add the following, note that `dfx install` requires port `8080` to work: - -```json -{ - "local": { - "bind": "127.0.0.1:8080", - "type": "ephemeral", - "replica": { - "subnet_type": "system" - } - } -} -``` +The remaining steps are only necessary for deploying the local ICP ledger canister. This makes it easier to test new account creation with Internet Identity, to make ICP transfers to those accounts or to run Taggr e2e tests without a Docker container. Alternatively, you can [create a backup](#creating-and-restoring-backups) and then refer to the [command reference](#command-reference) to build and deploy. Stop DFX if it's running: @@ -84,11 +70,13 @@ make local_reinstall Use `make cycles` to fabricate cycles for the canister. -Install NNS canisters (see the [DFX docs](https://github.com/dfinity/sdk/blob/master/docs/cli-reference/dfx-nns.md)): +Install the ICP ledger canister at its mainnet ID locally (the backend +hard-codes `MAINNET_LEDGER_CANISTER_ID`, so the ledger has to answer at +`ryjl3-tyaaa-aaaaa-aaaba-cai`): ```shell -dfx extension install nns -dfx nns install +./e2e/import_local_minter.sh +./e2e/install_icp_ledger.sh ``` Now you are ready to create a new Taggr account with Internet Identity locally. If you also want to make ICP transfers to this account then continue with the remaining steps, the remaining steps are not necessary for running e2e tests. diff --git a/e2e/install_icp_ledger.sh b/e2e/install_icp_ledger.sh new file mode 100755 index 00000000..d2c92005 --- /dev/null +++ b/e2e/install_icp_ledger.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# Install the ICP ledger canister at its mainnet ID (ryjl3-tyaaa-aaaaa-aaaba-cai) +# on the local replica. The backend hard-codes that principal via +# ic-ledger-types::MAINNET_LEDGER_CANISTER_ID, so e2e tests need a real ledger +# answering at that exact ID. The dfx.json `specified_id` pin makes that work +# without `dfx nns install` and without a `system` subnet. +set -euo pipefail + +VERSION="$(cat .icp-ledger-version | xargs)" +DIR="e2e/icp_ledger" +WASM="${DIR}/ledger.wasm.gz" +DID="${DIR}/ledger.did" + +mkdir -p "${DIR}" + +if [ ! -s "${WASM}" ]; then + curl -fsSL \ + "https://download.dfinity.systems/ic/${VERSION}/canisters/ledger-canister.wasm.gz" \ + -o "${WASM}" +fi + +if [ ! -s "${DID}" ]; then + # Repo path moved mid-2024; try the new layout first, fall back to the old one. + curl -fsSL \ + "https://raw.githubusercontent.com/dfinity/ic/${VERSION}/rs/ledger_suite/icp/ledger.did" \ + -o "${DID}" \ + || curl -fsSL \ + "https://raw.githubusercontent.com/dfinity/ic/${VERSION}/rs/rosetta-api/icp_ledger/ledger.did" \ + -o "${DID}" +fi + +# The ledger forbids fee>0 on transfers from the minting_account, so the +# test identity (local-minter) cannot be the minting_account — `transferICP` +# in e2e tests sends regular fee-paying transfers via `dfx ledger transfer`, +# which uses the default 10_000 e8s fee. Use a separate `minter` identity as +# the minting_account and pre-fund local-minter via initial_values instead. +if ! dfx identity get-principal --identity minter >/dev/null 2>&1; then + dfx identity new minter --storage-mode=plaintext +fi + +MINTER_ACCOUNT=$(dfx ledger account-id --identity minter) +LOCAL_MINTER_ACCOUNT=$(dfx ledger account-id --identity local-minter) + +# --mode reinstall so re-running this script (e.g. between e2e iterations) +# wipes ledger state and re-applies the Init args instead of attempting an +# upgrade with an Init-shaped payload. +dfx deploy icp_ledger --mode reinstall -y --argument "(variant { Init = record { + minting_account = \"${MINTER_ACCOUNT}\"; + initial_values = vec { + record { \"${LOCAL_MINTER_ACCOUNT}\"; record { e8s = 100_000_000_000_000 : nat64 } }; + }; + send_whitelist = vec {}; + transfer_fee = opt record { e8s = 10_000 : nat64 }; + token_symbol = opt \"ICP\"; + token_name = opt \"Internet Computer\"; +} })" diff --git a/release.sh b/release.sh index 650ec5e3..d28b3feb 100755 --- a/release.sh +++ b/release.sh @@ -6,7 +6,7 @@ export PATH=${HOME}/.local/share/dfx/bin:${PATH} run_release() { make build dfx start --background - dfx deploy + dfx deploy taggr dfx canister info taggr OUTPUT=$(dfx canister call taggr prod_release) if [ "$OUTPUT" != "(true)" ]; then @@ -22,63 +22,47 @@ prepare_artifacts() { # dist/frontend/* and target/wasm32-unknown-unknown/release/bucket.wasm.gz, # so cargo cannot compile the backend (host-side, for tests/clippy) until # the frontend and bucket canister have been built. - echo "==> Building frontend + canisters (prerequisite for cargo lint/test)" - NODE_ENV=production npm run build --quiet - ./build.sh bucket - ./build.sh taggr + echo "==> [1/7] Building frontend + canisters (prerequisite for cargo lint/test)" + NODE_ENV=production npm run build --quiet >/dev/null 2>&1 + ./build.sh bucket >/dev/null 2>&1 + ./build.sh taggr >/dev/null 2>&1 } run_lints() { - echo "==> Lints" - cargo clippy --tests --benches -- -D clippy::all + echo "==> [2/7] Lints" + cargo clippy -q --tests --benches -- -D clippy::all cargo fmt --all -- --check - npm run format:check + npm run format:check --silent } run_cargo_tests() { - echo "==> Cargo tests" - cargo test -- --test-threads 1 + echo "==> [3/7] Cargo tests" + cargo test -q -- --test-threads 1 } run_e2e() { - echo "==> e2e: dfx network config (system subnet for NNS)" - mkdir -p "$HOME/.config/dfx" - cat < "$HOME/.config/dfx/networks.json" -{ - "local": { - "bind": "127.0.0.1:8080", - "type": "ephemeral", - "replica": { - "subnet_type": "system" - } - } -} -EOF - - # NNS canisters (sgymv-...) heartbeat-print continuously; filter them out - # from the entire dfx lifecycle. dfx stop must run inside the subshell so - # the daemon dies and grep gets EOF. - ( - echo "==> e2e: dfx start + NNS + canister create" - dfx start --background -qqqq - ./e2e/import_local_minter.sh - dfx nns install - dfx canister create --all + # Silence the dfx/build chatter to keep CI output (and Claude transcripts) + # readable — only stage markers and the playwright run itself print. Re-run + # with `bash -x` or remove the `>/dev/null 2>&1` redirects to debug. + echo "==> [4/7] e2e: dfx start + ICP ledger + canister create" + dfx start --background -qqqq >/dev/null 2>&1 + ./e2e/import_local_minter.sh >/dev/null 2>&1 + dfx canister create --all >/dev/null 2>&1 + ./e2e/install_icp_ledger.sh >/dev/null 2>&1 - echo "==> e2e: dev build (needs .dfx/local/canister_ids.json from create)" - NODE_ENV=production DFX_NETWORK=local npm run build --quiet - ./build.sh bucket - FEATURES=dev ./build.sh taggr + echo "==> [5/7] e2e: dev build (needs .dfx/local/canister_ids.json from create)" + NODE_ENV=production DFX_NETWORK=local npm run build --quiet >/dev/null 2>&1 + ./build.sh bucket >/dev/null 2>&1 + FEATURES=dev ./build.sh taggr >/dev/null 2>&1 - echo "==> e2e: deploy + cycles" - FEATURES=dev dfx deploy - dfx --identity local-minter ledger fabricate-cycles --all --cycles 1000000000000000 + echo "==> [6/7] e2e: deploy + cycles" + FEATURES=dev dfx deploy taggr >/dev/null 2>&1 + dfx --identity local-minter ledger fabricate-cycles --all --cycles 1000000000000000 >/dev/null 2>&1 - echo "==> e2e: playwright" - npm run test:e2e + echo "==> [7/7] e2e: playwright" + npm run test:e2e - dfx stop - ) 2>&1 | grep --line-buffered -v 'sgymv' + dfx stop >/dev/null 2>&1 } run_tests() { diff --git a/src/frontend/src/common.tsx b/src/frontend/src/common.tsx index 7051d590..c3dddf28 100755 --- a/src/frontend/src/common.tsx +++ b/src/frontend/src/common.tsx @@ -1183,7 +1183,7 @@ export const showPopUp = ( export const signOut = async () => { localStorage.clear(); sessionStorage.clear(); - window.authClient.logout(); + await window.authClient.logout(); restartApp(); return true; };