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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
["runs.main: main.mjs", /^ main: main\.mjs$/m],
["runs.post: post.mjs", /^ post: post\.mjs$/m],
];
for (const name of ["build-command", "install-command", "module", "mode", "stage", "composer-version", "working-directory", "api-url"]) {
for (const name of ["build-command", "install-command", "module", "mode", "stage", "prisma-version", "working-directory", "api-url"]) {
checks.push([`input ${name}`, new RegExp(`^ ${name}:$`, "m")]);
}
for (const name of ["outcome", "build-id"]) {
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ An explicit token always wins over the OIDC exchange.

## How it works

Each run has three phases: install, build, and deploy. Your workflow owns checkout and the toolchain. The action runs your install and build commands exactly as configured, and it never inspects your repository to decide how to build. A repository with no build script — one that runs its source directly — sets `build-command: none` to skip the build phase. The deploy phase hands your built app to the [Prisma Composer](https://github.com/prisma/composer) CLI, running it under Bun. Bun must be on the runner PATH — add `oven-sh/setup-bun@v2` before this action. The generated Prisma deploy workflow includes that step automatically.
Each run has three phases: install, build, and deploy. Your workflow owns checkout and the toolchain. The action runs your install and build commands exactly as configured, and it never inspects your repository to decide how to build. A repository with no build script — one that runs its source directly — sets `build-command: none` to skip the build phase. The deploy phase hands your built app to the Composer commands of the unified [`prisma` CLI](https://www.npmjs.com/package/prisma), running it under Bun. Repositories with a `prisma` devDependency deploy with their own installed version; everything else uses the pinned `prisma-version` fallback fetched via bunx. Bun must be on the runner PATH — add `oven-sh/setup-bun@v2` before this action. The generated Prisma deploy workflow includes that step automatically.

Deploy targets follow your branches:

Expand All @@ -93,7 +93,7 @@ The credential resolves in order: an explicit `PRISMA_SERVICE_TOKEN` from the en
| `module` | `module.ts` | Path to your app's Composer module. |
| `mode` | `deploy` | `deploy` or `destroy`. |
| `stage` | derived | Empty derives the stage from the branch: the default branch deploys to production, any other branch name becomes the stage. `destroy` requires a resolved stage. |
| `composer-version` | `0.7.0` | The Composer CLI version the action fetches via bunx when the repo has no local bin. From 0.7.0 the bin ships in `@prisma/composer-cli`; set to a `0.6.x` value only if you need the old package. |
| `prisma-version` | `8.0.0-rc.7` | The `prisma` package version the action fetches via bunx the fallback for repositories that do not carry the `prisma` devDependency. Repositories that do carry it deploy with their own installed version. |
| `working-directory` | `.` | Where install, build, and deploy run. |
| `api-url` | `https://api.prisma.io` | Prisma API base URL for the OIDC credential exchange. |

Expand Down
6 changes: 3 additions & 3 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ inputs:
stage:
description: Empty derives the stage from the branch (default branch deploys to production, any other branch name becomes the stage)
default: ""
composer-version:
description: The @prisma/composer-cli version the action fetches via bunx when the repo has no local bin (0.7.0+); use @prisma/composer for 0.6.x
default: "0.7.0"
prisma-version:
description: The prisma package version the action fetches via bunx, as a fallback for repositories that do not carry the prisma devDependency
default: "8.0.0-rc.7"
working-directory:
description: Where install, build, and deploy run
default: "."
Expand Down
30 changes: 30 additions & 0 deletions cli.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Returns [cmd, args, logLabel] for running the Composer command family of
* the unified `prisma` CLI under Bun. The bunx fallback covers repositories
* without a `prisma` devDependency.
*
* The local bin runs as `bun run --bun prisma`, not `bun <path>`: `--bun`
* puts a `node` → bun shim first on PATH, so the CLI and the converge child
* it spawns (a `#!/usr/bin/env node` launcher) both run under Bun. `bun
* <absolute path>` runs only the CLI under Bun and leaves the child to Node,
* which cannot resolve the `./service.js` import to `service.ts`.
*
* @param {string} prismaVersion - `prisma` package version to fetch via bunx when the local bin is absent.
* @param {boolean} binExists - Whether node_modules/.bin/prisma exists in the working directory.
* @param {string[]} composerArgs - Arguments after `prisma composer`, e.g. ["deploy", "module.ts", "--stage", "x"].
* @returns {[string, string[], string]}
*/
export function selectPrismaCliCommand(prismaVersion, binExists, composerArgs) {
if (binExists) {
return [
"bun",
["run", "--bun", "prisma", "composer", ...composerArgs],
"composer=local prisma CLI (bun run --bun)",
];
}
return [
"bunx",
["--bun", "-p", `prisma@${prismaVersion}`, "prisma", "composer", ...composerArgs],
`composer=prisma@${prismaVersion} (bunx fallback)`,
];
}
23 changes: 0 additions & 23 deletions composer.mjs

This file was deleted.

30 changes: 12 additions & 18 deletions main.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { spawnSync } from "node:child_process";
import { appendFileSync, existsSync, readFileSync, writeSync } from "node:fs";
import { join, resolve } from "node:path";
import { selectBuildCommand } from "./build.mjs";
import { selectComposerCommand } from "./composer.mjs";
import { selectPrismaCliCommand } from "./cli.mjs";
import { resolveCredential } from "./credentials.mjs";
import { deployedUrlFromOutput } from "./deployment.mjs";
import { guardReport, makeReporter, mapPhase } from "./report.mjs";
Expand Down Expand Up @@ -103,7 +103,7 @@ async function runPhase(phase, command, args, capture = false) {

const mode = input("mode") || "deploy";
const modulePath = input("module") || "module.ts";
const composerVersion = input("composer-version") || "0.7.0";
const prismaVersion = input("prisma-version") || "8.0.0-rc.7";
const workdir = resolve(process.env.GITHUB_WORKSPACE ?? ".", input("working-directory") || ".");
const repository = process.env.GITHUB_REPOSITORY ?? "";
const branch = process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || "";
Expand Down Expand Up @@ -245,25 +245,19 @@ if (buildCommand === null) {
// The stage reaches the argv array straight from the environment; it is
// never interpolated into a shell string.
//
// The CLI bin is named prisma-composer. From 0.7.0 it ships inside
// @prisma/composer-cli (previously @prisma/composer); there is no npm package
// named prisma-composer, so `npx prisma-composer@v` 404s. The repo's own
// install already provides the bin, so prefer the local bin under Bun; fall
// back to bunx fetching the pinned package when the repo does not carry it.
const localBin = join(workdir, "node_modules", ".bin", "prisma-composer");
const [composerCmd, composerLead, composerLabel] = selectComposerCommand(
composerVersion,
const localBin = join(workdir, "node_modules", ".bin", "prisma");
const composerArgs =
mode === "deploy"
? ["deploy", modulePath, ...(stage ? ["--stage", stage] : [])]
: ["destroy", modulePath, "--stage", stage];
const [cliCmd, cliArgs, cliLabel] = selectPrismaCliCommand(
prismaVersion,
existsSync(localBin),
composerArgs,
);
log(composerLabel);
const composerArgs = [
...composerLead,
...(mode === "deploy"
? ["deploy", modulePath, ...(stage ? ["--stage", stage] : [])]
: ["destroy", modulePath, "--stage", stage]),
];
log(cliLabel);

const deployOutput = await runPhase(mode, composerCmd, composerArgs, mode === "deploy");
const deployOutput = await runPhase(mode, cliCmd, cliArgs, mode === "deploy");

if (reporter && buildId) {
const deployedUrl = mode === "deploy" ? deployedUrlFromOutput(deployOutput) : null;
Expand Down
74 changes: 74 additions & 0 deletions tests/cli.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import { selectPrismaCliCommand } from "../cli.mjs";

test("runs the local prisma CLI with bun run --bun when the bin exists", () => {
const [cmd, args, label] = selectPrismaCliCommand("8.0.0-rc.7", true, ["deploy", "module.ts"]);
assert.equal(cmd, "bun");
assert.deepEqual(args, ["run", "--bun", "prisma", "composer", "deploy", "module.ts"]);
assert.equal(label, "composer=local prisma CLI (bun run --bun)");
});

test("falls back to bunx with the input version when the bin is absent", () => {
const [cmd, args, label] = selectPrismaCliCommand("8.0.0-rc.7", false, ["deploy", "module.ts"]);
assert.equal(cmd, "bunx");
assert.deepEqual(args, [
"--bun",
"-p",
"prisma@8.0.0-rc.7",
"prisma",
"composer",
"deploy",
"module.ts",
]);
assert.equal(label, "composer=prisma@8.0.0-rc.7 (bunx fallback)");
});

test("deploy args pass through verbatim on the local CLI, stage included", () => {
const [, args] = selectPrismaCliCommand("8.1.0", true, [
"deploy",
"app/module.ts",
"--stage",
"feat/x",
]);
assert.deepEqual(args, [
"run",
"--bun",
"prisma",
"composer",
"deploy",
"app/module.ts",
"--stage",
"feat/x",
]);
});

test("destroy args pass through verbatim on the bunx fallback", () => {
const [, args] = selectPrismaCliCommand("8.1.0", false, [
"destroy",
"module.ts",
"--stage",
"feat/x",
]);
assert.deepEqual(args, [
"--bun",
"-p",
"prisma@8.1.0",
"prisma",
"composer",
"destroy",
"module.ts",
"--stage",
"feat/x",
]);
});

test("bunx fallback log label includes the exact version", () => {
const [, , label] = selectPrismaCliCommand("8.2.0", false, ["deploy", "module.ts"]);
assert.equal(label, "composer=prisma@8.2.0 (bunx fallback)");
});

test("local CLI label is always the same string regardless of version", () => {
const [, , label] = selectPrismaCliCommand("8.2.0", true, ["deploy", "module.ts"]);
assert.equal(label, "composer=local prisma CLI (bun run --bun)");
});
32 changes: 0 additions & 32 deletions tests/composer.test.mjs

This file was deleted.

Loading