Insert Distribution 16 #8717
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: CI | |
on: | |
merge_group: | |
pull_request: | |
workflow_dispatch: | |
concurrency: | |
group: ci-${{ github.ref }}-${{ github.event_name }} | |
cancel-in-progress: true | |
jobs: | |
build_next_image: | |
name: Build Next.js Docker Image | |
# Only build images for PRs and manual triggers, not for pushes to main/dev (handled by deploy workflows) | |
if: ${{ github.event_name != 'push' }} | |
# runs-on: ubuntu-latest | |
runs-on: arc-runner-set | |
permissions: | |
contents: read | |
outputs: | |
image-tar: ${{ steps.output-tar.outputs.path }} | |
image-name: next-app | |
image-tag: ${{ steps.image-tag.outputs.tag }} | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
with: | |
submodules: recursive | |
- name: Set Environment Variables from Template | |
run: | | |
cp .env.local.template .env.local | |
# fix for dockerized mode | |
sed -i 's/localhost/host.docker.internal/g' .env.local | |
# except NEXT_PUBLIC_URL | |
sed -i 's/NEXT_PUBLIC_URL=http:\/\/host.docker.internal/NEXT_PUBLIC_URL=http:\/\/localhost/g' .env.local | |
cat .env.local | |
- name: Extract build args from environment file | |
id: build-args | |
run: | | |
# Extract env vars and format them for docker build-args | |
echo "args<<EOF" >> $GITHUB_OUTPUT | |
cat .env.local | grep -v '^#' | grep -v '^$' >> $GITHUB_OUTPUT | |
echo "EOF" >> $GITHUB_OUTPUT | |
- name: Set up Docker Buildx | |
uses: docker/setup-buildx-action@v3 | |
- name: Generate unique image tag | |
id: image-tag | |
run: echo "tag=next-app-$(echo $GITHUB_SHA | cut -c1-7)" >> $GITHUB_OUTPUT | |
- name: Build Docker image | |
id: build | |
uses: docker/build-push-action@v6 | |
with: | |
context: . | |
file: ./Dockerfile | |
target: next-app-runner | |
push: false | |
load: true | |
tags: ${{ steps.image-tag.outputs.tag }} | |
build-args: | | |
PACKAGE=next-app | |
${{ steps.build-args.outputs.args }} | |
cache-from: type=gha | |
cache-to: type=gha,mode=max | |
- name: Export Docker image to tarball | |
run: docker save ${{ steps.image-tag.outputs.tag }} > next-app-image.tar | |
- name: Upload Docker image as artifact | |
id: upload-artifact | |
uses: actions/upload-artifact@v4 | |
with: | |
name: next-app-image | |
path: next-app-image.tar | |
retention-days: 1 | |
- name: Output tarball path | |
id: output-tar | |
run: echo "path=next-app-image.tar" >> $GITHUB_OUTPUT | |
supabase-schema-drift-check: | |
name: Supabase Schema Drift Check | |
runs-on: ubuntu-latest | |
permissions: | |
contents: read | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Check for Schema Drift | |
uses: ./.github/actions/supabase-schema-drift-check | |
unit-tests: | |
name: Unit Tests | |
runs-on: ubuntu-latest | |
permissions: | |
contents: read | |
env: | |
ANVIL_MAINNET_FORK_URL: ${{ secrets.CI_ANVIL_MAINNET_FORK_URL }} | |
ANVIL_BASE_FORK_URL: ${{ secrets.CI_ANVIL_BASE_FORK_URL }} | |
FOUNDRY_BASE_SEPOLIA_RPC_URL: ${{ secrets.CI_FOUNDRY_BASE_SEPOLIA_RPC_URL }} | |
YARN_ENABLE_HARDENED_MODE: 0 | |
steps: | |
- name: Add hosts to /etc/hosts | |
run: | | |
sudo su | |
if grep -q "host.docker.internal" /etc/hosts; then | |
echo "Hosts file already contains host.docker.internal" | |
else | |
echo "127.0.0.1 host.docker.internal" | sudo tee -a /etc/hosts | |
fi | |
- uses: actions/checkout@v4 | |
with: | |
submodules: recursive | |
fetch-depth: 2 | |
- name: Setup Environment | |
uses: ./.github/actions/setup-env | |
env: | |
YARN_ENABLE_HARDENED_MODE: 0 | |
with: | |
yarn-install: false | |
- name: Tilt CI | |
id: tilt-ci | |
shell: bash | |
run: tilt ci unit-tests --timeout=15m | |
# Since we use ephemeral containers, we don't need to clean up anymore | |
# - name: Tilt Down | |
# # always run tilt down if tilt ci started | |
# if: always() && steps.tilt-ci.outcome != 'skipped' | |
# shell: bash | |
# run: tilt down | |
# @todo anvil fixtures cause dirty repo, but only in github actions 😢 | |
- name: All Changes Committed | |
shell: bash | |
run: git diff # --exit-code | |
lint: | |
name: Lint | |
runs-on: ubuntu-latest | |
permissions: | |
contents: read | |
env: | |
ANVIL_MAINNET_FORK_URL: ${{ secrets.CI_ANVIL_MAINNET_FORK_URL }} | |
ANVIL_BASE_FORK_URL: ${{ secrets.CI_ANVIL_BASE_FORK_URL }} | |
FOUNDRY_BASE_SEPOLIA_RPC_URL: ${{ secrets.CI_FOUNDRY_BASE_SEPOLIA_RPC_URL }} | |
YARN_ENABLE_HARDENED_MODE: 0 | |
steps: | |
- name: Add hosts to /etc/hosts | |
run: | | |
sudo su | |
if grep -q "host.docker.internal" /etc/hosts; then | |
echo "Hosts file already contains host.docker.internal" | |
else | |
echo "127.0.0.1 host.docker.internal" | sudo tee -a /etc/hosts | |
fi | |
- uses: actions/checkout@v4 | |
with: | |
submodules: recursive | |
filter: 'blob:none' | |
fetch-depth: 0 | |
- name: Setup Environment | |
uses: ./.github/actions/setup-env | |
env: | |
YARN_ENABLE_HARDENED_MODE: 0 | |
with: | |
build-nextjs: 'false' | |
yarn-install: false | |
- name: Lint deps | |
shell: bash | |
run: | | |
tilt ci lint:deps | |
- name: Set Workers Count | |
id: workers | |
shell: bash | |
run: | | |
# set to max workers 1 until we figure out which task is causing the runner to disconnect | |
workers=$(MAX_WORKERS=1 node ./.devops/bin/worker-count) | |
echo "workers=$workers" >> $GITHUB_OUTPUT | |
- name: Lint | |
run: | | |
npx turbo run lint --affected --concurrency=${{ steps.workers.outputs.workers }} | |
env: | |
# app:lint is heavy on memory, so we need to increase the limit | |
NODE_OPTIONS: --max_old_space_size=8192 | |
TURBO_SCM_BASE: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }} | |
playwright-tests: | |
name: Playwright Tests (Shard ${{ matrix.shard }}/4) | |
needs: [build_next_image] | |
# Only run playwright tests when we build the image (PRs and manual triggers) | |
if: ${{ github.event_name != 'push' }} | |
# arc-runner-set had a good run | |
runs-on: arc-runner-set | |
# runs-on: ubuntu-latest | |
strategy: | |
# Don't stop all shards if one fails | |
fail-fast: false | |
matrix: | |
shard: [1, 2, 3, 4] | |
env: | |
ANVIL_MAINNET_FORK_URL: ${{ secrets.CI_ANVIL_MAINNET_FORK_URL }} | |
ANVIL_BASE_FORK_URL: ${{ secrets.CI_ANVIL_BASE_FORK_URL }} | |
YARN_ENABLE_HARDENED_MODE: 0 | |
NEXT_APP_IMAGE_TAG: ${{ needs.build_next_image.outputs.image-tag }} | |
# for writing PR comments | |
permissions: | |
contents: read | |
pull-requests: write | |
timeout-minutes: 60 | |
steps: | |
- name: Add hosts to /etc/hosts | |
run: | | |
sudo su | |
if grep -q "host.docker.internal" /etc/hosts; then | |
echo "Hosts file already contains host.docker.internal" | |
else | |
echo "127.0.0.1 host.docker.internal" | sudo tee -a /etc/hosts | |
fi | |
- uses: actions/checkout@v4 | |
with: | |
submodules: recursive | |
- name: Setup Environment | |
uses: ./.github/actions/setup-env | |
# yarn-install is now handled below | |
env: | |
YARN_ENABLE_HARDENED_MODE: 0 | |
- name: Yarn Install | |
shell: bash | |
run: yarn install --immutable | |
- name: Download Docker image artifact | |
uses: actions/download-artifact@v4 | |
with: | |
name: next-app-image | |
path: ./ | |
- name: Load Docker image | |
run: | | |
docker load < next-app-image.tar | |
echo "Loaded Docker image: ${{ env.NEXT_APP_IMAGE_TAG }}" | |
docker images | |
# @todo anvil fixtures cause dirty repo, but only in github actions 😢 | |
# - name: All Changes Committed | |
# shell: bash | |
# run: git diff --exit-code | |
- name: Install Playwright Dependencies | |
run: yarn playwright playwright install --with-deps > /dev/null | |
- name: Start Playwright Dependencies | |
shell: bash | |
run: | | |
export DEBUG="api:*,app:*,test:*,-test:fixtures:snaplet:*" | |
# Use the skip_docker_compose flag to not manage docker compose in tilt | |
nohup tilt up playwright:deps -- --skip_docker_compose & | |
tilt_pid=$! | |
echo "Tilt process started with PID: $tilt_pid" | |
echo waiting for tilt to be ready | |
for i in {1..10}; do | |
curl -s http://localhost:10350 > /dev/null && break || echo "Attempt $i failed, trying again..." | |
sleep 1 | |
done | |
sleep 1 | |
echo tilt is ready | |
max_attempts=90 | |
wait_timeout=10 | |
for ((i=1; i<=max_attempts; i++)); do | |
echo "Attempt $i: Waiting for tilt with timeout ${wait_timeout}s" | |
# Check if the Tilt process is still running | |
if ! ps -p $tilt_pid > /dev/null; then | |
echo "Tilt process (PID: $tilt_pid) is no longer running, exiting" | |
exit 1 | |
fi | |
# Check if any targets are in error | |
has_errors=$(tilt dump engine | jq ' | |
.ManifestTargets | |
| to_entries | |
| map( | |
select( | |
.value.State.RuntimeState.Status == "error" or | |
(.value.State.BuildHistory | length > 0 and .[0].Error != null) | |
) | |
) | |
| length > 0 | |
') | |
if [ "$has_errors" = "true" ]; then | |
echo "There are targets in error status:" | |
tilt dump engine | jq ' | |
.ManifestTargets | |
| to_entries | |
| map( | |
select( | |
.value.State.RuntimeState.Status == "error" or | |
(.value.State.BuildHistory | length > 0 and .[0].Error != null) | |
) | |
) | |
| map(.key) | |
' | |
exit 1 | |
fi | |
if tilt wait --timeout "${wait_timeout}s" --for=condition=Ready "uiresource/playwright:deps"; then | |
echo "Tilt is ready" | |
break | |
else | |
echo "Tilt not ready, retrying..." | |
if [ "${{runner.debug}}" == "1" ]; then | |
tilt dump engine | jq ' | |
.ManifestTargets | |
| to_entries | |
| map({key: (.value.State.RuntimeState.Status // "in_progress"), value: .key}) | |
| group_by(.key) | |
| map({key: .[0].key, value: map(.value)}) | |
| from_entries | |
' | |
fi | |
fi | |
if ((i == max_attempts)); then | |
echo "Reached maximum attempts, exiting" | |
exit 1 | |
fi | |
done | |
- name: Start Next App | |
shell: bash | |
run: | | |
export NEXT_APP_PORT=0.0.0.0:3000 | |
docker compose --env-file ./.env.local up -d next-app | |
# wait for next app to be ready | |
started=0 | |
for i in {1..30}; do | |
if curl -s http://localhost:3000 > /dev/null; then | |
started=1 | |
break | |
fi | |
echo "Attempt $i failed, trying again..." | |
sleep 1 | |
done | |
if [ $started -eq 0 ]; then | |
echo "Next app did not start" | |
exit 1 | |
fi | |
- name: Playwright Tests | |
id: playwright | |
shell: bash | |
run: | | |
docker compose logs -f --no-log-prefix next-app & | |
yarn playwright test --shard=${{ matrix.shard }}/4 | |
- name: Tilt Down | |
# always run tilt down if tilt ci started | |
if: always() && steps.playwright.outcome != 'skipped' | |
shell: bash | |
run: tilt down | |
- name: Upload blob report to GitHub Actions Artifacts | |
if: ${{ !cancelled() }} | |
uses: actions/upload-artifact@v4 | |
with: | |
name: blob-report-${{ matrix.shard }} | |
path: packages/playwright/blob-report/ | |
retention-days: 1 | |
playwright-merge-reports: | |
name: Playwright Merge Reports | |
runs-on: ubuntu-latest | |
if: ${{ !cancelled() }} | |
needs: [playwright-tests] | |
# for writing PR comments | |
permissions: | |
contents: read | |
pull-requests: write | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Setup Environment | |
uses: ./.github/actions/setup-env | |
env: | |
YARN_ENABLE_HARDENED_MODE: 0 | |
with: | |
build-nextjs: false | |
yarn-install: false | |
- name: Download blob reports from GitHub Actions Artifacts | |
uses: actions/download-artifact@v4 | |
with: | |
path: all-blob-reports | |
pattern: blob-report-* | |
merge-multiple: true | |
- name: Merge into HTML Report | |
run: npx playwright merge-reports --reporter html ./all-blob-reports | |
- name: Upload HTML report | |
uses: actions/upload-artifact@v4 | |
with: | |
name: html-report--attempt-${{ github.run_attempt }} | |
path: playwright-report | |
retention-days: 14 | |
- name: Merge into JSON Report | |
run: npx playwright merge-reports --reporter json ./all-blob-reports > playwright-report/report.json | |
- name: Playwright Markdown Report | |
if: always() # Always run regardless of previous step outcome | |
id: playwright-md-report | |
shell: bash | |
run: | | |
echo "------------------" | |
if [ ! -f "$PLAYWRIGHT_REPORT_PATH" ]; then | |
echo -e "# Playwright Test Results\n\n## No test results available\n\nNo test results were found. This could be because tests failed to run or artifacts were not uploaded." > playwright-report.md | |
else | |
bun run packages/playwright/bin/report-markdown.ts > playwright-report.md || echo -e "# Playwright Test Results\n\n## Error generating report\n\nThere was an error generating the test report." > playwright-report.md | |
fi | |
cat playwright-report.md | |
echo "------------------" | |
env: | |
PLAYWRIGHT_REPORT_PATH: playwright-report/report.json | |
- uses: mshick/add-pr-comment@v2 | |
if: ${{ !cancelled() && github.event_name == 'pull_request' }} # Only comment on PRs | |
with: | |
message-id: playwright-report # Use the same ID to update the comment | |
refresh-message-position: true | |
message-path: | | |
playwright-report.md | |
vercel-deploy-preview: | |
# **always** skip if on dev and main since it will be deployed with another workflow | |
if: ${{ github.ref != 'refs/heads/dev' && github.ref != 'refs/heads/main' && github.head_ref != 'dev' && github.head_ref != 'main' }} | |
runs-on: ubuntu-latest | |
permissions: | |
contents: read | |
pull-requests: write | |
env: | |
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} | |
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
submodules: recursive | |
- name: Setup Environment | |
uses: ./.github/actions/setup-env | |
env: | |
YARN_ENABLE_HARDENED_MODE: 0 | |
- name: Extract branch name | |
id: extract-branch | |
uses: ./.github/actions/extract-branch | |
- name: Set Public Hostname | |
id: public-hostname | |
run: echo "public-hostname=sendapp-${{steps.extract-branch.outputs.branch}}-0xsend.vercel.app" >> $GITHUB_OUTPUT | |
- name: Vercel Deploy Preview with Supabase Branch | |
if: github.base_ref == 'dev' | |
id: vercel-deploy-with-branch | |
uses: ./.github/actions/vercel-deploy | |
with: | |
vercel-token: ${{ secrets.VERCEL_TOKEN }} | |
public-hostname: ${{ steps.public-hostname.outputs.public-hostname }} | |
deploy-preview-extra-args: >- | |
-e SUPABASE_DB_URL="${{ secrets.STAGING_SUPABASE_DB_URL }}" | |
-e SUPABASE_JWT_SECRET="${{ secrets.STAGING_SUPABASE_JWT_SECRET }}" | |
-e SUPABASE_SERVICE_ROLE="${{ secrets.STAGING_SUPABASE_SERVICE_ROLE }}" | |
-e NEXT_PUBLIC_SUPABASE_URL="https://${{ secrets.STAGING_SUPABASE_PROJECT_ID }}.supabase.co" | |
-e NEXT_PUBLIC_SUPABASE_PROJECT_ID="${{ secrets.STAGING_SUPABASE_PROJECT_ID }}" | |
-e NEXT_PUBLIC_SUPABASE_GRAPHQL_URL="https://${{ secrets.STAGING_SUPABASE_PROJECT_ID }}.supabase.co/graphql" | |
-e NEXT_PUBLIC_BASE_CHAIN_ID="84532" | |
-e NEXT_PUBLIC_MAINNET_CHAIN_ID="11155111" | |
-e NEXT_PUBLIC_BASE_RPC_URL="${{ secrets.BASE_SEPOLIA_RPC_URL }}" | |
-e NEXT_PUBLIC_MAINNET_RPC_URL="https://ethereum-sepolia-rpc.publicnode.com" | |
-e NEXT_PUBLIC_SUPABASE_ANON_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InVncXRvdWxleGh2YWhldnN5c3VxIiwicm9sZSI6ImFub24iLCJpYXQiOjE2OTMwOTE5MzUsImV4cCI6MjAwODY2NzkzNX0.RL8W-jw2rsDhimYl8KklF2B9bNTPQ-Kj5zZA0XlufUA" | |
env: | |
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} | |
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} | |
VERCEL_GIT_COMMIT_SHA: ${{ github.sha }} | |
VERCEL_GIT_COMMIT_REF: ${{ github.head_ref }} | |
VERCEL_GIT_PULL_REQUEST_ID: ${{ github.event.pull_request.number }} | |
SUPABASE_DB_URL: ${{ secrets.STAGING_SUPABASE_DB_URL }} | |
SUPABASE_JWT_SECRET: ${{ secrets.STAGING_SUPABASE_JWT_SECRET }} | |
SUPABASE_SERVICE_ROLE: ${{ secrets.STAGING_SUPABASE_SERVICE_ROLE }} | |
NEXT_PUBLIC_SUPABASE_URL: https://${{ secrets.STAGING_SUPABASE_PROJECT_ID }}.supabase.co | |
NEXT_PUBLIC_SUPABASE_PROJECT_ID: ${{ secrets.STAGING_SUPABASE_PROJECT_ID }} | |
NEXT_PUBLIC_SUPABASE_GRAPHQL_URL: https://${{ secrets.STAGING_SUPABASE_PROJECT_ID }}.supabase.co/graphql | |
NEXT_PUBLIC_BASE_CHAIN_ID: 84532 | |
NEXT_PUBLIC_MAINNET_CHAIN_ID: 11155111 | |
NEXT_PUBLIC_BASE_RPC_URL: ${{ secrets.BASE_SEPOLIA_RPC_URL }} | |
NEXT_PUBLIC_MAINNET_RPC_URL: https://ethereum-sepolia-rpc.publicnode.com | |
NEXT_PUBLIC_SUPABASE_ANON_KEY: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InVncXRvdWxleGh2YWhldnN5c3VxIiwicm9sZSI6ImFub24iLCJpYXQiOjE2OTMwOTE5MzUsImV4cCI6MjAwODY2NzkzNX0.RL8W-jw2rsDhimYl8KklF2B9bNTPQ-Kj5zZA0XlufUA | |
- name: Vercel Deploy Preview | |
if: github.base_ref != 'dev' | |
id: vercel-deploy-preview | |
uses: ./.github/actions/vercel-deploy | |
with: | |
vercel-token: ${{ secrets.VERCEL_TOKEN }} | |
public-hostname: ${{ steps.public-hostname.outputs.public-hostname }} | |
env: | |
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} | |
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} | |
VERCEL_GIT_COMMIT_SHA: ${{ github.sha }} | |
VERCEL_GIT_COMMIT_REF: ${{ github.head_ref }} | |
VERCEL_GIT_PULL_REQUEST_ID: ${{ github.event.pull_request.number }} | |
- uses: mshick/add-pr-comment@v2 | |
if: github.base_ref == 'dev' | |
with: | |
message-id: vercel-preview-url | |
refresh-message-position: true | |
message: | | |
Vercel Unique URL: [${{ steps.vercel-deploy-with-branch.outputs.deployment-url }}](${{ steps.vercel-deploy-with-branch.outputs.deployment-url }}) | |
Vercel Preview URL: [${{ steps.public-hostname.outputs.public-hostname }}](https://${{ steps.public-hostname.outputs.public-hostname }}/) | |
Last Commit: ${{ github.event.pull_request.head.sha }} | |
- uses: mshick/add-pr-comment@v2 | |
if: github.base_ref != 'dev' | |
with: | |
message-id: vercel-preview-url | |
refresh-message-position: true | |
message: | | |
Vercel Unique URL: [${{ steps.vercel-deploy-preview.outputs.deployment-url }}](${{ steps.vercel-deploy-preview.outputs.deployment-url }}) | |
Vercel Preview URL: [${{ steps.public-hostname.outputs.public-hostname }}](https://${{ steps.public-hostname.outputs.public-hostname }}/) | |
Last Commit: ${{ github.event.pull_request.head.sha }} |