build extensions can now install custom packages and run instructions in the image. Also prisma extension now works and also works with multiple schema files

This commit is contained in:
Eric Allam
2024-08-21 16:20:41 +01:00
committed by Eric Allam
parent 97fce66253
commit cacd13b1ab
19 changed files with 380 additions and 67 deletions
+26 -11
View File
@@ -126,37 +126,37 @@ function applyLayerToManifest(layer: BuildLayer, manifest: BuildManifest): Build
let $manifest = { ...manifest };
if (layer.commands) {
manifest.build.commands ??= [];
manifest.build.commands = manifest.build.commands.concat(layer.commands);
$manifest.build.commands ??= [];
$manifest.build.commands = $manifest.build.commands.concat(layer.commands);
}
if (layer.build?.env) {
manifest.build.env ??= {};
Object.assign(manifest.build.env, layer.build.env);
$manifest.build.env ??= {};
Object.assign($manifest.build.env, layer.build.env);
}
if (layer.deploy?.env) {
manifest.deploy.env ??= {};
manifest.deploy.sync ??= {};
manifest.deploy.sync.env ??= {};
$manifest.deploy.env ??= {};
$manifest.deploy.sync ??= {};
$manifest.deploy.sync.env ??= {};
for (const [key, value] of Object.entries(layer.deploy.env)) {
if (!value) {
continue;
}
if (layer.deploy.override || manifest.deploy.env[key] === undefined) {
const existingValue = manifest.deploy.env[key];
if (layer.deploy.override || $manifest.deploy.env[key] === undefined) {
const existingValue = $manifest.deploy.env[key];
if (existingValue !== value) {
manifest.deploy.sync.env[key] = value;
$manifest.deploy.sync.env[key] = value;
}
}
}
}
if (layer.dependencies) {
const externals = manifest.externals ?? [];
const externals = $manifest.externals ?? [];
for (const [name, version] of Object.entries(layer.dependencies)) {
externals.push({ name, version });
@@ -165,6 +165,21 @@ function applyLayerToManifest(layer: BuildLayer, manifest: BuildManifest): Build
$manifest.externals = externals;
}
if (layer.image) {
$manifest.image ??= {};
$manifest.image.instructions ??= [];
$manifest.image.pkgs ??= [];
if (layer.image.instructions) {
$manifest.image.instructions = $manifest.image.instructions.concat(layer.image.instructions);
}
if (layer.image.pkgs) {
$manifest.image.pkgs = $manifest.image.pkgs.concat(layer.image.pkgs);
$manifest.image.pkgs = Array.from(new Set($manifest.image.pkgs));
}
}
return $manifest;
}
+2
View File
@@ -378,6 +378,7 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
apiKey: projectClient.client.accessToken!,
authAccessToken: authorization.auth.accessToken,
compilationPath: destination.path,
buildEnvVars: buildManifest.build.env,
});
logger.debug("Build result", buildResult);
@@ -564,6 +565,7 @@ async function writeContainerfile(outputPath: string, buildManifest: BuildManife
runtime: buildManifest.runtime,
entrypoint: buildManifest.runControllerEntryPoint,
build: buildManifest.build,
image: buildManifest.image,
indexScript: buildManifest.indexControllerEntryPoint,
});
+41 -5
View File
@@ -34,6 +34,7 @@ export interface BuildImageOptions {
extraCACerts?: string;
apiUrl: string;
apiKey: string;
buildEnvVars?: Record<string, string | undefined>;
// Optional deployment spinner
deploymentSpinner?: any; // Replace 'any' with the actual type if known
@@ -62,6 +63,7 @@ export async function buildImage(options: BuildImageOptions) {
extraCACerts,
apiUrl,
apiKey,
buildEnvVars,
} = options;
if (selfHosted) {
@@ -81,6 +83,7 @@ export async function buildImage(options: BuildImageOptions) {
extraCACerts: extraCACerts,
apiUrl,
apiKey,
buildEnvVars,
});
}
@@ -109,6 +112,7 @@ export async function buildImage(options: BuildImageOptions) {
extraCACerts,
apiUrl,
apiKey,
buildEnvVars,
});
}
@@ -131,6 +135,7 @@ export interface DepotBuildImageOptions {
loadImage?: boolean;
noCache?: boolean;
extraCACerts?: string;
buildEnvVars?: Record<string, string | undefined>;
}
type BuildImageSuccess = {
@@ -156,6 +161,10 @@ async function depotBuildImage(options: DepotBuildImageOptions): Promise<BuildIm
password: options.auth,
});
const buildArgs = Object.entries(options.buildEnvVars || {})
.filter(([key, value]) => value)
.flatMap(([key, value]) => ["--build-arg", `${key}=${value}`]);
const args = [
"build",
"-f",
@@ -179,6 +188,7 @@ async function depotBuildImage(options: DepotBuildImageOptions): Promise<BuildIm
`TRIGGER_API_URL=${options.apiUrl}`,
"--build-arg",
`TRIGGER_SECRET_KEY=${options.apiKey}`,
...(buildArgs || []),
...(options.extraCACerts ? ["--build-arg", `NODE_EXTRA_CA_CERTS=${options.extraCACerts}`] : []),
"--progress",
"plain",
@@ -266,6 +276,7 @@ interface SelfHostedBuildImageOptions {
apiKey: string;
noCache?: boolean;
extraCACerts?: string;
buildEnvVars?: Record<string, string | undefined>;
}
async function selfHostedBuildImage(
@@ -273,7 +284,11 @@ async function selfHostedBuildImage(
): Promise<BuildImageResults> {
const imageRef = `${options.registryHost ? `${options.registryHost}/` : ""}${options.imageTag}`;
const buildArgs = [
const buildArgs = Object.entries(options.buildEnvVars || {})
.filter(([key, value]) => value)
.flatMap(([key, value]) => ["--build-arg", `${key}=${value}`]);
const args = [
"build",
"-f",
"Containerfile",
@@ -294,6 +309,7 @@ async function selfHostedBuildImage(
`TRIGGER_API_URL=${options.apiUrl}`,
"--build-arg",
`TRIGGER_SECRET_KEY=${options.apiKey}`,
...(buildArgs || []),
...(options.extraCACerts ? ["--build-arg", `NODE_EXTRA_CA_CERTS=${options.extraCACerts}`] : []),
"--progress",
"plain",
@@ -302,7 +318,7 @@ async function selfHostedBuildImage(
".", // The build context
].filter(Boolean) as string[];
logger.debug(`docker ${buildArgs.join(" ")}`, {
logger.debug(`docker ${args.join(" ")}`, {
cwd: options.cwd,
});
@@ -310,7 +326,7 @@ async function selfHostedBuildImage(
let digest: string | undefined;
// Build the image
const buildProcess = x("docker", buildArgs, {
const buildProcess = x("docker", args, {
nodeOptions: { cwd: options.cwd },
});
@@ -408,10 +424,13 @@ function extractImageDigest(outputs: string[]) {
export type GenerateContainerfileOptions = {
runtime: BuildRuntime;
build: BuildManifest["build"];
image: BuildManifest["image"];
indexScript: string;
entrypoint: string;
};
const DEFAULT_PACKAGES = ["busybox", "ca-certificates", "dumb-init", "git", "openssl"];
export async function generateContainerfile(options: GenerateContainerfileOptions) {
switch (options.runtime) {
case "node": {
@@ -434,9 +453,18 @@ async function generateBunContainerfile(options: GenerateContainerfileOptions) {
const postInstallCommands = (options.build.commands || []).map((cmd) => `RUN ${cmd}`).join("\n");
const baseInstructions = (options.image?.instructions || []).join("\n");
const packages = Array.from(new Set(DEFAULT_PACKAGES.concat(options.image?.pkgs || []))).join(
" "
);
return `
FROM imbios/bun-node:22-debian AS base
RUN apt-get update && apt-get --fix-broken install -y && apt-get install -y --no-install-recommends busybox ca-certificates dumb-init git openssl && apt-get clean && rm -rf /var/lib/apt/lists/*
${baseInstructions}
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get --fix-broken install -y && apt-get install -y --no-install-recommends ${packages} && apt-get clean && rm -rf /var/lib/apt/lists/*
FROM base AS build
@@ -530,10 +558,18 @@ async function generateNodeContainerfile(options: GenerateContainerfileOptions)
const postInstallCommands = (options.build.commands || []).map((cmd) => `RUN ${cmd}`).join("\n");
const baseInstructions = (options.image?.instructions || []).join("\n");
const packages = Array.from(new Set(DEFAULT_PACKAGES.concat(options.image?.pkgs || []))).join(
" "
);
return `
FROM node:21-bookworm-slim@sha256:99afef5df7400a8d118e0504576d32ca700de5034c4f9271d2ff7c91cc12d170 AS base
${baseInstructions}
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get --fix-broken install -y && apt-get install -y --no-install-recommends busybox ca-certificates dumb-init git openssl && apt-get clean && rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get --fix-broken install -y && apt-get install -y --no-install-recommends ${packages} && apt-get clean && rm -rf /var/lib/apt/lists/*
FROM base AS build
+4
View File
@@ -56,6 +56,10 @@ export interface BuildLayer {
id: string;
commands?: string[];
files?: Record<string, string>;
image?: {
pkgs?: string[];
instructions?: string[];
};
build?: {
env?: Record<string, string | undefined>;
};
@@ -0,0 +1,59 @@
import { BuildContext, BuildExtension } from "../build/extensions.js";
import { BuildManifest } from "../schemas/build.js";
export type AudioWaveformOptions = {
version?: string;
checksum?: string;
};
const AUDIOWAVEFORM_VERSION = "1.10.1";
const AUDIOWAVEFORM_CHECKSUM =
"sha256:00b41ea4d6e7a5b4affcfe4ac99951ec89da81a8cba40af19e9b98c3a8f9b4b8";
export function audioWaveform(options: AudioWaveformOptions = {}): BuildExtension {
return new AudioWaveformExtension();
}
class AudioWaveformExtension implements BuildExtension {
public readonly name = "AudioWaveformExtension";
constructor(private options: AudioWaveformOptions = {}) {}
async onBuildComplete(context: BuildContext, manifest: BuildManifest) {
if (context.target === "dev") {
return;
}
const opts = this.options.version
? {
version: this.options.version,
checksum: this.options.checksum,
}
: {
version: AUDIOWAVEFORM_VERSION,
checksum: AUDIOWAVEFORM_CHECKSUM,
};
context.logger.debug("Adding audiowaveform to the build", {
...opts,
});
const instructions = [
`ADD ${
opts.checksum ? `--checksum=${opts.checksum}` : ""
} https://github.com/bbc/audiowaveform/releases/download/${opts.version}/audiowaveform_${
opts.version
}-1-12_amd64.deb .`,
`RUN dpkg -i audiowaveform_${opts.version}-1-12_amd64.deb || true`,
`RUN rm audiowaveform*.deb`,
];
context.addLayer({
id: "audiowaveform",
image: {
pkgs: ["sox"],
instructions,
},
});
}
}
+1
View File
@@ -3,3 +3,4 @@ export * from "./additionalFiles.js";
export * from "./additionalPackages.js";
export * from "./prisma.js";
export * from "./syncEnvVars.js";
export * from "./audioWaveform.js";
+96 -33
View File
@@ -1,19 +1,24 @@
import assert from "node:assert";
import { existsSync } from "node:fs";
import { cp } from "node:fs/promises";
import { join, resolve } from "node:path";
import { cp, readdir } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { BuildContext, BuildExtension } from "../build/extensions.js";
import { BuildManifest, BuildTarget } from "../schemas/build.js";
import { binaryForRuntime } from "../build/runtime.js";
import { BuildManifest, BuildTarget } from "../schemas/build.js";
export type PrismaExtensionOptions = {
schema: string;
migrate?: boolean;
version?: string;
directUrlEnvVarName?: string;
};
const BINARY_TARGET = "linux-arm64-openssl-3.0.x";
export function prismaExtension(options: PrismaExtensionOptions): PrismaExtension {
return new PrismaExtension(options);
}
export class PrismaExtension implements BuildExtension {
moduleExternals: string[];
@@ -76,51 +81,109 @@ export class PrismaExtension implements BuildExtension {
context.logger.debug(`PrismaExtension is generating the Prisma client for version ${version}`);
// Now we need to add a layer that:
// Copies the prisma schema to the build outputPath
// Adds the `prisma` CLI dependency to the dependencies
// Adds the `prisma generate` command, which generates the Prisma client
const schemaDestinationPath = join(manifest.outputPath, "prisma", "schema.prisma");
// Copy the prisma schema to the build output path
context.logger.debug(
`Copying the prisma schema from ${this._resolvedSchemaPath} to ${schemaDestinationPath}`
);
const usingSchemaFolder = dirname(this._resolvedSchemaPath).endsWith("schema");
await cp(this._resolvedSchemaPath, schemaDestinationPath);
const commands: string[] = [];
const commands = [
`${binaryForRuntime(
manifest.runtime
)} node_modules/prisma/build/index.js generate --schema=./prisma/schema.prisma`,
];
let prismaDir: string | undefined;
if (usingSchemaFolder) {
const schemaDir = dirname(this._resolvedSchemaPath);
prismaDir = dirname(schemaDir);
context.logger.debug(`Using the schema folder: ${schemaDir}`);
// Find all the files in schemaDir that end with .prisma (excluding the schema.prisma file)
const prismaFiles = await readdir(schemaDir).then((files) =>
files.filter((file) => file.endsWith(".prisma"))
);
context.logger.debug(`Found prisma files in the schema folder`, {
prismaFiles,
});
const schemaDestinationPath = join(manifest.outputPath, "prisma", "schema");
const allPrismaFiles = [...prismaFiles];
for (const file of allPrismaFiles) {
const destination = join(schemaDestinationPath, file);
const source = join(schemaDir, file);
context.logger.debug(`Copying the prisma schema from ${source} to ${destination}`);
await cp(source, destination);
}
commands.push(
`${binaryForRuntime(manifest.runtime)} node_modules/prisma/build/index.js generate` // Don't add the --schema flag or this will fail
);
} else {
prismaDir = dirname(this._resolvedSchemaPath);
// Now we need to add a layer that:
// Copies the prisma schema to the build outputPath
// Adds the `prisma` CLI dependency to the dependencies
// Adds the `prisma generate` command, which generates the Prisma client
const schemaDestinationPath = join(manifest.outputPath, "prisma", "schema.prisma");
// Copy the prisma schema to the build output path
context.logger.debug(
`Copying the prisma schema from ${this._resolvedSchemaPath} to ${schemaDestinationPath}`
);
await cp(this._resolvedSchemaPath, schemaDestinationPath);
commands.push(
`${binaryForRuntime(
manifest.runtime
)} node_modules/prisma/build/index.js generate --schema=./prisma/schema.prisma`
);
}
const env: Record<string, string | undefined> = {};
if (this.options.migrate) {
// Copy the migrations directory to the build output path
const migrationsDir = join(prismaDir, "migrations");
const migrationsDestinationPath = join(manifest.outputPath, "prisma", "migrations");
context.logger.debug(
`Copying the prisma migrations from ${migrationsDir} to ${migrationsDestinationPath}`
);
await cp(migrationsDir, migrationsDestinationPath, { recursive: true });
commands.push(
`${binaryForRuntime(manifest.runtime)} node_modules/prisma/build/index.js migrate deploy`
);
env.DATABASE_URL = manifest.deploy.env?.DATABASE_URL;
if (this.options.directUrlEnvVarName) {
env[this.options.directUrlEnvVarName] =
manifest.deploy.env?.[this.options.directUrlEnvVarName];
} else {
env.DIRECT_URL = manifest.deploy.env?.DIRECT_URL;
}
}
context.logger.debug(`Adding the prisma layer with the following commands`, {
commands,
env,
dependencies: {
prisma: version,
},
});
context.addLayer({
id: "prisma",
commands,
dependencies: {
prisma: version,
},
build: this.options.migrate
? {
env: {
DATABASE_URL: manifest.deploy.env?.DATABASE_URL,
DATABASE_DIRECT_URL:
manifest.deploy.env?.DATABASE_DIRECT_URL ??
manifest.deploy.env?.DIRECT_URL ??
manifest.deploy.env?.DATABASE_URL,
DIRECT_URL:
manifest.deploy.env?.DATABASE_DIRECT_URL ??
manifest.deploy.env?.DIRECT_URL ??
manifest.deploy.env?.DATABASE_URL,
},
}
: {},
build: {
env,
},
});
}
}
+6
View File
@@ -52,6 +52,12 @@ export const BuildManifest = z.object({
})
.optional(),
}),
image: z
.object({
pkgs: z.array(z.string()).optional(),
instructions: z.array(z.string()).optional(),
})
.optional(),
otelImportHook: z
.object({
include: z.array(z.string()).optional(),
+54
View File
@@ -1354,6 +1354,9 @@ importers:
'@opentelemetry/api':
specifier: 1.4.1
version: 1.4.1
'@prisma/client':
specifier: 5.18.0
version: 5.18.0(prisma@5.18.0)
'@react-email/components':
specifier: ^0.0.17
version: 0.0.17(@types/react@18.3.1)(react@18.2.0)
@@ -1475,6 +1478,9 @@ importers:
esbuild:
specifier: ^0.19.11
version: 0.19.11
prisma:
specifier: 5.18.0
version: 5.18.0
trigger.dev:
specifier: workspace:*
version: link:../../packages/cli-v3
@@ -7878,6 +7884,19 @@ packages:
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
dev: false
/@prisma/client@5.18.0(prisma@5.18.0):
resolution: {integrity: sha512-BWivkLh+af1kqC89zCJYkHsRcyWsM8/JHpsDMM76DjP3ZdEquJhXa4IeX+HkWPnwJ5FanxEJFZZDTWiDs/Kvyw==}
engines: {node: '>=16.13'}
requiresBuild: true
peerDependencies:
prisma: '*'
peerDependenciesMeta:
prisma:
optional: true
dependencies:
prisma: 5.18.0
dev: false
/@prisma/client@5.4.1(prisma@5.4.1):
resolution: {integrity: sha512-xyD0DJ3gRNfLbPsC+YfMBBuLJtZKQfy1OD2qU/PZg+HKrr7SO+09174LMeTlWP0YF2wca9LxtVd4HnAiB5ketQ==}
engines: {node: '>=16.13'}
@@ -7892,14 +7911,41 @@ packages:
prisma: 5.4.1
dev: false
/@prisma/debug@5.18.0:
resolution: {integrity: sha512-f+ZvpTLidSo3LMJxQPVgAxdAjzv5OpzAo/eF8qZqbwvgi2F5cTOI9XCpdRzJYA0iGfajjwjOKKrVq64vkxEfUw==}
/@prisma/engines-version@5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169:
resolution: {integrity: sha512-a/+LpJj8vYU3nmtkg+N3X51ddbt35yYrRe8wqHTJtYQt7l1f8kjIBcCs6sHJvodW/EK5XGvboOiwm47fmNrbgg==}
/@prisma/engines-version@5.4.1-1.2f302df92bd8945e20ad4595a73def5b96afa54f:
resolution: {integrity: sha512-+nUQM/y8C+1GG5Ioeqcu6itFslCfxvQSAUVSMC9XM2G2Fcq0F4Afnp6m0pXF6X6iUBWen7jZBPmM9Qlq4Nr3/A==}
dev: false
/@prisma/engines@5.18.0:
resolution: {integrity: sha512-ofmpGLeJ2q2P0wa/XaEgTnX/IsLnvSp/gZts0zjgLNdBhfuj2lowOOPmDcfKljLQUXMvAek3lw5T01kHmCG8rg==}
requiresBuild: true
dependencies:
'@prisma/debug': 5.18.0
'@prisma/engines-version': 5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169
'@prisma/fetch-engine': 5.18.0
'@prisma/get-platform': 5.18.0
/@prisma/engines@5.4.1:
resolution: {integrity: sha512-vJTdY4la/5V3N7SFvWRmSMUh4mIQnyb/MNoDjzVbh9iLmEC+uEykj/1GPviVsorvfz7DbYSQC4RiwmlEpTEvGA==}
requiresBuild: true
/@prisma/fetch-engine@5.18.0:
resolution: {integrity: sha512-I/3u0x2n31rGaAuBRx2YK4eB7R/1zCuayo2DGwSpGyrJWsZesrV7QVw7ND0/Suxeo/vLkJ5OwuBqHoCxvTHpOg==}
dependencies:
'@prisma/debug': 5.18.0
'@prisma/engines-version': 5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169
'@prisma/get-platform': 5.18.0
/@prisma/get-platform@5.18.0:
resolution: {integrity: sha512-Tk+m7+uhqcKDgnMnFN0lRiH7Ewea0OEsZZs9pqXa7i3+7svS3FSCqDBCaM9x5fmhhkufiG0BtunJVDka+46DlA==}
dependencies:
'@prisma/debug': 5.18.0
/@prisma/instrumentation@5.11.0:
resolution: {integrity: sha512-ou4nvDpNEY6+t3Dn9juOTz6tK33D0Y4XXkEZ2uPd8KH6Mqmc+4LYOOm470DP7noj7dyJjuGiM+wpPk//HKrcDg==}
dependencies:
@@ -23543,6 +23589,14 @@ packages:
react: 18.2.0
dev: false
/prisma@5.18.0:
resolution: {integrity: sha512-+TrSIxZsh64OPOmaSgVPH7ALL9dfU0jceYaMJXsNrTkFHO7/3RANi5K2ZiPB1De9+KDxCWn7jvRq8y8pvk+o9g==}
engines: {node: '>=16.13'}
hasBin: true
requiresBuild: true
dependencies:
'@prisma/engines': 5.18.0
/prisma@5.4.1:
resolution: {integrity: sha512-op9PmU8Bcw5dNAas82wBYTG0yHnpq9/O3bhxbDBrNzwZTwBqsVCxxYRLf6wHNh9HVaDGhgjjHlu1+BcW8qdnBg==}
engines: {node: '>=16.13'}
+4 -2
View File
@@ -13,9 +13,10 @@
"build:client": "tsup-node ./src/clientUsage.ts --format esm,cjs",
"client": "ts-node -r dotenv/config -r tsconfig-paths/register ./src/clientUsage.ts",
"triggerWithLargePayload": "ts-node -r dotenv/config -r tsconfig-paths/register ./src/triggerWithLargePayload.ts",
"postinstall": "echo 'package.json postinstall'"
"postinstall": "prisma generate --no-hints"
},
"dependencies": {
"@prisma/client": "5.18.0",
"@ffmpeg-installer/ffmpeg": "^1.1.0",
"@ffprobe-installer/ffprobe": "^2.1.2",
"@infisical/sdk": "^2.1.9",
@@ -66,6 +67,7 @@
"trigger.dev": "workspace:*",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.5.4"
"typescript": "^5.5.4",
"prisma": "5.18.0"
}
}
@@ -0,0 +1,20 @@
-- CreateTable
CREATE TABLE "Post" (
"id" SERIAL NOT NULL,
"title" TEXT NOT NULL,
"content" TEXT NOT NULL,
"authorId" INTEGER NOT NULL,
CONSTRAINT "Post_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "User" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "Post" ADD CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"
@@ -1,14 +0,0 @@
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
directUrl = env("DIRECT_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id String @id @default(cuid())
email String @unique
}
@@ -0,0 +1,8 @@
// post.prisma
model Post {
id Int @id @default(autoincrement())
title String
content String
authorId Int
author User @relation(fields: [authorId], references: [id])
}
@@ -0,0 +1,16 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
generator client {
provider = "prisma-client-js"
previewFeatures = ["prismaSchemaFolder"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
directUrl = env("DATABASE_URL_UNPOOLED")
}
@@ -0,0 +1,6 @@
// user.prisma
model User {
id Int @id @default(autoincrement())
name String
posts Post[]
}
+3
View File
@@ -0,0 +1,3 @@
import { PrismaClient } from "@prisma/client";
export const prisma = new PrismaClient();
@@ -0,0 +1,17 @@
import { prisma } from "@/db.js";
import { task } from "@trigger.dev/sdk/v3";
export const prismaTask = task({
id: "prisma-task",
run: async () => {
const users = await prisma.user.findMany();
await prisma.user.create({
data: {
name: "Alice",
},
});
return users;
},
});
+14 -2
View File
@@ -1,6 +1,10 @@
import { OpenAIInstrumentation } from "@traceloop/instrumentation-openai";
import { defineConfig, ResolveEnvironmentVariablesFunction } from "@trigger.dev/sdk/v3";
import { emitDecoratorMetadata } from "@trigger.dev/sdk/v3/extensions";
import {
emitDecoratorMetadata,
audioWaveform,
prismaExtension,
} from "@trigger.dev/sdk/v3/extensions";
import { InfisicalClient } from "@infisical/sdk";
export const resolveEnvVars: ResolveEnvironmentVariablesFunction = async (ctx) => {
@@ -55,7 +59,15 @@ export default defineConfig({
console.log(`Task ${ctx.task.id} failed ${ctx.run.id}`);
},
build: {
extensions: [emitDecoratorMetadata()],
extensions: [
emitDecoratorMetadata(),
audioWaveform(),
prismaExtension({
schema: "prisma/schema/schema.prisma",
migrate: true,
directUrlEnvVarName: "DATABASE_URL_UNPOOLED",
}),
],
external: ["@ffmpeg-installer/ffmpeg", "re2"],
},
});