Skip to content

Publish CLI

Publish CLI #24

Workflow file for this run

name: Release CLI
on:
workflow_dispatch:
inputs:
dry_run:
description: Validate the release without publishing, tagging, or pushing
required: false
type: boolean
default: false
concurrency:
group: release-cli-main
cancel-in-progress: false
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- name: Ensure workflow runs from main
run: |
if [ "${GITHUB_REF}" != "refs/heads/main" ]; then
echo "This workflow only releases from main."
exit 1
fi
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: pnpm/action-setup@v5
with:
version: 10.30.0
- uses: actions/setup-node@v6
with:
node-version: 24
registry-url: https://registry.npmjs.org
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Resolve CLI version
id: cli_version
run: |
VERSION="$(node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('packages/cli/package.json', 'utf8')).version)")"
printf 'version=%s\n' "${VERSION}" >> "$GITHUB_OUTPUT"
- name: Fail if version already exists on npm
run: |
PACKAGE='@prisma/cli'
VERSION='${{ steps.cli_version.outputs.version }}'
if npm view "${PACKAGE}@${VERSION}" version >/dev/null 2>&1; then
echo "${PACKAGE}@${VERSION} already exists on npm."
exit 1
fi
- name: Fail if release tag already exists
run: |
VERSION='${{ steps.cli_version.outputs.version }}'
TAG="cli-v${VERSION}"
if git ls-remote --exit-code --tags origin "refs/tags/${TAG}" >/dev/null 2>&1; then
echo "Release tag ${TAG} already exists."
exit 1
fi
- name: Run focused CLI tests
run: pnpm --filter @prisma/cli test
- name: Build CLI package
run: pnpm --filter @prisma/cli build
- name: Prepare staged publish package
run: node scripts/prepare-cli-publish.mjs .publish/cli
- name: Audit staged package contents
working-directory: .publish/cli
run: |
PACK_JSON="$(npm pack --dry-run --json)"
PACK_JSON="${PACK_JSON}" node -e "
const pack = JSON.parse(process.env.PACK_JSON)[0]
const files = pack.files.map((file) => file.path).sort()
const forbidden = files.filter((file) =>
file.startsWith('src/') ||
file.startsWith('tests/') ||
file.startsWith('fixtures/') ||
file.startsWith('docs/') ||
file.startsWith('.prisma/') ||
file.startsWith('.publish/')
)
if (forbidden.length) {
console.error('Forbidden files in npm package:', forbidden.join(', '))
process.exit(1)
}
for (const required of ['dist/cli.js', 'README.md', 'LICENSE', 'package.json']) {
if (!files.includes(required)) {
console.error('Missing required package file:', required)
process.exit(1)
}
}
"
- name: Smoke test staged tarball install
run: |
TARBALL="$(cd .publish/cli && npm pack --silent)"
TMPDIR="$(mktemp -d)"
cat > "${TMPDIR}/package.json" <<'EOF'
{
"name": "cli-publish-smoke",
"private": true
}
EOF
pnpm add -D "${PWD}/.publish/cli/${TARBALL}" --dir "${TMPDIR}"
(
cd "${TMPDIR}"
pnpm prisma-cli --help
pnpm prisma-cli auth whoami --json
)
- name: Ensure release still targets the latest main
if: ${{ !inputs.dry_run }}
run: |
git fetch origin main
if [ "$(git rev-parse HEAD)" != "$(git rev-parse origin/main)" ]; then
echo "main moved while the release was running. Rerun the workflow from the latest main."
exit 1
fi
- name: Publish to npm
if: ${{ !inputs.dry_run }}
working-directory: .publish/cli
run: npm publish --access public --tag preview --provenance
- name: Create and push release tag
if: ${{ !inputs.dry_run }}
run: |
VERSION='${{ steps.cli_version.outputs.version }}'
TAG="cli-v${VERSION}"
git tag "${TAG}"
git push origin "refs/tags/${TAG}"
- name: Summarize release
run: |
VERSION='${{ steps.cli_version.outputs.version }}'
{
echo "## Release CLI"
echo
echo "- Version: \`${VERSION}\`"
echo "- Dry run: \`${{ inputs.dry_run }}\`"
if [ '${{ inputs.dry_run }}' = 'true' ]; then
echo "- Publish: skipped"
echo "- Tag/push: skipped"
else
echo "- npm package: \`@prisma/cli@${VERSION}\`"
echo "- npm dist-tag: \`preview\`"
echo "- git tag: \`cli-v${VERSION}\`"
fi
} >> "$GITHUB_STEP_SUMMARY"