Skip to content

fix(test): pin React in deploy suite fixtures #12902

fix(test): pin React in deploy suite fixtures

fix(test): pin React in deploy suite fixtures #12902

Workflow file for this run

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 ~10–25% 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: static-export-basepath
label: static-export-basepath
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