Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .drive/projects/prisma-cli-v8/deferred.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,3 +420,14 @@ CLI does not do, and each restarts as engine work if wanted:
move `config-loader` into a shared library package the ORM publishes
for exactly this kind of consumer, or cut the cloud extension's use
of it. Spans both product repos; Will decides which.

## Left open by the dev-build fix (2026-08-17)

The release channel is green as of 2026-08-17: `@prisma/composer-cli@0.7.0` and `@prisma/orm-toolchain@8.0.0-rc.2` are both released and both peer `@prisma/cli-engine@0.1.1`, so the conformance run reports nothing and `packages/cli/scripts/conformance.ts` carries no exceptions. What closed, for the record: composer's `0.6.0` was uninstallable (published out-of-band with `npm publish`, leaving `workspace:0.6.0` in its manifest) and the ORM had no released version carrying the command family.

Still open:

- **Both product repos need their publish-notification step** (the work in the closed composer#232 and prisma#30033): a `repository_dispatch` of type `product-published` to `prisma/prisma-cli`, placed immediately after the publish step and keyed on its outcome. Until then a daily scheduled run is what notices a product release, so a new product version reaches the CLI within a day rather than within minutes. `docs/oss/release-automation.md` carries the exact step, and `DEPLOY_GITHUB_TOKEN` is provisioned in all three repositories (2026-08-17).
- **Neither product repo installs its own tarball before publishing.** That is why an uninstallable `@prisma/composer-cli@0.6.0` sat on `latest` unnoticed. prisma-cli's check 3 does exactly this — pack, install into a clean sandbox with `npm --ignore-scripts`, start every declared bin — and is worth porting to both.
- **The engine-pin check compares for equality, not peer satisfaction.** Both families now declare an exact peer equal to the shell's pin, so equality is correct and stricter today. Widening to range satisfaction belongs with the post-GA move to engine ranges (ADR 0004), not before.
- **`credential-manager.ts` uses the banned word.** `packages/cli/src/auth/credential-manager.ts` has a private `#repin` method (about the active-workspace marker, a different concept from dependency versions). The operator banned the word outright; renaming it is a mechanical change to a private method, left out of the publish-channel work to keep that diff to one subject.
91 changes: 0 additions & 91 deletions .github/workflows/auto-repin.yml

This file was deleted.

8 changes: 8 additions & 0 deletions .github/workflows/pr-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,5 +119,13 @@ jobs:
# Finds a broken tarball on the pull request instead of at publish
# time. The turbo task builds its own dependencies, so no explicit
# build step is needed here.
#
# PUBLISH_CHANNEL=dev because merging a pull request publishes a dev
# version of the CLI, and a dev CLI is allowed to depend on the
# products' dev builds. A release publish sets `release` instead and
# is refused if any dependency is a dev build; that is checked in
# publish.yml, which is the only place a release can happen.
- name: Conformance checks
env:
PUBLISH_CHANNEL: dev
run: pnpm check:conformance
47 changes: 31 additions & 16 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ name: Publish to npm
# Trigger model:
# - push to `main` with the root `version` unchanged → publish
# `<base>-dev.<run>` under the `dev` dist-tag (operator ruling
# 2026-08-13: automated family repins deploy automatically; only a
# real release needs a human). The dev suffix is stamped
# ephemerally below and never committed.
# 2026-08-13: a product's new version reaches the CLI and deploys
# without a human; only a real release needs one). The dev suffix is
# stamped ephemerally below and never committed.
# - push to `main` with the root `version` changed → publish `<base>`
# under its canonical dist-tag — `next` on the RC line, `latest` for
# stable — and create a GitHub Release (marked pre-release on the RC
Expand Down Expand Up @@ -110,10 +110,19 @@ jobs:
PUSH_BEFORE_SHA: ${{ github.event.before }}
run: node scripts/determine-version.ts

# Dev publishes only: stamp `<base>-dev.<run>` across the lockstep
# manifests for this run. Ephemeral — nothing is committed; release
# versions remain exactly what the commit says. The engine is
# excluded from the sweep and ships at its own committed version.
# Dev publishes only, and ephemeral — nothing is committed, so a
# release publishes exactly what the commit says. Two stamps:
#
# 1. `<base>-dev.<run>` across the lockstep manifests. The engine
# is excluded from the sweep and ships at its own committed
# version.
# 2. The product CLI packages move to their `dev` builds. A dev
# CLI depends on the products' latest dev versions and a
# release depends only on their releases (operator ruling
# 2026-08-17), and the committed manifests hold the release
# versions. The build and the conformance checks below run
# after this, so a broken product dev build fails the dev
# publish instead of shipping in it.
- name: Stamp dev version
if: ${{ steps.version.outputs.publish == 'true' && steps.version.outputs.tag == 'dev' }}
# The lockfile refresh is part of the stamp: pnpm verifies
Expand All @@ -123,6 +132,7 @@ jobs:
# bump-version does for committed bumps; still ephemeral.
run: |
node scripts/set-version.ts "${{ steps.version.outputs.version }}"
node scripts/update-product-versions.mjs --channel dev
pnpm install --lockfile-only --no-frozen-lockfile

- name: Build packages
Expand All @@ -141,20 +151,25 @@ jobs:
if: ${{ steps.version.outputs.publish == 'true' }}
run: pnpm test:scripts

# The three conformance checks against what is about to ship:
# built output imports only declared dependencies, every mounted
# config-section validator survives hostile input, and the packed
# The conformance checks against what is about to ship: built
# output imports only declared dependencies, every mounted
# config-section validator survives hostile input, the packed
# tarballs survive a registry consumer's install — clean sandbox,
# npm with --ignore-scripts, unpublished workspace siblings via
# computed file: overrides, every declared bin started on plain
# Node at exit 0, engine pins agreeing everywhere. Runs before
# BOTH publish paths so the dry run is covered too. The tarballs
# it packs land in artifacts/tarballs and are the ones uploaded
# below: what was verified is what ships. (This absorbed the
# interim scripts/tarball-smoke.mjs, which was written to this
# check's design.)
# Node at exit 0, engine pins agreeing everywhere — and a release
# depends on no dev build. Runs before BOTH publish paths so the
# dry run is covered too. The tarballs it packs land in
# artifacts/tarballs and are the ones uploaded below: what was
# verified is what ships.
#
# PUBLISH_CHANNEL is what makes the dev-build check answer
# correctly for this run. It is the run's own dist-tag, so a dev
# publish is allowed its dev builds and a release is not.
- name: Run conformance checks
if: ${{ steps.version.outputs.publish == 'true' }}
env:
PUBLISH_CHANNEL: ${{ steps.version.outputs.tag == 'dev' && 'dev' || 'release' }}
run: pnpm check:conformance

# The verified tarballs, retrievable per run. On a real release the
Expand Down
114 changes: 114 additions & 0 deletions .github/workflows/update-product-versions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
name: Update product versions

# When composer or prisma/prisma publish a release of a package this repo
# depends on, this workflow moves the dependency and opens a pull
# request; merging it ships a dev version of the CLI (operator rulings
# 2026-08-13 and 2026-08-17; docs/oss/release-automation.md). Only a real
# CLI release needs a human.
#
# It follows each product's RELEASE tag, never `dev`. The dev channel
# gets its dev builds from publish.yml's stamp instead, so what this
# workflow commits is always releasable.
#
# Triggers:
# - repository_dispatch `product-published`: sent by the product repos'
# publish workflows the moment they ship.
# - schedule (daily): the backstop for a missed dispatch — the same
# comparison against the registry, so a lost event delays an update
# by at most a day.
# - workflow_dispatch: run it by hand.
#
# The pull request is opened with DEPLOY_GITHUB_TOKEN, not GITHUB_TOKEN:
# pull requests created with the workflow token never trigger CI, and
# without CI the required checks never report, so auto-merge would wait
# forever. See docs/oss/release-automation.md for what the token is and
# how to replace it.

on:
repository_dispatch:
types: [product-published]
schedule:
- cron: "17 6 * * *"
workflow_dispatch:

concurrency:
group: update-product-versions
cancel-in-progress: false

jobs:
update:
name: Compare versions against the registry
runs-on: ubuntu-latest
# Read-only: every write here goes through DEPLOY_GITHUB_TOKEN (the
# branch push, the pull request, auto-merge), and the checkout keeps
# no credentials, so the workflow token needs nothing but the
# checkout itself. Granting it write access would hand a
# write-capable token to every action in the job for no reason.
permissions:
contents: read

steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false

- name: Set up pnpm
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8

- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version-file: .node-version

- name: Compare versions against the registry
id: compare
run: node scripts/update-product-versions.mjs --channel release

# `pnpm install --no-frozen-lockfile` scoped to the lockfile
# refresh: the edit touches two manifests, and the pull request must
# carry the matching lockfile or every CI job fails on
# --frozen-lockfile.
- name: Refresh the lockfile
if: ${{ steps.compare.outputs.changed == 'true' }}
run: |
corepack enable
pnpm install --lockfile-only --no-frozen-lockfile

- name: Open the pull request with auto-merge
if: ${{ steps.compare.outputs.changed == 'true' }}
env:
GH_TOKEN: ${{ secrets.DEPLOY_GITHUB_TOKEN }}
SUMMARY: ${{ steps.compare.outputs.summary }}
run: |
if [ -z "$GH_TOKEN" ]; then
echo "::error::DEPLOY_GITHUB_TOKEN is not configured, so this workflow cannot open a pull request that triggers CI. See docs/oss/release-automation.md."
exit 1
fi
# An open pull request from an earlier run already carries these
# versions or is about to be superseded by this one. Close it
# rather than stacking a second: two open version updates race
# each other's auto-merge and one lands on a stale lockfile.
gh pr list --state open --json number,headRefName \
--jq '.[] | select(.headRefName | startswith("product-versions/")) | .number' \
| while read -r number; do
[ -n "$number" ] || continue
gh pr close "$number" \
--comment "Superseded by a newer product version update." \
--delete-branch
done
BRANCH="product-versions/$(date -u +%Y%m%d%H%M%S)"
git config user.name "willbot"
git config user.email "w.a.madden+machine@gmail.com"
git checkout -b "$BRANCH"
git add packages/cli/package.json packages/prisma/package.json pnpm-lock.yaml
git commit -s --trailer "Signed-off-by: Will Madden <madden@prisma.io>" \
-m "Follow the products' released versions: $SUMMARY" \
-m "Opened automatically (update-product-versions.yml); merging ships a dev version of the CLI. A real release stays a human act."
git push "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" "$BRANCH"
PR_URL=$(gh pr create \
--title "Follow the products' released versions: $SUMMARY" \
--body "Opened automatically by update-product-versions.yml, following the products' release dist-tags ($SUMMARY). Merging publishes a \`dev\` version of the CLI; the conformance checks on this pull request are what stand between a product release and that dev build." \
--base main --head "$BRANCH")
gh pr merge --auto --squash "$PR_URL"
echo "Opened $PR_URL with auto-merge armed."
2 changes: 1 addition & 1 deletion docs/architecture/adrs/0004-engine-version-pinning.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Per edge:

**Exact now, range later.** During the rc line the engine breaks its consumers deliberately, slice by slice, so a version range would be fiction. Post-GA, the recorded destination is widening the peers to a range under a written engine compatibility contract, so a non-breaking engine release ships in the next shell release with zero family republishes. Widening is a deliberate future decision against that contract, not a drift.

**Engine versioning — RULED (operator, 2026-08-13): the engine versions independently.** Under the shell lockstep it previously shared, every CLI release manufactured a new engine version — and with it a family-repin obligation — whether or not the engine changed. Decoupled, an engine version means "the engine changed", the exact peers stay valid between real engine changes, and the repin train runs only when there is something to repin for. Mechanically: the engine is excluded from `set-version.ts`'s lockstep (alongside `@prisma/compute`), follows honest pre-1.0 semver (breaking bumps the minor), and publishes at its own manifest version, with an already-published version treated as a no-op by the publish workflow.
**Engine versioning — RULED (operator, 2026-08-13): the engine versions independently.** Under the shell lockstep it previously shared, every CLI release manufactured a new engine version — and obliged every product to publish a new peer version — whether or not the engine changed. Decoupled, an engine version means "the engine changed", the exact peers stay valid between real engine changes, and the products only have to move when there is a real change to move to. Mechanically: the engine is excluded from `set-version.ts`'s lockstep (alongside `@prisma/compute`), follows honest pre-1.0 semver (breaking bumps the minor), and publishes at its own manifest version, with an already-published version treated as a no-op by the publish workflow.

## Enforcement

Expand Down
Loading
Loading