From 0ded15b3633cbf7b15d0a3ae1eecd020da54f4fc Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Tue, 24 Mar 2026 00:15:45 +0100 Subject: [PATCH] Add visual regression CI for twenty-ui (#18877) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - New workflow `ci-visual-regression.yaml` that runs on PRs touching `twenty-ui` or `twenty-shared` - Builds `twenty-ui` storybook and uploads the tarball as a GitHub Actions artifact - Dispatches to `twentyhq/ci-privileged` which handles the pixel-diff comparison and posts a PR comment ### Flow ``` twenty CI (this PR) ci-privileged pixel-perfect ───────────────── ────────────── ────────────── Build storybook Upload artifact Dispatch ──────────────► Download artifact Upload to S3 (OIDC) POST /import-from-storage ────► Import build POST /diffs/run ──────────────► Screenshots + diff ◄────────────────────────────── Diff report JSON Post PR comment ``` Companion PRs: - https://github.com/twentyhq/ci-privileged/pull/1 (ci-privileged workflow) - https://github.com/twentyhq/twenty-infra/pull/497 (OIDC trust + Helm cleanup) - https://github.com/twentyhq/pixel-perfect/pull/5 (API simplification) ## Test plan - [ ] Merge companion PRs first and configure secrets/environments - [ ] Open a test PR touching twenty-ui, verify storybook builds and dispatch fires - [ ] Verify visual regression comment appears on the PR --- .github/workflows/ci-front.yaml | 34 ++++++++ .github/workflows/ci-ui.yaml | 144 ++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 .github/workflows/ci-ui.yaml diff --git a/.github/workflows/ci-front.yaml b/.github/workflows/ci-front.yaml index 3525801ac6c..34fe656649a 100644 --- a/.github/workflows/ci-front.yaml +++ b/.github/workflows/ci-front.yaml @@ -151,6 +151,40 @@ jobs: # npx nyc merge coverage-artifacts ${{ env.PATH_TO_COVERAGE }}/coverage-storybook.json # - name: Checking coverage # run: npx nx storybook:coverage twenty-front --checkCoverage=true --configuration=${{ matrix.storybook_scope }} + visual-regression-dispatch: + needs: front-sb-build + if: github.event_name == 'pull_request' + timeout-minutes: 5 + runs-on: ubuntu-latest + steps: + - name: Download storybook build + uses: actions/download-artifact@v4 + with: + name: storybook-static + path: storybook-static + - name: Package storybook + run: tar -czf /tmp/storybook-twenty-front.tar.gz -C storybook-static . + - name: Upload storybook tarball + uses: actions/upload-artifact@v4 + with: + name: storybook-twenty-front-tarball + path: /tmp/storybook-twenty-front.tar.gz + retention-days: 1 + - name: Dispatch to ci-privileged + uses: peter-evans/repository-dispatch@v2 + with: + token: ${{ secrets.CI_PRIVILEGED_DISPATCH_TOKEN }} + repository: twentyhq/ci-privileged + event-type: visual-regression + client-payload: >- + { + "pr_number": "${{ github.event.pull_request.number }}", + "run_id": "${{ github.run_id }}", + "repo": "${{ github.repository }}", + "project": "twenty-front", + "branch": "${{ github.head_ref }}", + "commit": "${{ github.event.pull_request.head.sha }}" + } front-task: needs: changed-files-check if: needs.changed-files-check.outputs.any_changed == 'true' diff --git a/.github/workflows/ci-ui.yaml b/.github/workflows/ci-ui.yaml new file mode 100644 index 00000000000..1945be4a670 --- /dev/null +++ b/.github/workflows/ci-ui.yaml @@ -0,0 +1,144 @@ +name: CI UI + +on: + pull_request: + merge_group: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + +jobs: + changed-files-check: + if: github.event_name != 'merge_group' + uses: ./.github/workflows/changed-files.yaml + with: + files: | + package.json + yarn.lock + packages/twenty-ui/** + packages/twenty-shared/** + ui-task: + needs: changed-files-check + if: needs.changed-files-check.outputs.any_changed == 'true' + timeout-minutes: 30 + runs-on: ubuntu-latest + strategy: + matrix: + task: [lint, typecheck, test] + steps: + - name: Cancel Previous Runs + uses: styfle/cancel-workflow-action@0.11.0 + with: + access_token: ${{ github.token }} + - name: Fetch custom Github Actions and base branch history + uses: actions/checkout@v4 + with: + fetch-depth: 10 + - name: Install dependencies + uses: ./.github/actions/yarn-install + - name: Run ${{ matrix.task }} + run: npx nx ${{ matrix.task }} twenty-ui + ui-sb-build: + needs: changed-files-check + if: needs.changed-files-check.outputs.any_changed == 'true' + timeout-minutes: 30 + runs-on: ubuntu-latest + steps: + - name: Cancel Previous Runs + uses: styfle/cancel-workflow-action@0.11.0 + with: + access_token: ${{ github.token }} + - name: Fetch custom Github Actions and base branch history + uses: actions/checkout@v4 + with: + fetch-depth: 10 + - name: Install dependencies + uses: ./.github/actions/yarn-install + - name: Build storybook + run: npx nx storybook:build twenty-ui + - name: Upload storybook build + uses: actions/upload-artifact@v4 + with: + name: storybook-twenty-ui + path: packages/twenty-ui/storybook-static + retention-days: 1 + ui-sb-test: + timeout-minutes: 30 + runs-on: ubuntu-latest + needs: ui-sb-build + env: + STORYBOOK_URL: http://localhost:6007 + steps: + - name: Fetch custom Github Actions and base branch history + uses: actions/checkout@v4 + with: + fetch-depth: 10 + - name: Install dependencies + uses: ./.github/actions/yarn-install + - name: Download storybook build + uses: actions/download-artifact@v4 + with: + name: storybook-twenty-ui + path: packages/twenty-ui/storybook-static + - name: Install Playwright + run: | + cd packages/twenty-ui + npx playwright install + - name: Serve storybook & run tests + run: | + npx http-server packages/twenty-ui/storybook-static --port 6007 --silent & + timeout 30 bash -c 'until curl -sf http://localhost:6007 > /dev/null 2>&1; do sleep 1; done' + npx nx storybook:test twenty-ui + visual-regression-dispatch: + needs: ui-sb-build + if: github.event_name == 'pull_request' + timeout-minutes: 5 + runs-on: ubuntu-latest + steps: + - name: Download storybook build + uses: actions/download-artifact@v4 + with: + name: storybook-twenty-ui + path: storybook-static + - name: Package storybook + run: tar -czf /tmp/storybook-twenty-ui.tar.gz -C storybook-static . + - name: Upload storybook tarball + uses: actions/upload-artifact@v4 + with: + name: storybook-twenty-ui-tarball + path: /tmp/storybook-twenty-ui.tar.gz + retention-days: 1 + - name: Dispatch to ci-privileged + uses: peter-evans/repository-dispatch@v2 + with: + token: ${{ secrets.CI_PRIVILEGED_DISPATCH_TOKEN }} + repository: twentyhq/ci-privileged + event-type: visual-regression + client-payload: >- + { + "pr_number": "${{ github.event.pull_request.number }}", + "run_id": "${{ github.run_id }}", + "repo": "${{ github.repository }}", + "project": "twenty-ui", + "branch": "${{ github.head_ref }}", + "commit": "${{ github.event.pull_request.head.sha }}" + } + ci-ui-status-check: + if: always() && !cancelled() + timeout-minutes: 5 + runs-on: ubuntu-latest + needs: + [ + changed-files-check, + ui-task, + ui-sb-build, + ui-sb-test, + ] + steps: + - name: Fail job if any needs failed + if: contains(needs.*.result, 'failure') + run: exit 1