Files
Reinier van der LeerandClaude Opus 5.5 0b6912505b ci(platform): cache the Prisma CLI and retry its download in backend and full-stack CI (#16070)
Backend CI and full-stack CI now cache the Prisma CLI between runs and
retry its download, so a dropped connection while fetching it no longer
fails a run. Resolves #16066

### Why / What / How

**Why:** every backend job downloads the Prisma CLI and its engines
before it can generate the Prisma client, and a run fails when that
download loses its connection. "Generate Prisma Client" was the first
failed step in 7 of 201 failed Backend CI runs from 16 to 23 Oct: two
merge-queue runs (39670924275, 39821302302), whose failure ejected a PR,
and a `dev` push (40025378565). Each failed in prisma-python's `npm
install [email protected].0`, where `@prisma/engines`' postinstall lost its
connection to Prisma's engine host (`Error: aborted`, `ECONNRESET`).
Nothing cached the CLI and nothing retried it.

**What:** the backend type-check and test jobs and full-stack CI's
API-type check now generate the client through one composite action. It
restores the CLI directory from the Actions cache, so a warm job
downloads nothing. On a cold job, the CLI install is tried up to three
times, 15 s and then 30 s apart, before the step fails.

**How:** `prisma py version --json` reports the directory prisma-python
installs the CLI and engines into, along with the CLI version, engine
hash and platform. The cache's path and key come from that output, so no
Prisma version is written in the workflows and a prisma-python bump
changes the key by itself. The retried command is `prisma --version`,
which installs the CLI and fetches every engine. Once it has succeeded,
`prisma generate` and `prisma migrate` make no network calls, so a
schema error fails at once rather than three times.

### Changes 🏗️

- `.github/actions/generate-prisma-client/action.yml`: resolves the
cache path and key, restores and saves the cache, installs the CLI with
the retry, then runs `prisma generate` and `gen-prisma-stub`.
- `.github/scripts/install-prisma-cli-with-retry.sh`: the retry, in the
shape of `docker-pull-with-retry.sh` from #15978.
- `.github/scripts/test_install_prisma_cli_retry.py`: success after 0, 1
or 2 failures, and a terminal failure that exits 1. Like
`test_docker_pull_retry.py`, no workflow runs it.
- `platform-backend-ci.yml` (two jobs) and `platform-fullstack-ci.yml`
use the action, and their path filters include it and the script.
- `.github/dependabot.yml`: the github-actions entry also scans the new
action's folder. `"/"` covers only `.github/workflows`, so the action's
`actions/cache` pin would otherwise never be bumped.

Settled choices:
- **A composite action** rather than three inline copies, so the key is
defined in one place. `.github/actions/start-minio` is the precedent.
- **Named `generate-prisma-client`**, because the root `.gitignore`'s
`pri*` ignores any path starting `prisma-`.
- **Three attempts**, because the failures were single dropped
connections rather than outages. The Docker pull retry uses five for
Docker Hub's 20 s refusals.
- **Not covered:** `docs-block-sync.yml`, `copilot-setup-steps.yml`,
`platform-preview-seed-fixture.yml` and both deploy workflows still run
a bare `prisma generate`.
- **Merge-queue runs:** GitHub documents no cache scoping for them.
Whether they can read `dev`'s entry or only `master`'s is unknown, and
the retry covers them either way.

The CI workflows are mostly Nick's area, so he is the natural reviewer.

**Verified:** I ran the action's resolve step verbatim, and it printed
the cache directory and the key
`prisma-cli-Linux-X64-<platform>-5.17.0-393aa359…`. I ran the real retry
script against a cold CLI directory twice. With the `@prisma/engines`
postinstall failing once, CI's failure, it retried after 15 s and
installed. With the postinstall always failing, it exited 1 after three
attempts. Locally I showed that once `prisma --version` has run, `prisma
generate` succeeds with both the npm registry and the engine host
pointed at a dead port. The script's two tests pass, and each of three
mutations turns them red (two attempts, exit 0 on the last failure, a
different delay). In this PR's first CI run, all 16 jobs using the
action missed the cache and installed on the first attempt, and the
action saved the entry. A re-run of `type-check (3.12)` restored it and
installed nothing, and on the next head all 16 jobs restored it. Per-job
evidence is in a comment. The Dependabot entry is only reasoned about:
Dependabot reads it after the change reaches `dev`.

### Agents and large language models used

- Claude Code with Claude Opus 5.5

### Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
  - [x] The resolve step prints the cache directory and key locally
- [x] The retry script recovers from one failed CLI install and exits 1
after three
  - [x] `prisma generate` needs no network once the CLI is installed
- [x] This PR's CI: every job generates the client, and the first run
saves the cache
- [x] A re-run of one job restores the cache and logs no "Installing
Prisma CLI"

#### For configuration changes:

- [x] `.env.default` is updated or already compatible with my changes
- [x] `docker-compose.yml` is updated or already compatible with my
changes
- [x] I have included a list of my configuration changes in the PR
description (under **Changes**)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 (Claude Code) <[email protected]>
2026-11-04 04:27:12 +00:00
..