Files
cloudflare__vinext/.github/workflows/ci.yml
James Anderson c107079ad1 fix(build): avoid workerd cwd in module identity (#3007)
* fix(build): avoid workerd cwd in module identity

* test(cloudflare): cover legacy module identity

* test(ci): run Cloudflare encoded-path fixtures

* test(cloudflare): version module identity fixture
2026-08-20 12:29:25 +00:00

682 lines
27 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: CI
on:
push:
branches: [main]
pull_request: # run on PRs against any base branch, not just main
workflow_call: # allow other workflows to run CI as a gate
permissions:
contents: read
concurrency:
group: ci-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: true
jobs:
check:
name: Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ./.github/actions/setup
- name: Build plugin (needed for benchmark workspace type resolution)
run: vp run build
- run: vp run check
- run: vp run knip
- name: Bash syntax check
run: |
for f in scripts/*.sh; do
bash -n "$f" || { echo "Syntax error in $f"; exit 1; }
done
- name: Verify integration shard manifest
run: node scripts/ci-integration-shard.mjs --check --shard-total=10
test-unit:
name: Vitest (unit ${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3]
shardTotal: [3]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ./.github/actions/setup
- run: vp test run --project unit --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
test-integration:
name: Vitest (integration ${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Shard count mirrors scripts/ci-integration-timings.json (shardTotal);
# the Check job fails on drift between this matrix and the manifest.
# Rebalance weights by regenerating the manifest from real CI blobs:
# gh run download <run-id> -p 'blob-report-*' -D /tmp/blobs
# node scripts/ci-integration-timings-refresh.mjs /tmp/blobs --run=<run-id> --write
# Revisit the count with: node scripts/ci-integration-shard.mjs --recommend --target-ms=<competing-bottleneck>
# 10 shards: the wall-clock floor for integration on this public repo
# (free runner minutes). Beyond 10, the slowest integration shard plus
# the serial report tail drops below the create-next-app (windows) job,
# so more shards stop moving the overall wall. See PR "Wall-clock vs
# shard count". Lowering it further means changing those two jobs, not
# this count.
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
shardTotal: [10]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ./.github/actions/setup
- name: Build plugin (needed by ecosystem and nextjs-compat fixtures)
run: vp run build
- name: Compute shard files from timing manifest
id: shard
shell: bash
run: |
files=$(node scripts/ci-integration-shard.mjs --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
echo "files<<EOF" >> "$GITHUB_OUTPUT"
echo "$files" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
# Coverage is gated to push-to-main runs to keep PR feedback fast.
# Istanbul instrumentation adds ~1025% to each shard's wall-clock.
#
# SHARD_FILES holds the planner's emitted file list (paths discovered from
# `vp test list`). It is passed via env and left unquoted on purpose so the
# shell word-splits it into separate file arguments. Routing it through env
# instead of expanding ${{ steps.shard.outputs.files }} inline keeps a
# filename from a fork PR from being interpolated into the script text
# (template injection). The shell treats it as data, never as code.
- name: Run integration shard
run: |
if [ -z "${SHARD_FILES// /}" ]; then
echo "::error::shard ${SHARD_INDEX}/${SHARD_TOTAL} resolved to no files"
exit 1
fi
vp test run --project integration $COVERAGE \
--reporter=blob \
--outputFile.blob=".vitest-reports/blob-${SHARD_INDEX}-${SHARD_TOTAL}.json" \
$SHARD_FILES
env:
CI: true
SHARD_FILES: ${{ steps.shard.outputs.files }}
COVERAGE: ${{ github.event_name == 'push' && '--coverage' || '' }}
SHARD_INDEX: ${{ matrix.shardIndex }}
SHARD_TOTAL: ${{ matrix.shardTotal }}
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: ${{ !cancelled() }}
with:
name: blob-report-${{ matrix.shardIndex }}
path: .vitest-reports/*
include-hidden-files: true
retention-days: 1
test-integration-merge:
name: Vitest (integration report)
if: ${{ !cancelled() }}
needs: [test-integration]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ./.github/actions/setup
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: .vitest-reports
pattern: blob-report-*
merge-multiple: true
# --coverage tells vitest to invoke coverageProvider.mergeReports() on the
# coverage data embedded in each shard's blob, producing a unified report.
# Only enabled on push-to-main, matching the shard config above.
- run: vp test run --merge-reports ${{ github.event_name == 'push' && '--coverage' || '' }}
env:
CI: true
- name: Publish coverage matrix to job summary
if: ${{ !cancelled() && hashFiles('coverage/coverage-summary.json') != '' }}
run: node scripts/coverage-summary.mjs >> "$GITHUB_STEP_SUMMARY"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: ${{ !cancelled() && hashFiles('coverage/index.html') != '' }}
with:
name: coverage-html
path: coverage
retention-days: 14
create-next-app:
name: create-next-app (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ./.github/actions/setup
- name: Build plugin
run: vp run build
- name: Pack vinext packages for local install
run: |
vp pm pack --pack-destination "${{ runner.temp }}"
cd ../types
vp pm pack --pack-destination "${{ runner.temp }}"
working-directory: packages/vinext
- name: Scaffold a fresh create-next-app project
run: vp dlx create-next-app@16.2.7 "${{ runner.temp }}/cna-test" --yes
- name: Deny build scripts in scaffolded project
working-directory: ${{ runner.temp }}/cna-test
shell: bash
# pnpm 11 may auto-add placeholder allowBuilds entries during the
# scaffold install. Replace them with explicit false values.
run: |
node -e '
const fs = require("fs");
const f = "pnpm-workspace.yaml";
let y = fs.existsSync(f) ? fs.readFileSync(f, "utf8") : "";
y = y.replace(/allowBuilds:[\s\S]*?(?=\n\S|\n*$)/, "");
y = y.trimEnd() + "\n\nallowBuilds:\n sharp: false\n unrs-resolver: false\n";
fs.writeFileSync(f, y);
'
- name: Pin pnpm in scaffolded project to vinext's version
working-directory: ${{ runner.temp }}/cna-test
shell: bash
# create-next-app's bundled pnpm links node_modules to a major-version
# store (e.g. v10). vp's bundled pnpm can drift to a newer major (v11),
# which then refuses to operate on the existing node_modules with
# ERR_PNPM_UNEXPECTED_STORE. vp respects the project's packageManager
# field, so writing the same one vinext itself uses makes vp's pnpm
# match the store layout from create-next-app. Tracks any future
# bump in vinext's root package.json automatically.
run: |
# cd into the workspace to read package.json via a relative path —
# ${{ github.workspace }} contains backslashes on Windows that get
# eaten by the JS string literal parser when interpolated inline.
PKG_MGR=$(cd "${{ github.workspace }}" && node -p "require('./package.json').packageManager")
node -e "
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
pkg.packageManager = '$PKG_MGR';
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
"
echo "Pinned packageManager to $PKG_MGR"
- name: Install vinext from local tarball
working-directory: ${{ runner.temp }}/cna-test
shell: bash
run: |
node "$GITHUB_WORKSPACE/scripts/configure-local-vinext-types.mjs" "${{ runner.temp }}"
vp add "${{ runner.temp }}"/vinext-[0-9]*.tgz
- name: Run vinext init
working-directory: ${{ runner.temp }}/cna-test
run: vp exec vinext init --skip-check --platform=node
- name: Start dev server and verify HTTP 200
working-directory: ${{ runner.temp }}/cna-test
shell: bash
run: |
vp exec vite dev --host 127.0.0.1 --port 3099 &
SERVER_PID=$!
trap 'kill "$SERVER_PID" 2>/dev/null || true' EXIT
for i in $(seq 1 45); do
STATUS=$(node -e '
const http = require("node:http");
let done = false;
function finish(status) {
if (done) return;
done = true;
console.log(status);
process.exit(status === 200 ? 0 : 1);
}
const req = http.get("http://127.0.0.1:3099/", (res) => {
res.resume();
finish(res.statusCode);
});
req.setTimeout(10000, () => {
req.destroy();
finish("timeout");
});
req.on("error", () => {
finish("000");
});
' || true)
echo "HTTP status from dev server: ${STATUS:-request failed} (attempt $i)"
if [ "$STATUS" = "200" ]; then
echo "Server responded with HTTP 200 (attempt $i)"
exit 0
fi
sleep 1
done
echo "Server did not respond with HTTP 200 before the retry limit"
exit 1
create-next-app-cloudflare:
name: create-next-app (cloudflare init)
runs-on: ubuntu-latest
env:
CNA_CLOUDFLARE_WORKER: create-next-app-cloudflare
# Public test namespace already used by examples/workers-cache.
CNA_CLOUDFLARE_KV_NAMESPACE_ID: b38f84f642aa4bd4ba17a37bb516f0b5
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ./.github/actions/setup
- name: Build packages
run: vp run build
- name: Pack vinext packages for local install
working-directory: packages/vinext
run: |
vp pm pack --pack-destination "${{ runner.temp }}"
cd ../types
vp pm pack --pack-destination "${{ runner.temp }}"
cd ../cloudflare
vp pm pack --pack-destination "${{ runner.temp }}"
- name: Scaffold a fresh create-next-app project
run: vp dlx create-next-app@16.2.7 "${{ runner.temp }}/cna-cloudflare" --yes
- name: Pin pnpm in scaffolded project to vinext's version
working-directory: ${{ runner.temp }}/cna-cloudflare
run: |
PKG_MGR=$(cd "${{ github.workspace }}" && node -p "require('./package.json').packageManager")
node -e "
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
pkg.packageManager = '$PKG_MGR';
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
"
- name: Allow required native dependency builds
working-directory: ${{ runner.temp }}/cna-cloudflare
run: |
node -e '
const fs = require("fs");
const f = "pnpm-workspace.yaml";
let y = fs.existsSync(f) ? fs.readFileSync(f, "utf8") : "";
y = y.replace(/allowBuilds:[\s\S]*?(?=\n\S|\n*$)/, "");
y = y.trimEnd() + "\n\nallowBuilds:\n esbuild: true\n sharp: true\n unrs-resolver: true\n workerd: true\n";
fs.writeFileSync(f, y);
'
- name: Install local vinext packages
working-directory: ${{ runner.temp }}/cna-cloudflare
run: |
node "$GITHUB_WORKSPACE/scripts/configure-local-vinext-types.mjs" "${{ runner.temp }}"
vp add "${{ runner.temp }}"/vinext-[0-9]*.tgz "${{ runner.temp }}"/vinext-cloudflare-*.tgz
- name: Run Cloudflare init
working-directory: ${{ runner.temp }}/cna-cloudflare
run: >-
vp exec vinext init --skip-check --platform=cloudflare
--data-cache=kv
--image-optimization=cloudflare-images
- name: Configure generated Cloudflare deployment
working-directory: ${{ runner.temp }}/cna-cloudflare
run: |
node -e '
const fs = require("node:fs");
const configPath = "wrangler.jsonc";
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
config.name = process.env.CNA_CLOUDFLARE_WORKER;
config.preview_urls = true;
for (const namespace of config.kv_namespaces ?? []) {
if (namespace.binding === "VINEXT_KV_CACHE") {
namespace.id = process.env.CNA_CLOUDFLARE_KV_NAMESPACE_ID;
}
}
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
'
- name: Build generated Cloudflare project
working-directory: ${{ runner.temp }}/cna-cloudflare
run: vp exec vinext build
- name: Verify generated Cloudflare build output
working-directory: ${{ runner.temp }}/cna-cloudflare
run: test -f dist/server/wrangler.json
create-vinext-app-cloudflare:
name: create-vinext-app (Cloudflare build)
runs-on: ubuntu-latest
env:
CVA_CLOUDFLARE_WORKER: create-vinext-app-cloudflare
# Public test namespace already used by examples/workers-cache.
CVA_CLOUDFLARE_KV_NAMESPACE_ID: b38f84f642aa4bd4ba17a37bb516f0b5
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ./.github/actions/setup
- name: Build packages
run: vp run build
- name: Pack vinext packages for local install
working-directory: packages/vinext
run: |
vp pm pack --pack-destination "${{ runner.temp }}"
cd ../types
vp pm pack --pack-destination "${{ runner.temp }}"
cd ../cloudflare
vp pm pack --pack-destination "${{ runner.temp }}"
cd ../create-vinext-app
vp pm pack --pack-destination "${{ runner.temp }}"
- name: Scaffold a fresh create-vinext-app project
working-directory: ${{ runner.temp }}
run: >-
npx --yes
--package "${{ runner.temp }}"/create-vinext-app-*.tgz
create-vinext-app "${{ runner.temp }}/cva-cloudflare"
--yes
--skip-install
--disable-git
--platform=cloudflare
--cdn-cache=data-cache
--data-cache=kv
--image-optimization=cloudflare-images
- name: Pin pnpm in scaffolded project to vinext's version
working-directory: ${{ runner.temp }}/cva-cloudflare
run: |
PKG_MGR=$(cd "${{ github.workspace }}" && node -p "require('./package.json').packageManager")
node -e "
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
pkg.packageManager = '$PKG_MGR';
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
"
- name: Allow required native dependency builds
working-directory: ${{ runner.temp }}/cva-cloudflare
run: |
node -e '
const fs = require("fs");
const f = "pnpm-workspace.yaml";
let y = fs.existsSync(f) ? fs.readFileSync(f, "utf8") : "";
y = y.replace(/allowBuilds:[\s\S]*?(?=\n\S|\n*$)/, "");
y = y.trimEnd() + "\n\nallowBuilds:\n esbuild: true\n sharp: true\n unrs-resolver: true\n workerd: true\n";
fs.writeFileSync(f, y);
'
- name: Install local vinext packages
working-directory: ${{ runner.temp }}/cva-cloudflare
run: |
node "$GITHUB_WORKSPACE/scripts/configure-local-vinext-types.mjs" "${{ runner.temp }}"
vp add "${{ runner.temp }}"/vinext-[0-9]*.tgz "${{ runner.temp }}"/vinext-cloudflare-*.tgz
- name: Configure generated Cloudflare deployment
working-directory: ${{ runner.temp }}/cva-cloudflare
run: |
node -e '
const fs = require("node:fs");
const configPath = "wrangler.jsonc";
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
config.name = process.env.CVA_CLOUDFLARE_WORKER;
config.preview_urls = true;
for (const namespace of config.kv_namespaces ?? []) {
if (namespace.binding === "VINEXT_KV_CACHE") {
namespace.id = process.env.CVA_CLOUDFLARE_KV_NAMESPACE_ID;
}
}
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
'
- name: Build generated Cloudflare project
working-directory: ${{ runner.temp }}/cva-cloudflare
run: vp exec vinext build
- name: Verify generated Cloudflare build output
working-directory: ${{ runner.temp }}/cva-cloudflare
run: test -f dist/server/wrangler.json
e2e:
name: E2E (${{ matrix.label }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- project: pages-router
label: pages-router
shardIndex: 1
shardTotal: 1
- project: pages-router-complex
label: pages-router-complex
shardIndex: 1
shardTotal: 1
- project: pages-router-basepath-dev
label: pages-router-basepath-dev
shardIndex: 1
shardTotal: 1
- project: pages-router-basepath
label: pages-router-basepath
shardIndex: 1
shardTotal: 1
- project: app-router
label: app-router 1/3
shardIndex: 1
shardTotal: 3
- project: app-router
label: app-router 2/3
shardIndex: 2
shardTotal: 3
- project: app-router-bfcache
label: app-router-bfcache
shardIndex: 1
shardTotal: 1
- project: app-router-prefetch-searchparams
label: app-router-prefetch-searchparams
shardIndex: 1
shardTotal: 1
- project: app-router
label: app-router 3/3
shardIndex: 3
shardTotal: 3
- project: app-router-isr-prod
label: app-router-isr-prod
shardIndex: 1
shardTotal: 1
- project: app-router-client-cache
label: app-router-client-cache
shardIndex: 1
shardTotal: 1
- project: app-router-chrome-browser-specific
label: app-router-chrome-browser-specific
shardIndex: 1
shardTotal: 1
- project: app-router-webkit-browser-specific
label: app-router-webkit-browser-specific
shardIndex: 1
shardTotal: 1
- project: cloudflare-pages-router
label: cloudflare-pages-router
shardIndex: 1
shardTotal: 1
- project: pages-router-prod
label: pages-router-prod
shardIndex: 1
shardTotal: 1
- project: pages-scroll-restoration
label: pages-scroll-restoration
shardIndex: 1
shardTotal: 1
- project: cloudflare-workers
label: cloudflare-workers
shardIndex: 1
shardTotal: 1
- project: cloudflare-encoded-paths
label: cloudflare-encoded-paths
shardIndex: 1
shardTotal: 1
- project: cloudflare-sentry-app
label: cloudflare-sentry-app
shardIndex: 1
shardTotal: 1
- project: cloudflare-sentry-pages
label: cloudflare-sentry-pages
shardIndex: 1
shardTotal: 1
- project: cloudflare-dev
label: cloudflare-dev
shardIndex: 1
shardTotal: 1
- project: cloudflare-pages-router-dev
label: cloudflare-pages-router-dev
shardIndex: 1
shardTotal: 1
- project: static-export
label: static-export
shardIndex: 1
shardTotal: 1
- project: app-with-src
label: app-with-src
shardIndex: 1
shardTotal: 1
- project: standalone-output
label: standalone-output
shardIndex: 1
shardTotal: 1
- project: root-layout-redirect
label: root-layout-redirect
shardIndex: 1
shardTotal: 1
- project: ppr-impact-demo
label: ppr-impact-demo
shardIndex: 1
shardTotal: 1
- project: app-front-redirect-issue
label: app-front-redirect-issue
shardIndex: 1
shardTotal: 1
- project: web-worker-vinext
label: web-worker-vinext
shardIndex: 1
shardTotal: 1
- project: web-worker-cloudflare
label: web-worker-cloudflare
shardIndex: 1
shardTotal: 1
- project: nextjs-worker-cloudflare
label: nextjs-worker-cloudflare
shardIndex: 1
shardTotal: 1
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ./.github/actions/setup
- name: Build plugin
run: vp run build
# ci.yml owns the playwright browsers cache; nextjs-deploy-suite.yml
# restores it read-only via the same composite action so both workflows
# share a single warm cache entry per Playwright version + browser + OS.
- name: Cache Playwright browsers
id: playwright-cache
uses: ./.github/actions/playwright-cache
with:
browser: ${{ matrix.project == 'app-router-webkit-browser-specific' && 'webkit' || 'chromium' }}
mode: cache
restore-keys: |
playwright-${{ matrix.project == 'app-router-webkit-browser-specific' && 'webkit' || 'chromium' }}-${{ runner.os }}-
# Cache the apt download cache for WebKit's system dependencies.
# `playwright install-deps webkit` always runs `apt-get install`, but with
# the package archives cached we skip the (slow) download step on hits.
- name: Cache apt archives (WebKit deps)
if: ${{ matrix.project == 'app-router-webkit-browser-specific' }}
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cache/apt-archives
key: apt-webkit-${{ runner.os }}-v${{ steps.playwright-cache.outputs.version }}
restore-keys: |
apt-webkit-${{ runner.os }}-
# Install Chromium browser binaries. Skip when the cache is fully hit —
# `playwright install` is a no-op for already-installed browsers but the
# CLI startup itself adds a few seconds per job.
- name: Install Playwright Chromium
if: ${{ matrix.project != 'app-router-webkit-browser-specific' && steps.playwright-cache.outputs.cache-hit != 'true' }}
run: vp exec playwright install chromium
# Install WebKit browser binaries only on cache miss. System deps are
# installed in a separate step below so we can cache them independently.
- name: Install Playwright WebKit (browser only)
if: ${{ matrix.project == 'app-router-webkit-browser-specific' && steps.playwright-cache.outputs.cache-hit != 'true' }}
run: vp exec playwright install webkit
# Install WebKit system dependencies. This always runs because apt
# packages are root-level and not part of ~/.cache/ms-playwright. The
# apt archive cache above keeps this fast on hits by avoiding re-downloads.
# `playwright install-deps` invokes apt-get under sudo internally.
- name: Install Playwright WebKit system dependencies
if: ${{ matrix.project == 'app-router-webkit-browser-specific' }}
run: |
set -euo pipefail
mkdir -p ~/.cache/apt-archives
sudo mkdir -p /var/cache/apt/archives
# Seed apt's archive dir from our cache so apt skips re-downloading
# .deb files it already has from a previous run.
if [ -n "$(ls -A ~/.cache/apt-archives 2>/dev/null)" ]; then
sudo cp -rn ~/.cache/apt-archives/. /var/cache/apt/archives/ || true
fi
# Tell apt not to delete .deb files after install so they survive
# for our post-step cache copy.
echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' | sudo tee /etc/apt/apt.conf.d/01keep-debs >/dev/null
vp exec playwright install-deps webkit
# Persist newly downloaded archives back to our cache directory.
sudo find /var/cache/apt/archives -maxdepth 1 -name '*.deb' -exec cp -n {} ~/.cache/apt-archives/ \;
sudo chown -R "$(id -u):$(id -g)" ~/.cache/apt-archives
- run: vp exec playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
env:
CI: true
PLAYWRIGHT_PROJECT: ${{ matrix.project }}
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: failure()
with:
name: playwright-report-${{ matrix.project }}-${{ matrix.shardIndex }}-${{ matrix.shardTotal }}
path: playwright-report/
retention-days: 7
ci:
name: CI
if: ${{ always() }}
needs:
[
check,
test-unit,
test-integration-merge,
create-next-app,
create-next-app-cloudflare,
create-vinext-app-cloudflare,
e2e,
]
runs-on: ubuntu-latest
steps:
- name: All checks passed
if: ${{ !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }}
run: echo "All checks passed"
- name: Some checks failed
if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
run: exit 1