mirror of
https://github.com/BillyOutlast/posthog.git
synced 2026-02-04 03:01:23 +01:00
698 lines
38 KiB
YAML
698 lines
38 KiB
YAML
name: Storybook
|
|
on:
|
|
pull_request:
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
|
# This is so that the workflow run isn't canceled when a snapshot update is pushed within it by posthog-bot
|
|
# We do however cancel from container-images-ci.yml if a commit is pushed by someone OTHER than posthog-bot
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
# Job to decide if we should run storybook ci
|
|
# See https://github.com/dorny/paths-filter#conditional-execution for more details
|
|
changes:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
name: Determine need to run storybook checks
|
|
# Set job outputs to values from filter step
|
|
outputs:
|
|
frontend: ${{ steps.filter.outputs.frontend }}
|
|
steps:
|
|
- uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2
|
|
id: filter
|
|
with:
|
|
filters: |
|
|
frontend:
|
|
- 'frontend/**'
|
|
- 'products/**/*.ts'
|
|
- 'products/**/*.tsx'
|
|
- 'products/**/frontend/**'
|
|
- 'common/**'
|
|
- 'ee/frontend/**'
|
|
- '.storybook/**'
|
|
- 'package.json'
|
|
- '.github/workflows/ci-storybook.yml'
|
|
- 'playwright.config.ts'
|
|
|
|
build-storybook:
|
|
name: Build Storybook
|
|
runs-on: ${{ needs.changes.outputs.frontend == 'true' && 'depot-ubuntu-24.04-8' || 'ubuntu-latest' }}
|
|
timeout-minutes: 15
|
|
needs: changes
|
|
steps:
|
|
- name: Skip storybook checks
|
|
if: ${{ needs.changes.outputs.frontend == 'false' }}
|
|
run: |
|
|
echo "Skipping storybook checks - no frontend changes detected"
|
|
exit 0
|
|
|
|
- uses: actions/checkout@v4
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
with:
|
|
ref: ${{ github.event.pull_request.head.ref }}
|
|
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
|
# Use PostHog Bot token when not on forks to enable proper snapshot updating
|
|
token: ${{ secrets.POSTHOG_BOT_GITHUB_TOKEN || github.token }}
|
|
|
|
- name: Install pnpm
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4
|
|
|
|
- name: Set up Node.js
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22.17.1
|
|
cache: pnpm
|
|
|
|
- name: Install dependencies
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
run: pnpm --filter=@posthog/storybook... install --frozen-lockfile
|
|
|
|
- name: Cache webpack build
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: common/storybook/node_modules/.cache/
|
|
key: ${{ runner.os }}-webpack-storybook-${{ hashFiles('pnpm-lock.yaml') }}
|
|
restore-keys: ${{ runner.os }}-webpack-storybook-
|
|
|
|
- name: Build Storybook
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
env:
|
|
NODE_OPTIONS: --max-old-space-size=32768
|
|
run: |
|
|
bin/turbo --filter=@posthog/storybook prepare
|
|
pnpm --filter=@posthog/storybook build --test
|
|
|
|
- name: Upload Storybook build artifact
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: storybook-build
|
|
path: common/storybook/dist
|
|
retention-days: 1
|
|
|
|
storybook-chromatic:
|
|
name: Publish to Chromatic
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
needs: changes
|
|
# Don't run on forks
|
|
if: github.event.pull_request.head.repo.full_name == github.repository
|
|
outputs:
|
|
storybook-url: ${{ steps.publish.outputs.storybookUrl }}
|
|
steps:
|
|
- name: Skip storybook checks
|
|
if: ${{ needs.changes.outputs.frontend == 'false' }}
|
|
run: |
|
|
echo "Skipping storybook checks - no frontend changes detected"
|
|
exit 0
|
|
- uses: actions/checkout@v4
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
with:
|
|
ref: ${{ github.event.pull_request.head.ref }}
|
|
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
|
# Use PostHog Bot token when not on forks to enable proper snapshot updating
|
|
token: ${{ secrets.POSTHOG_BOT_GITHUB_TOKEN || github.token }}
|
|
fetch-depth: 0 # 👈 Required to retrieve git history (https://www.chromatic.com/docs/github-actions)
|
|
|
|
- name: Install pnpm
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4
|
|
|
|
- name: Set up Node.js
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22.17.1
|
|
cache: pnpm
|
|
|
|
- name: Install dependencies and Chromatic
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
run: |
|
|
pnpm --filter=@posthog/storybook... install --frozen-lockfile
|
|
bin/turbo --filter=@posthog/storybook prepare
|
|
pnpm install -w -D chromatic
|
|
|
|
- name: Publish to Chromatic
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
uses: chromaui/action@d7afd50124cf4f337bcd943e7f45cfa85a5e4476 # v12
|
|
id: publish
|
|
env:
|
|
# 👇 Chromatic project token, refer to the manage page to obtain it
|
|
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
|
with:
|
|
token: ${{ secrets.GITHUB_TOKEN }}
|
|
buildScriptName: build
|
|
workingDir: ./common/storybook/
|
|
|
|
visual-regression:
|
|
name: Visual regression tests - ${{ matrix.browser }} (${{ matrix.shard }}/${{ matrix.shard_count }})
|
|
runs-on: ubuntu-latest
|
|
needs: [changes, build-storybook]
|
|
timeout-minutes: 20
|
|
container:
|
|
image: mcr.microsoft.com/playwright:v1.45.0
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- browser: chromium
|
|
shard_count: 16
|
|
shard: 1
|
|
- browser: chromium
|
|
shard_count: 16
|
|
shard: 2
|
|
- browser: chromium
|
|
shard_count: 16
|
|
shard: 3
|
|
- browser: chromium
|
|
shard_count: 16
|
|
shard: 4
|
|
- browser: chromium
|
|
shard_count: 16
|
|
shard: 5
|
|
- browser: chromium
|
|
shard_count: 16
|
|
shard: 6
|
|
- browser: chromium
|
|
shard_count: 16
|
|
shard: 7
|
|
- browser: chromium
|
|
shard_count: 16
|
|
shard: 8
|
|
- browser: chromium
|
|
shard_count: 16
|
|
shard: 9
|
|
- browser: chromium
|
|
shard_count: 16
|
|
shard: 10
|
|
- browser: chromium
|
|
shard_count: 16
|
|
shard: 11
|
|
- browser: chromium
|
|
shard_count: 16
|
|
shard: 12
|
|
- browser: chromium
|
|
shard_count: 16
|
|
shard: 13
|
|
- browser: chromium
|
|
shard_count: 16
|
|
shard: 14
|
|
- browser: chromium
|
|
shard_count: 16
|
|
shard: 15
|
|
- browser: chromium
|
|
shard_count: 16
|
|
shard: 16
|
|
# WebKit is much faster, so we run with only 3 shards
|
|
- browser: webkit
|
|
shard_count: 3
|
|
shard: 1
|
|
- browser: webkit
|
|
shard_count: 3
|
|
shard: 2
|
|
- browser: webkit
|
|
shard_count: 3
|
|
shard: 3
|
|
env:
|
|
NODE_OPTIONS: --max-old-space-size=16384
|
|
OPT_OUT_CAPTURE: 1
|
|
outputs:
|
|
# The below have to be manually listed unfortunately, as GitHub Actions doesn't allow matrix-dependent outputs
|
|
chromium-1-added: ${{ steps.diff.outputs.chromium-1-added }}
|
|
chromium-1-modified: ${{ steps.diff.outputs.chromium-1-modified }}
|
|
chromium-1-deleted: ${{ steps.diff.outputs.chromium-1-deleted }}
|
|
chromium-1-total: ${{ steps.diff.outputs.chromium-1-total }}
|
|
chromium-1-commitHash: ${{ steps.commit-hash.outputs.chromium-1-commitHash }}
|
|
chromium-1-pathsFlapping: ${{ steps.flappy-bird.outputs.chromium-1-pathsFlapping }}
|
|
chromium-2-added: ${{ steps.diff.outputs.chromium-2-added }}
|
|
chromium-2-modified: ${{ steps.diff.outputs.chromium-2-modified }}
|
|
chromium-2-deleted: ${{ steps.diff.outputs.chromium-2-deleted }}
|
|
chromium-2-total: ${{ steps.diff.outputs.chromium-2-total }}
|
|
chromium-2-commitHash: ${{ steps.commit-hash.outputs.chromium-2-commitHash }}
|
|
chromium-2-pathsFlapping: ${{ steps.flappy-bird.outputs.chromium-2-pathsFlapping }}
|
|
chromium-3-added: ${{ steps.diff.outputs.chromium-3-added }}
|
|
chromium-3-modified: ${{ steps.diff.outputs.chromium-3-modified }}
|
|
chromium-3-deleted: ${{ steps.diff.outputs.chromium-3-deleted }}
|
|
chromium-3-total: ${{ steps.diff.outputs.chromium-3-total }}
|
|
chromium-3-commitHash: ${{ steps.commit-hash.outputs.chromium-3-commitHash }}
|
|
chromium-3-pathsFlapping: ${{ steps.flappy-bird.outputs.chromium-3-pathsFlapping }}
|
|
chromium-4-added: ${{ steps.diff.outputs.chromium-4-added }}
|
|
chromium-4-modified: ${{ steps.diff.outputs.chromium-4-modified }}
|
|
chromium-4-deleted: ${{ steps.diff.outputs.chromium-4-deleted }}
|
|
chromium-4-total: ${{ steps.diff.outputs.chromium-4-total }}
|
|
chromium-4-commitHash: ${{ steps.commit-hash.outputs.chromium-4-commitHash }}
|
|
chromium-4-pathsFlapping: ${{ steps.flappy-bird.outputs.chromium-4-pathsFlapping }}
|
|
chromium-5-added: ${{ steps.diff.outputs.chromium-5-added }}
|
|
chromium-5-modified: ${{ steps.diff.outputs.chromium-5-modified }}
|
|
chromium-5-deleted: ${{ steps.diff.outputs.chromium-5-deleted }}
|
|
chromium-5-total: ${{ steps.diff.outputs.chromium-5-total }}
|
|
chromium-5-commitHash: ${{ steps.commit-hash.outputs.chromium-5-commitHash }}
|
|
chromium-5-pathsFlapping: ${{ steps.flappy-bird.outputs.chromium-5-pathsFlapping }}
|
|
chromium-6-added: ${{ steps.diff.outputs.chromium-6-added }}
|
|
chromium-6-modified: ${{ steps.diff.outputs.chromium-6-modified }}
|
|
chromium-6-deleted: ${{ steps.diff.outputs.chromium-6-deleted }}
|
|
chromium-6-total: ${{ steps.diff.outputs.chromium-6-total }}
|
|
chromium-6-commitHash: ${{ steps.commit-hash.outputs.chromium-6-commitHash }}
|
|
chromium-6-pathsFlapping: ${{ steps.flappy-bird.outputs.chromium-6-pathsFlapping }}
|
|
chromium-7-added: ${{ steps.diff.outputs.chromium-7-added }}
|
|
chromium-7-modified: ${{ steps.diff.outputs.chromium-7-modified }}
|
|
chromium-7-deleted: ${{ steps.diff.outputs.chromium-7-deleted }}
|
|
chromium-7-total: ${{ steps.diff.outputs.chromium-7-total }}
|
|
chromium-7-commitHash: ${{ steps.commit-hash.outputs.chromium-7-commitHash }}
|
|
chromium-7-pathsFlapping: ${{ steps.flappy-bird.outputs.chromium-7-pathsFlapping }}
|
|
chromium-8-added: ${{ steps.diff.outputs.chromium-8-added }}
|
|
chromium-8-modified: ${{ steps.diff.outputs.chromium-8-modified }}
|
|
chromium-8-deleted: ${{ steps.diff.outputs.chromium-8-deleted }}
|
|
chromium-8-total: ${{ steps.diff.outputs.chromium-8-total }}
|
|
chromium-8-commitHash: ${{ steps.commit-hash.outputs.chromium-8-commitHash }}
|
|
chromium-8-pathsFlapping: ${{ steps.flappy-bird.outputs.chromium-8-pathsFlapping }}
|
|
chromium-9-added: ${{ steps.diff.outputs.chromium-9-added }}
|
|
chromium-9-modified: ${{ steps.diff.outputs.chromium-9-modified }}
|
|
chromium-9-deleted: ${{ steps.diff.outputs.chromium-9-deleted }}
|
|
chromium-9-total: ${{ steps.diff.outputs.chromium-9-total }}
|
|
chromium-9-commitHash: ${{ steps.commit-hash.outputs.chromium-9-commitHash }}
|
|
chromium-9-pathsFlapping: ${{ steps.flappy-bird.outputs.chromium-9-pathsFlapping }}
|
|
chromium-10-added: ${{ steps.diff.outputs.chromium-10-added }}
|
|
chromium-10-modified: ${{ steps.diff.outputs.chromium-10-modified }}
|
|
chromium-10-deleted: ${{ steps.diff.outputs.chromium-10-deleted }}
|
|
chromium-10-total: ${{ steps.diff.outputs.chromium-10-total }}
|
|
chromium-10-commitHash: ${{ steps.commit-hash.outputs.chromium-10-commitHash }}
|
|
chromium-10-pathsFlapping: ${{ steps.flappy-bird.outputs.chromium-10-pathsFlapping }}
|
|
chromium-11-added: ${{ steps.diff.outputs.chromium-11-added }}
|
|
chromium-11-modified: ${{ steps.diff.outputs.chromium-11-modified }}
|
|
chromium-11-deleted: ${{ steps.diff.outputs.chromium-11-deleted }}
|
|
chromium-11-total: ${{ steps.diff.outputs.chromium-11-total }}
|
|
chromium-11-commitHash: ${{ steps.commit-hash.outputs.chromium-11-commitHash }}
|
|
chromium-11-pathsFlapping: ${{ steps.flappy-bird.outputs.chromium-11-pathsFlapping }}
|
|
chromium-12-added: ${{ steps.diff.outputs.chromium-12-added }}
|
|
chromium-12-modified: ${{ steps.diff.outputs.chromium-12-modified }}
|
|
chromium-12-deleted: ${{ steps.diff.outputs.chromium-12-deleted }}
|
|
chromium-12-total: ${{ steps.diff.outputs.chromium-12-total }}
|
|
chromium-12-commitHash: ${{ steps.commit-hash.outputs.chromium-12-commitHash }}
|
|
chromium-12-pathsFlapping: ${{ steps.flappy-bird.outputs.chromium-12-pathsFlapping }}
|
|
chromium-13-added: ${{ steps.diff.outputs.chromium-13-added }}
|
|
chromium-13-modified: ${{ steps.diff.outputs.chromium-13-modified }}
|
|
chromium-13-deleted: ${{ steps.diff.outputs.chromium-13-deleted }}
|
|
chromium-13-total: ${{ steps.diff.outputs.chromium-13-total }}
|
|
chromium-13-commitHash: ${{ steps.commit-hash.outputs.chromium-13-commitHash }}
|
|
chromium-13-pathsFlapping: ${{ steps.flappy-bird.outputs.chromium-13-pathsFlapping }}
|
|
chromium-14-added: ${{ steps.diff.outputs.chromium-14-added }}
|
|
chromium-14-modified: ${{ steps.diff.outputs.chromium-14-modified }}
|
|
chromium-14-deleted: ${{ steps.diff.outputs.chromium-14-deleted }}
|
|
chromium-14-total: ${{ steps.diff.outputs.chromium-14-total }}
|
|
chromium-14-commitHash: ${{ steps.commit-hash.outputs.chromium-14-commitHash }}
|
|
chromium-14-pathsFlapping: ${{ steps.flappy-bird.outputs.chromium-14-pathsFlapping }}
|
|
chromium-15-added: ${{ steps.diff.outputs.chromium-15-added }}
|
|
chromium-15-modified: ${{ steps.diff.outputs.chromium-15-modified }}
|
|
chromium-15-deleted: ${{ steps.diff.outputs.chromium-15-deleted }}
|
|
chromium-15-total: ${{ steps.diff.outputs.chromium-15-total }}
|
|
chromium-15-commitHash: ${{ steps.commit-hash.outputs.chromium-15-commitHash }}
|
|
chromium-15-pathsFlapping: ${{ steps.flappy-bird.outputs.chromium-15-pathsFlapping }}
|
|
chromium-16-added: ${{ steps.diff.outputs.chromium-16-added }}
|
|
chromium-16-modified: ${{ steps.diff.outputs.chromium-16-modified }}
|
|
chromium-16-deleted: ${{ steps.diff.outputs.chromium-16-deleted }}
|
|
chromium-16-total: ${{ steps.diff.outputs.chromium-16-total }}
|
|
chromium-16-commitHash: ${{ steps.commit-hash.outputs.chromium-16-commitHash }}
|
|
chromium-16-pathsFlapping: ${{ steps.flappy-bird.outputs.chromium-16-pathsFlapping }}
|
|
webkit-1-added: ${{ steps.diff.outputs.webkit-1-added }}
|
|
webkit-1-modified: ${{ steps.diff.outputs.webkit-1-modified }}
|
|
webkit-1-deleted: ${{ steps.diff.outputs.webkit-1-deleted }}
|
|
webkit-1-total: ${{ steps.diff.outputs.webkit-1-total }}
|
|
webkit-1-commitHash: ${{ steps.commit-hash.outputs.webkit-1-commitHash }}
|
|
webkit-1-pathsFlapping: ${{ steps.flappy-bird.outputs.webkit-1-pathsFlapping }}
|
|
webkit-2-added: ${{ steps.diff.outputs.webkit-2-added }}
|
|
webkit-2-modified: ${{ steps.diff.outputs.webkit-2-modified }}
|
|
webkit-2-deleted: ${{ steps.diff.outputs.webkit-2-deleted }}
|
|
webkit-2-total: ${{ steps.diff.outputs.webkit-2-total }}
|
|
webkit-2-commitHash: ${{ steps.commit-hash.outputs.webkit-2-commitHash }}
|
|
webkit-2-pathsFlapping: ${{ steps.flappy-bird.outputs.webkit-2-pathsFlapping }}
|
|
webkit-3-added: ${{ steps.diff.outputs.webkit-3-added }}
|
|
webkit-3-modified: ${{ steps.diff.outputs.webkit-3-modified }}
|
|
webkit-3-deleted: ${{ steps.diff.outputs.webkit-3-deleted }}
|
|
webkit-3-total: ${{ steps.diff.outputs.webkit-3-total }}
|
|
webkit-3-commitHash: ${{ steps.commit-hash.outputs.webkit-3-commitHash }}
|
|
webkit-3-pathsFlapping: ${{ steps.flappy-bird.outputs.webkit-3-pathsFlapping }}
|
|
steps:
|
|
- name: Skip storybook checks
|
|
if: ${{ needs.changes.outputs.frontend == 'false' }}
|
|
run: |
|
|
echo "Skipping storybook checks - no frontend changes detected"
|
|
exit 0
|
|
|
|
- uses: actions/checkout@v4
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
with:
|
|
ref: ${{ github.event.pull_request.head.ref }}
|
|
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
|
# Use PostHog Bot token when not on forks to enable proper snapshot updating
|
|
token: ${{ secrets.POSTHOG_BOT_GITHUB_TOKEN || github.token }}
|
|
|
|
- name: Install pnpm
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4
|
|
|
|
- name: Set up Node.js
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22.17.1
|
|
cache: pnpm
|
|
|
|
- name: Get pnpm cache directory path
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
id: pnpm-cache-dir
|
|
run: echo "PNPM_STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
|
|
|
- uses: actions/cache@v4
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
id: pnpm-cache
|
|
with:
|
|
path: ${{ steps.pnpm-cache-dir.outputs.PNPM_STORE_PATH }}
|
|
key: ${{ runner.os }}-pnpm-regression-${{ hashFiles('pnpm-lock.yaml') }}
|
|
restore-keys: ${{ runner.os }}-pnpm-regression-
|
|
|
|
- name: Install package.json dependencies with pnpm
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
run: pnpm --filter=@posthog/storybook... install --frozen-lockfile
|
|
|
|
- name: Install CI utilities with pnpm
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
run: pnpm install http-server wait-on -g
|
|
|
|
- name: Download Storybook build artifact
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: storybook-build
|
|
path: common/storybook/dist
|
|
|
|
- name: Serve Storybook in the background
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
run: |
|
|
retries=5
|
|
max_timeout=30
|
|
pnpm exec http-server common/storybook/dist --port 6006 --silent &
|
|
server_pid=$!
|
|
echo "Started http-server with PID: $server_pid"
|
|
|
|
# Give the server a moment to start
|
|
sleep 2
|
|
|
|
while [ $retries -gt 0 ]; do
|
|
echo "Checking if Storybook is available (retries left: $retries, timeout: ${max_timeout}s)..."
|
|
if pnpm wait-on http://127.0.0.1:6006 --timeout $max_timeout; then
|
|
echo "✅ Storybook is available at http://127.0.0.1:6006"
|
|
break
|
|
fi
|
|
retries=$((retries-1))
|
|
if [ $retries -gt 0 ]; then
|
|
echo "⚠️ Failed to connect to Storybook, retrying... ($retries retries left)"
|
|
# Check if server is still running
|
|
if ! kill -0 $server_pid 2>/dev/null; then
|
|
echo "❌ http-server process is no longer running, restarting it..."
|
|
pnpm exec http-server common/storybook/dist --port 6006 --silent &
|
|
server_pid=$!
|
|
echo "Restarted http-server with PID: $server_pid"
|
|
sleep 2
|
|
fi
|
|
fi
|
|
done
|
|
|
|
if [ $retries -eq 0 ]; then
|
|
echo "❌ Failed to serve Storybook after all retries"
|
|
# Try to get some diagnostic information
|
|
echo "Checking port 6006 status:"
|
|
netstat -tuln | grep 6006 || echo "Port 6006 is not in use"
|
|
echo "Checking http-server process:"
|
|
ps aux | grep http-server || echo "No http-server process found"
|
|
echo "Checking Storybook dist directory:"
|
|
ls -la common/storybook/dist || echo "Storybook dist directory not found"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Run @storybook/test-runner (up to 3 attempts)
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
env:
|
|
HOME: /root
|
|
VARIANT: ${{ github.event.pull_request.head.repo.full_name == github.repository && 'update' || 'verify' }}
|
|
STORYBOOK_SKIP_TAGS: 'test-skip,test-skip-${{ matrix.browser }}'
|
|
run: |
|
|
for i in 1 2 3; do
|
|
echo "Attempt $i: Running @storybook/test-runner"
|
|
pnpm --filter=@posthog/storybook test:visual:ci:$VARIANT --browsers ${{ matrix.browser }} --shard ${{ matrix.shard }}/${{ matrix.shard_count }} && break
|
|
if [ $i -eq 3 ]; then
|
|
echo "Test runner failed after 3 attempts"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
- name: Archive failure screenshots
|
|
if: needs.changes.outputs.frontend == 'true' && ${{ failure() }}
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: failure-screenshots-${{ matrix.browser }}-${{ matrix.shard }}
|
|
path: frontend/__snapshots__/__failures__/
|
|
|
|
- name: Configure global git diff log
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
run: git config --global --add safe.directory '*'
|
|
|
|
- name: Count and optimize updated snapshots
|
|
id: diff
|
|
# Skip on forks
|
|
if: needs.changes.outputs.frontend == 'true' && github.event.pull_request.head.repo.full_name == github.repository
|
|
run: |
|
|
echo $(git diff --name-status frontend/__snapshots__) # Wrapped to ignore exit 1 on empty diff
|
|
ADDED=$(git diff --name-status frontend/__snapshots__/ | grep '^A' | wc -l)
|
|
MODIFIED=$(git diff --name-status frontend/__snapshots__/ | grep '^M' | wc -l)
|
|
DELETED=$(git diff --name-status frontend/__snapshots__/ | grep '^D' | wc -l)
|
|
TOTAL=$(git diff --name-status frontend/__snapshots__/ | wc -l)
|
|
|
|
# If added or modified, run OptiPNG
|
|
if [ $ADDED -gt 0 ] || [ $MODIFIED -gt 0 ]; then
|
|
echo "Snapshots updated ($ADDED new, $MODIFIED changed), running OptiPNG"
|
|
apt update && apt install -y optipng
|
|
# TODO: RESTORE OptiPNG - it turns out it never worked before
|
|
# optipng -clobber -o4 -strip all frontend/__snapshots__/*.png
|
|
|
|
# we don't want to _always_ run OptiPNG
|
|
# so, we run it after checking for a diff
|
|
# but, the files we diffed might then be changed by OptiPNG
|
|
# and as a result they might no longer be different...
|
|
|
|
# we check again
|
|
git diff --name-status frontend/__snapshots__/ # For debugging
|
|
ADDED=$(git diff --name-status frontend/__snapshots__/ | grep '^A' | wc -l)
|
|
MODIFIED=$(git diff --name-status frontend/__snapshots__/ | grep '^M' | wc -l)
|
|
DELETED=$(git diff --name-status frontend/__snapshots__/ | grep '^D' | wc -l)
|
|
TOTAL=$(git diff --name-status frontend/__snapshots__/ | wc -l)
|
|
|
|
if [ $ADDED -gt 0 ] || [ $MODIFIED -gt 0 ]; then
|
|
echo "Snapshots updated ($ADDED new, $MODIFIED changed), _even after_ running OptiPNG"
|
|
git add frontend/__snapshots__/ playwright/
|
|
fi
|
|
fi
|
|
|
|
echo "Snapshot changes:"
|
|
echo "Added: $ADDED"
|
|
echo "Modified: $MODIFIED"
|
|
echo "Deleted: $DELETED"
|
|
echo "Total: $TOTAL"
|
|
echo "${{ matrix.browser }}-${{ matrix.shard }}-added=$ADDED" >> $GITHUB_OUTPUT
|
|
echo "${{ matrix.browser }}-${{ matrix.shard }}-modified=$MODIFIED" >> $GITHUB_OUTPUT
|
|
echo "${{ matrix.browser }}-${{ matrix.shard }}-deleted=$DELETED" >> $GITHUB_OUTPUT
|
|
echo "${{ matrix.browser }}-${{ matrix.shard }}-total=$TOTAL" >> $GITHUB_OUTPUT
|
|
|
|
- name: Catch and flag flapping snapshots
|
|
id: flappy-bird
|
|
if: needs.changes.outputs.frontend == 'true' && github.event.pull_request.head.repo.full_name == github.repository
|
|
shell: bash
|
|
run: |
|
|
PATHS_FLAPPING=()
|
|
last_human_commit=$(git log --pretty=format:"%H" --perl-regexp --author='^(?!github-actions)' -1)
|
|
echo "Last human commit identified: $last_human_commit"
|
|
for snapshot_path in $(git diff --name-only HEAD frontend/__snapshots__); do
|
|
echo "Checking snapshot path: $snapshot_path"
|
|
number_of_times_snapshot_has_changed_since_last_human_commit=$(git log --oneline ${last_human_commit}..HEAD -- $snapshot_path | wc -l | xargs)
|
|
echo "Number of times $snapshot_path has changed since last human commit: $number_of_times_snapshot_has_changed_since_last_human_commit"
|
|
if [ "$number_of_times_snapshot_has_changed_since_last_human_commit" -gt 0 ]; then
|
|
PATHS_FLAPPING+=($snapshot_path)
|
|
fi
|
|
done
|
|
echo "Paths flapping: ${PATHS_FLAPPING[*]:-none}"
|
|
echo "${{ matrix.browser }}-${{ matrix.shard }}-pathsFlapping=${PATHS_FLAPPING[*]}" >> $GITHUB_OUTPUT
|
|
|
|
- name: Commit updated snapshots
|
|
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9
|
|
if: needs.changes.outputs.frontend == 'true' && github.event.pull_request.head.repo.full_name == github.repository
|
|
id: commit
|
|
with:
|
|
add: '["frontend/__snapshots__/", "playwright/"]'
|
|
message: 'Update UI snapshots for `${{ matrix.browser }}` (${{ matrix.shard }})'
|
|
pull: --rebase --autostash # Make sure we're up to date with other browsers' updates
|
|
default_author: github_actions
|
|
github_token: ${{ secrets.POSTHOG_BOT_GITHUB_TOKEN || github.token }}
|
|
- name: Add commit hash to outputs, including browser name
|
|
id: commit-hash
|
|
if: needs.changes.outputs.frontend == 'true' && steps.commit.outputs.pushed == 'true'
|
|
run: echo "${{ matrix.browser }}-${{ matrix.shard }}-commitHash=${{ steps.commit.outputs.commit_long_sha }}" >> $GITHUB_OUTPUT
|
|
|
|
visual-regression-summary:
|
|
name: Summarize visual regression tests
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
needs: [visual-regression, changes]
|
|
# Run even if visual-regression fails for one (or more) of the browsers
|
|
if: always()
|
|
steps:
|
|
- name: Post comment about updated snapshots
|
|
if: needs.changes.outputs.frontend == 'true' && github.event.pull_request.head.repo.full_name == github.repository
|
|
uses: actions/github-script@v6
|
|
with:
|
|
github-token: ${{ secrets.POSTHOG_BOT_GITHUB_TOKEN || github.token }}
|
|
script: |
|
|
const BROWSERS = ['chromium', 'webkit']
|
|
|
|
const diffJobOutputs = ${{ toJson(needs.visual-regression.outputs) }}
|
|
const summaryDiff = { total: 0, added: 0, modified: 0, deleted: 0 }
|
|
const flaps = []
|
|
const diffByBrowser = Object.fromEntries(BROWSERS.map(browser => [browser, {
|
|
total: 0, added: 0, modified: 0, deleted: 0, commitHashes: []
|
|
}]))
|
|
for (const [key, rawValue] of Object.entries(diffJobOutputs)) {
|
|
// Split e.g. 'chromium-1-commitHash' into ['chromium', '1' 'commitHash']
|
|
const [browser, shardNumber, diffKey] = key.split('-')
|
|
// Sum up the counts - but not the commit hash
|
|
if (diffKey === 'pathsFlapping') {
|
|
if (rawValue && typeof rawValue === 'string') { // Ensure rawValue is a string and not empty
|
|
for (const path of rawValue.split(' ')) {
|
|
flaps.push([browser, parseInt(shardNumber), path.trim()])
|
|
}
|
|
}
|
|
} else if (diffKey === 'commitHash') {
|
|
diffByBrowser[browser].commitHashes.push([parseInt(shardNumber), rawValue])
|
|
} else {
|
|
const value = parseInt(rawValue)
|
|
diffByBrowser[browser][diffKey] += value
|
|
summaryDiff[diffKey] += value
|
|
}
|
|
}
|
|
|
|
for (const browser of BROWSERS) {
|
|
if (diffByBrowser[browser]?.total === undefined) {
|
|
diffByBrowser[browser] = null // Null means failure
|
|
}
|
|
}
|
|
|
|
if (summaryDiff.total === 0) {
|
|
console.log('No changes were made, skipping comment')
|
|
return
|
|
}
|
|
|
|
const diffByBrowserDisplay = Object.entries(diffByBrowser).map(([browser, diff]) => {
|
|
if (!diff) {
|
|
return `- \`${browser}\`: failed`
|
|
}
|
|
const { added: a, modified: m, deleted: d, commitHashes } = diff
|
|
const b = a + m + d > 0 ? '**' : '' // Bold list item if there were changes
|
|
let extraInfo = ''
|
|
if (b) {
|
|
const commitInfo = commitHashes.map(
|
|
([shardNumber, commitHash]) =>
|
|
`[diff for shard ${shardNumber}](https://github.com/${{ github.repository }}/pull/${{ github.event.pull_request.number }}/commits/${commitHash})`
|
|
).join(', ') || "wasn't pushed!"
|
|
extraInfo = ` (${commitInfo})`
|
|
}
|
|
return `- ${b}\`${browser}\`${b}: **${a}** added, **${m}** modified, **${d}** deleted${extraInfo}`
|
|
}).join('\n')
|
|
|
|
let flapsDisplay = ''
|
|
if (flaps.length > 0) {
|
|
flapsDisplay = [
|
|
'\n> [!CAUTION]',
|
|
'> ### Detected flapping snapshots',
|
|
'>',
|
|
'> These snapshots have auto-updated more than once since the last human commit:',
|
|
'>',
|
|
...flaps.map(([browser, shardNumber, path]) => `> - \`${path.replace('frontend/__snapshots__/', '')}\` (${browser}, shard ${shardNumber})`),
|
|
'>',
|
|
'> **The flippy-flappies are deadly and must be fixed ASAP.** They\'re productivity killers.',
|
|
'> Run `pnpm storybook` locally and make the fix now.',
|
|
'> (Often, the cause is `ResizeObserver` being used instead of the better CSS container queries.)\n'
|
|
].join('\n')
|
|
}
|
|
|
|
github.rest.issues.createComment({
|
|
issue_number: context.issue.number,
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
body: `## 📸 UI snapshots have been updated
|
|
|
|
**${summaryDiff.total}** snapshot changes in total. **${summaryDiff.added}** added, **${summaryDiff.modified}** modified, **${summaryDiff.deleted}** deleted:
|
|
|
|
${flapsDisplay}${diffByBrowserDisplay}
|
|
|
|
_Triggered by [this commit](https://github.com/${{ github.repository }}/pull/${{ github.event.pull_request.number }}/commits/${{ github.sha }})._
|
|
|
|
👉 **[Review this PR's diff of snapshots.](https://github.com/${{ github.repository }}/pull/${{ github.event.pull_request.number }}/files#:~:text=frontend/__snapshots__/)**`
|
|
})
|
|
|
|
# Job just to collate the status of the matrix jobs for requiring passing status
|
|
visual_regression_tests:
|
|
needs: [visual-regression]
|
|
name: Visual regression tests pass
|
|
runs-on: ubuntu-latest
|
|
if: always()
|
|
steps:
|
|
- name: Check matrix outcome
|
|
run: |
|
|
# The `needs.visual-regression.result` will be 'success' only if all jobs in the matrix succeeded.
|
|
# Otherwise, it will be 'failure'.
|
|
if [[ "${{ needs.visual-regression.result }}" != "success" ]]; then
|
|
echo "One or more jobs in the visual-regression test matrix failed."
|
|
exit 1
|
|
fi
|
|
echo "All jobs in the visual-regression test matrix passed."
|
|
|
|
calculate-running-time:
|
|
name: Calculate running time
|
|
needs: [storybook-chromatic, visual-regression, changes]
|
|
runs-on: ubuntu-latest
|
|
if: # Run on pull requests to PostHog/posthog + on PostHog/posthog outside of PRs - but never on forks
|
|
needs.changes.outputs.frontend == 'true' && (
|
|
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'PostHog/posthog') ||
|
|
(github.event_name != 'pull_request' && github.repository == 'PostHog/posthog'))
|
|
steps:
|
|
- name: Calculate running time
|
|
run: |
|
|
gh auth login --with-token < <(echo ${{ secrets.GITHUB_TOKEN }})
|
|
run_id=${GITHUB_RUN_ID}
|
|
repo=${GITHUB_REPOSITORY}
|
|
run_info=$(gh api repos/${repo}/actions/runs/${run_id})
|
|
echo run_info: ${run_info}
|
|
# name is the name of the workflow file
|
|
# run_started_at is the start time of the workflow
|
|
# we want to get the number of seconds between the start time and now
|
|
name=$(echo ${run_info} | jq -r '.name')
|
|
run_url=$(echo ${run_info} | jq -r '.url')
|
|
run_started_at=$(echo ${run_info} | jq -r '.run_started_at')
|
|
run_attempt=$(echo ${run_info} | jq -r '.run_attempt')
|
|
start_seconds=$(date -d "${run_started_at}" +%s)
|
|
now_seconds=$(date +%s)
|
|
duration=$((now_seconds-start_seconds))
|
|
echo running_time_duration_seconds=${duration} >> $GITHUB_ENV
|
|
echo running_time_run_url=${run_url} >> $GITHUB_ENV
|
|
echo running_time_run_attempt=${run_attempt} >> $GITHUB_ENV
|
|
echo running_time_run_id=${run_id} >> $GITHUB_ENV
|
|
echo running_time_run_started_at=${run_started_at} >> $GITHUB_ENV
|
|
- name: Capture running time to PostHog
|
|
uses: PostHog/posthog-github-action@v0.1
|
|
with:
|
|
posthog-token: ${{secrets.POSTHOG_API_TOKEN}}
|
|
event: 'posthog-ci-running-time'
|
|
properties: '{"duration_seconds": ${{ env.running_time_duration_seconds }}, "run_url": "${{ env.running_time_run_url }}", "run_attempt": "${{ env.running_time_run_attempt }}", "run_id": "${{ env.running_time_run_id }}", "run_started_at": "${{ env.running_time_run_started_at }}"}'
|