From 5a1ae372dcea4bf4c2ea8f1fbf0028ac7ecc4d49 Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Fri, 5 Jun 2026 18:05:31 +0200 Subject: [PATCH] refactor: simplify visual regression dispatch Use a single event_type=visual-regression for all Argos projects. ci-privileged routes based on project/artifact_name in the payload. Collapsed from 5 dispatch jobs to 4 (twenty-ui, twenty-new-ui, comparison-baseline, comparison-pr). Removed cloud/self-hosted distinction since everything targets the same self-hosted Argos. --- .../workflows/visual-regression-dispatch.yaml | 81 ++++++------------- packages/twenty-new-ui/README.md | 6 +- 2 files changed, 29 insertions(+), 58 deletions(-) diff --git a/.github/workflows/visual-regression-dispatch.yaml b/.github/workflows/visual-regression-dispatch.yaml index bb5642d27e0..6447f031024 100644 --- a/.github/workflows/visual-regression-dispatch.yaml +++ b/.github/workflows/visual-regression-dispatch.yaml @@ -4,11 +4,8 @@ name: Visual Regression Dispatch # Runs in the context of the base repo (not the fork) so it has access to secrets, # making it work for external contributor PRs. # -# Handles three Argos projects: -# 1. Self-hosted (twenty-ui pixel diff) — existing, via visual-regression event -# 2. Cloud (twenty-new-ui pixel diff) — via visual-regression-cloud event -# 3. Cloud (twenty-ui vs twenty-new-ui comparison) — via visual-regression-cloud event -# Baseline: twenty-ui screenshots on main; comparison: twenty-new-ui on PRs +# All dispatches use the same event_type=visual-regression with project/artifact_name +# in the payload. ci-privileged routes to the correct Argos project based on these. on: workflow_run: @@ -21,7 +18,6 @@ permissions: pull-requests: read jobs: - # ── Identify which workflow triggered and what artifact to look for ── resolve-context: if: github.event.workflow_run.conclusion == 'success' runs-on: ubuntu-latest @@ -138,18 +134,16 @@ jobs: core.setOutput('sha', ''); } - # ── Self-hosted Argos: twenty-ui pixel diff (existing behavior, unchanged) ── - dispatch-self-hosted-pr: + # ── Dispatch: twenty-ui pixel diff (CI UI, PRs + main) ── + dispatch-twenty-ui: needs: resolve-context if: >- needs.resolve-context.outputs.workflow_name == 'CI UI' && - needs.resolve-context.outputs.has_artifact == 'true' && - needs.resolve-context.outputs.is_pr == 'true' && - github.event.workflow_run.event == 'pull_request' + needs.resolve-context.outputs.has_artifact == 'true' runs-on: ubuntu-latest timeout-minutes: 5 steps: - - name: Dispatch to ci-privileged (self-hosted) + - name: Dispatch to ci-privileged env: GH_TOKEN: ${{ secrets.CI_PRIVILEGED_DISPATCH_TOKEN }} PR_NUMBER: ${{ needs.resolve-context.outputs.pr_number }} @@ -158,51 +152,30 @@ jobs: BRANCH: ${{ github.event.workflow_run.head_branch }} COMMIT: ${{ github.event.workflow_run.head_sha }} REFERENCE_COMMIT: ${{ needs.resolve-context.outputs.merge_base_sha }} + ARTIFACT_NAME: ${{ needs.resolve-context.outputs.artifact_name }} run: | ARGS=( --method POST -f event_type=visual-regression - -f "client_payload[pr_number]=$PR_NUMBER" + -f "client_payload[project]=twenty-ui" + -f "client_payload[artifact_name]=$ARTIFACT_NAME" -f "client_payload[run_id]=$WORKFLOW_RUN_ID" -f "client_payload[repo]=$REPOSITORY" -f "client_payload[branch]=$BRANCH" -f "client_payload[commit]=$COMMIT" ) + if [ -n "$PR_NUMBER" ]; then + ARGS+=(-f "client_payload[pr_number]=$PR_NUMBER") + fi if [ -n "$REFERENCE_COMMIT" ]; then ARGS+=(-f "client_payload[reference_commit]=$REFERENCE_COMMIT") fi gh api repos/twentyhq/ci-privileged/dispatches "${ARGS[@]}" - dispatch-self-hosted-main: - needs: resolve-context - if: >- - needs.resolve-context.outputs.workflow_name == 'CI UI' && - needs.resolve-context.outputs.has_artifact == 'true' && - github.event.workflow_run.event == 'push' && - github.event.workflow_run.head_branch == 'main' - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - name: Dispatch to ci-privileged (self-hosted) - env: - GH_TOKEN: ${{ secrets.CI_PRIVILEGED_DISPATCH_TOKEN }} - WORKFLOW_RUN_ID: ${{ github.event.workflow_run.id }} - REPOSITORY: ${{ github.repository }} - BRANCH: ${{ github.event.workflow_run.head_branch }} - COMMIT: ${{ github.event.workflow_run.head_sha }} - run: | - gh api repos/twentyhq/ci-privileged/dispatches \ - --method POST \ - -f event_type=visual-regression \ - -f "client_payload[run_id]=$WORKFLOW_RUN_ID" \ - -f "client_payload[repo]=$REPOSITORY" \ - -f "client_payload[branch]=$BRANCH" \ - -f "client_payload[commit]=$COMMIT" - - # ── Cloud Argos: twenty-new-ui pixel diff ── - dispatch-cloud-new-ui: + # ── Dispatch: twenty-new-ui pixel diff (CI New UI, PRs + main) ── + dispatch-twenty-new-ui: needs: resolve-context if: >- needs.resolve-context.outputs.workflow_name == 'CI New UI' && @@ -210,7 +183,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - name: Dispatch to ci-privileged (cloud — twenty-new-ui pixel diff) + - name: Dispatch to ci-privileged env: GH_TOKEN: ${{ secrets.CI_PRIVILEGED_DISPATCH_TOKEN }} WORKFLOW_RUN_ID: ${{ github.event.workflow_run.id }} @@ -223,7 +196,7 @@ jobs: run: | ARGS=( --method POST - -f event_type=visual-regression-cloud + -f event_type=visual-regression -f "client_payload[project]=twenty-new-ui" -f "client_payload[artifact_name]=$ARTIFACT_NAME" -f "client_payload[run_id]=$WORKFLOW_RUN_ID" @@ -241,10 +214,8 @@ jobs: gh api repos/twentyhq/ci-privileged/dispatches "${ARGS[@]}" - # ── Cloud Argos: cross-comparison (twenty-ui baseline on main, twenty-new-ui on PRs) ── - - # Upload twenty-ui screenshots as comparison baseline on main push - dispatch-cloud-comparison-baseline: + # ── Dispatch: cross-comparison baseline (CI UI on main → twenty-ui-vs-new-ui) ── + dispatch-comparison-baseline: needs: resolve-context if: >- needs.resolve-context.outputs.workflow_name == 'CI UI' && @@ -254,7 +225,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - name: Dispatch to ci-privileged (cloud — comparison baseline) + - name: Dispatch to ci-privileged (comparison baseline) env: GH_TOKEN: ${{ secrets.CI_PRIVILEGED_DISPATCH_TOKEN }} WORKFLOW_RUN_ID: ${{ github.event.workflow_run.id }} @@ -265,16 +236,16 @@ jobs: run: | gh api repos/twentyhq/ci-privileged/dispatches \ --method POST \ - -f event_type=visual-regression-cloud \ - -f "client_payload[project]=comparison" \ + -f event_type=visual-regression \ + -f "client_payload[project]=twenty-ui-vs-new-ui" \ -f "client_payload[artifact_name]=$ARTIFACT_NAME" \ -f "client_payload[run_id]=$WORKFLOW_RUN_ID" \ -f "client_payload[repo]=$REPOSITORY" \ -f "client_payload[branch]=$BRANCH" \ -f "client_payload[commit]=$COMMIT" - # Upload twenty-new-ui screenshots as comparison on PRs - dispatch-cloud-comparison-pr: + # ── Dispatch: cross-comparison PR (CI New UI on PRs → twenty-ui-vs-new-ui) ── + dispatch-comparison-pr: needs: resolve-context if: >- needs.resolve-context.outputs.workflow_name == 'CI New UI' && @@ -284,7 +255,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - name: Dispatch to ci-privileged (cloud — comparison PR) + - name: Dispatch to ci-privileged (comparison PR) env: GH_TOKEN: ${{ secrets.CI_PRIVILEGED_DISPATCH_TOKEN }} WORKFLOW_RUN_ID: ${{ github.event.workflow_run.id }} @@ -297,8 +268,8 @@ jobs: run: | ARGS=( --method POST - -f event_type=visual-regression-cloud - -f "client_payload[project]=comparison" + -f event_type=visual-regression + -f "client_payload[project]=twenty-ui-vs-new-ui" -f "client_payload[artifact_name]=$ARTIFACT_NAME" -f "client_payload[run_id]=$WORKFLOW_RUN_ID" -f "client_payload[repo]=$REPOSITORY" diff --git a/packages/twenty-new-ui/README.md b/packages/twenty-new-ui/README.md index 104da01bfc8..13f7e4ec912 100644 --- a/packages/twenty-new-ui/README.md +++ b/packages/twenty-new-ui/README.md @@ -215,14 +215,14 @@ Apollo error formatting, and the icon/theme-color pickers tied to Twenty's icon - **Workbench** — Storybook (`@storybook/react-vite`). Every component has stories covering variants, sizes, and states (via `storybook-addon-pseudo-states`), in light and dark, with `autodocs`. - **Functional** — component/interaction tests via `@storybook/addon-vitest` (real browser); unit tests (Jest) for hooks/utilities; coverage gate via `@storybook/addon-coverage`. - **Accessibility** — Storybook a11y addon (axe-core) with `parameters.a11y.test = 'error'` so violations fail CI. -- **Visual parity** — visual regression via Argos (self-hosted for local dev, Argos cloud for CI) plus a cross-package comparison project that diffs `twenty-new-ui` stories against `twenty-ui` stories with identical names; a pixel-diff threshold is the per-component acceptance gate. See [Visual regression](#visual-regression) below. +- **Visual parity** — visual regression via Argos (self-hosted) plus a cross-package comparison project that diffs `twenty-new-ui` stories against `twenty-ui` stories with identical names; a pixel-diff threshold is the per-component acceptance gate. See [Visual regression](#visual-regression) below. - **Performance & size** — `size-limit` per entry point with budgets; tree-shaking fixtures (importing one component must not pull the library); build-time tracking; render benchmarks via React Profiler; load-time via Lighthouse/Playwright on the built Storybook. As one concrete benchmark, a dedicated **stress story** renders a very large number of a single component (e.g. 10,000 buttons) and measures total render time — compared against the `twenty-ui` equivalent and gated against a budget to catch per-instance overhead regressions. CI surfaces a per-PR diff table (`twenty-ui` vs `twenty-new-ui`) for size, a11y, and visual changes. ## Visual regression -Two Argos cloud projects provide visual regression in CI: +Two Argos projects (on argos.twenty-internal.com) provide visual regression in CI: 1. **`twenty-new-ui`** — pixel diff of `twenty-new-ui` stories against the `main` branch baseline. Catches regressions introduced by a PR. 2. **`twenty-ui-vs-new-ui`** — cross-package comparison. The baseline is always `twenty-ui` screenshots from `main`; PR builds upload `twenty-new-ui` screenshots and diff them against the `twenty-ui` baseline. This shows exactly which components still differ between the two implementations. @@ -319,7 +319,7 @@ a passing visual-parity diff, and a within-budget size entry. 1. Published package name: `twenty-new-ui` now, renamed to `twenty-ui` at cut-over (Phase 6). 2. Styling: confirm SCSS Modules vs vanilla-extract vs plain CSS Modules. 3. Variants helper: `clsx` + `data-*` vs `cva`. -4. ~~Visual regression tooling: Chromatic vs self-hosted image snapshots.~~ **Resolved:** Argos — self-hosted for local dev, Argos cloud for CI. See [Visual regression](#visual-regression). +4. ~~Visual regression tooling: Chromatic vs self-hosted image snapshots.~~ **Resolved:** Argos (self-hosted at argos.twenty-internal.com). See [Visual regression](#visual-regression). 5. How aggressively to drop `framer-motion` in favor of CSS/Base UI transitions. 6. Scope of `assets` / `testing` / `json-visualizer`: port verbatim or modernize. 7. Where to draw the generic-vs-app-specific line for `modules/ui`, and whether hybrid components live as a headless core in `twenty-new-ui` with a thin app wrapper in `twenty-front`.