fade22015f
* Adds new features table to top of v4 upgrade guide * Adds wait idempotency to wait-until, wait-for, and wait-for-token pages * Adds new priority docs page and updates the v4 upgrade guide * Adds new task lifecycle hooks * Removes the message about requiring tasks to be exported * Adds new global lifecycle hooks section * Moves sections from upgrade guide into the table * Adds hidden task page * Improves the global lifecycle hooks section * Updates middleware and locals section * Adds new useWaitToken page to the react hooks section * Adds a new ai.tool section * Moves Docker (legacy) page into self-hosting section * Removes known issues from v4 upgrade guide * Replace “toolTask” with “ai.tool” in the Streams page example * Renames guide to “Migrating from v3” and adds redirect * Remove references to v4 * Removes changelog from migration guide * The installation guide now references `@latest update` * Changes all references from `/sdk/v3` to `/sdk` * Updates @v4-beta to @latest * Fixed broken link * Fixes broken link * Adds an upgrade to v4 using AI section * Fixes 2 broken links * Adds an entry for targetting preview branches * Updates the run statuses * Adds boolean helpers section to the runs and realtime pages * Updates the concurrency page * Updates the test page to include the new options * Adds SDK and curl options for the preview branch targeting * Updates new bulk actions page * Remove the releasing concurrency section * Got rid of some more @v4-beta mentions * Improved rate limit docs * Improved migrating docs * Removed commented sections of the docs * useWaitToken hook * Fixed the description * Fix for missing test image --------- Co-authored-by: Matt Aitken <matt@mattaitken.com> Co-authored-by: Dan <8297864+D-K-P@users.noreply.github.com>
381 lines
11 KiB
Plaintext
381 lines
11 KiB
Plaintext
---
|
|
title: "Custom build extensions"
|
|
sidebarTitle: "Custom"
|
|
description: "Customize how your project is built and deployed to Trigger.dev with your own custom build extensions"
|
|
---
|
|
|
|
Build extensions allow you to hook into the build system and customize the build process or the resulting bundle and container image (in the case of deploying). See our [build extension overview](/config/extensions/overview) for more information on how to install and use our built-in extensions. Build extensions can do the following:
|
|
|
|
- Add additional files to the build
|
|
- Add dependencies to the list of externals
|
|
- Add esbuild plugins
|
|
- Add additional npm dependencies
|
|
- Add additional system packages to the image build container
|
|
- Add commands to run in the image build container
|
|
- Add environment variables to the image build container
|
|
- Sync environment variables to your Trigger.dev project
|
|
|
|
## Creating a build extension
|
|
|
|
Build extensions are added to your `trigger.config.ts` file, with a required `name` and optional build hook functions. Here's a simple example of a build extension that just logs a message when the build starts:
|
|
|
|
```ts
|
|
import { defineConfig } from "@trigger.dev/sdk";
|
|
|
|
export default defineConfig({
|
|
project: "my-project",
|
|
build: {
|
|
extensions: [
|
|
{
|
|
name: "my-extension",
|
|
onBuildStart: async (context) => {
|
|
console.log("Build starting!");
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|
|
```
|
|
|
|
You can also extract that out into a function instead of defining it inline, in which case you will need to import the `BuildExtension` type from the `@trigger.dev/build` package:
|
|
|
|
<Note>
|
|
You'll need to add the `@trigger.dev/build` package to your `devDependencies` before the below
|
|
code will work. Make sure it's version matches that of the installed `@trigger.dev/sdk` package.
|
|
</Note>
|
|
|
|
```ts
|
|
import { defineConfig } from "@trigger.dev/sdk";
|
|
import { BuildExtension } from "@trigger.dev/build";
|
|
|
|
export default defineConfig({
|
|
project: "my-project",
|
|
build: {
|
|
extensions: [myExtension()],
|
|
},
|
|
});
|
|
|
|
function myExtension(): BuildExtension {
|
|
return {
|
|
name: "my-extension",
|
|
onBuildStart: async (context) => {
|
|
console.log("Build starting!");
|
|
},
|
|
};
|
|
}
|
|
```
|
|
|
|
## Build hooks
|
|
|
|
### externalsForTarget
|
|
|
|
This allows the extension to add additional dependencies to the list of externals for the build. This is useful for dependencies that are not included in the bundle, but are expected to be available at runtime.
|
|
|
|
```ts
|
|
import { defineConfig } from "@trigger.dev/sdk";
|
|
|
|
export default defineConfig({
|
|
project: "my-project",
|
|
build: {
|
|
extensions: [
|
|
{
|
|
name: "my-extension",
|
|
externalsForTarget: async (target) => {
|
|
return ["my-dependency"];
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|
|
```
|
|
|
|
### onBuildStart
|
|
|
|
This hook runs before the build starts. It receives the `BuildContext` object as an argument.
|
|
|
|
```ts
|
|
import { defineConfig } from "@trigger.dev/sdk";
|
|
|
|
export default defineConfig({
|
|
project: "my-project",
|
|
build: {
|
|
extensions: [
|
|
{
|
|
name: "my-extension",
|
|
onBuildStart: async (context) => {
|
|
console.log("Build starting!");
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|
|
```
|
|
|
|
If you want to add an esbuild plugin, you must do so in the `onBuildStart` hook. Here's an example of adding a custom esbuild plugin:
|
|
|
|
```ts
|
|
import { defineConfig } from "@trigger.dev/sdk";
|
|
|
|
export default defineConfig({
|
|
project: "my-project",
|
|
build: {
|
|
extensions: [
|
|
{
|
|
name: "my-extension",
|
|
onBuildStart: async (context) => {
|
|
context.registerPlugin({
|
|
name: "my-plugin",
|
|
setup(build) {
|
|
build.onLoad({ filter: /.*/, namespace: "file" }, async (args) => {
|
|
return {
|
|
contents: "console.log('Hello, world!')",
|
|
loader: "js",
|
|
};
|
|
});
|
|
},
|
|
});
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|
|
```
|
|
|
|
You can use the `BuildContext.target` property to determine if the build is for `dev` or `deploy`:
|
|
|
|
```ts
|
|
import { defineConfig } from "@trigger.dev/sdk";
|
|
|
|
export default defineConfig({
|
|
project: "my-project",
|
|
build: {
|
|
extensions: [
|
|
{
|
|
name: "my-extension",
|
|
onBuildStart: async (context) => {
|
|
if (context.target === "dev") {
|
|
console.log("Building for dev");
|
|
} else {
|
|
console.log("Building for deploy");
|
|
}
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|
|
```
|
|
|
|
### onBuildComplete
|
|
|
|
This hook runs after the build completes. It receives the `BuildContext` object and a `BuildManifest` object as arguments. This is where you can add in one or more `BuildLayer`'s to the context.
|
|
|
|
```ts
|
|
import { defineConfig } from "@trigger.dev/sdk";
|
|
|
|
export default defineConfig({
|
|
project: "my-project",
|
|
build: {
|
|
extensions: [
|
|
{
|
|
name: "my-extension",
|
|
onBuildComplete: async (context, manifest) => {
|
|
context.addLayer({
|
|
id: "more-dependencies",
|
|
dependencies,
|
|
});
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|
|
```
|
|
|
|
See the [addLayer](#addlayer) documentation for more information on how to use `addLayer`.
|
|
|
|
## BuildTarget
|
|
|
|
Can either be `dev` or `deploy`, matching the CLI command name that is being run.
|
|
|
|
```sh
|
|
npx trigger.dev@latest dev # BuildTarget is "dev"
|
|
npx trigger.dev@latest deploy # BuildTarget is "deploy"
|
|
```
|
|
|
|
## BuildContext
|
|
|
|
### addLayer()
|
|
|
|
<ParamField path="layer" type="BuildLayer">
|
|
The layer to add to the build context. See the [BuildLayer](#buildlayer) documentation for more
|
|
information.
|
|
</ParamField>
|
|
|
|
### registerPlugin()
|
|
|
|
<ParamField path="plugin" type="esbuild.Plugin" required>
|
|
The esbuild plugin to register.
|
|
</ParamField>
|
|
|
|
<ParamField path="options" type="object">
|
|
<Expandable title="properties">
|
|
<ResponseField name="target" type="BuildTarget">
|
|
An optional target to register the plugin for. If not provided, the plugin will be registered
|
|
for all targets.
|
|
</ResponseField>
|
|
<ResponseField name="placement" type="first | last">
|
|
An optional placement for the plugin. If not provided, the plugin will be registered in place.
|
|
This allows you to control the order of plugins.
|
|
</ResponseField>
|
|
</Expandable>
|
|
</ParamField>
|
|
|
|
### resolvePath()
|
|
|
|
Resolves a path relative to the project's working directory.
|
|
|
|
<ParamField path="path" type="string">
|
|
The path to resolve.
|
|
</ParamField>
|
|
|
|
```ts
|
|
const resolvedPath = context.resolvePath("my-other-dependency");
|
|
```
|
|
|
|
### properties
|
|
|
|
<ParamField path="target" type="BuildTarget">
|
|
The target of the build, either `dev` or `deploy`.
|
|
</ParamField>
|
|
|
|
<ParamField path="config" type="ResolvedConfig">
|
|
<Expandable title="properties">
|
|
<ResponseField name="runtime" type="string">
|
|
The runtime of the project (either node or bun)
|
|
</ResponseField>
|
|
<ResponseField name="project" type="string">
|
|
The project ref
|
|
</ResponseField>
|
|
<ResponseField name="dirs" type="string[]">
|
|
The trigger directories to search for tasks
|
|
</ResponseField>
|
|
<ResponseField name="build" type="object">
|
|
The build configuration object
|
|
</ResponseField>
|
|
<ResponseField name="workingDir" type="string">
|
|
The working directory of the project
|
|
</ResponseField>
|
|
<ResponseField name="workspaceDir" type="string">
|
|
The root workspace directory of the project
|
|
</ResponseField>
|
|
<ResponseField name="packageJsonPath" type="string">
|
|
The path to the package.json file
|
|
</ResponseField>
|
|
<ResponseField name="lockfilePath" type="string">
|
|
The path to the lockfile (package-lock.json, yarn.lock, or pnpm-lock.yaml)
|
|
</ResponseField>
|
|
<ResponseField name="configFile" type="string">
|
|
The path to the trigger.config.ts file
|
|
</ResponseField>
|
|
<ResponseField name="tsconfigPath" type="string">
|
|
The path to the tsconfig.json file
|
|
</ResponseField>
|
|
</Expandable>
|
|
</ParamField>
|
|
|
|
<ParamField path="logger" type="BuildLogger">
|
|
A logger object that can be used to log messages to the console.
|
|
</ParamField>
|
|
|
|
## BuildLayer
|
|
|
|
<ParamField path="id" type="string">
|
|
A unique identifier for the layer.
|
|
</ParamField>
|
|
|
|
<ParamField path="commands" type="string[]">
|
|
An array of commands to run in the image build container.
|
|
|
|
```ts
|
|
commands: ["echo 'Hello, world!'"];
|
|
```
|
|
|
|
These commands are run after packages have been installed and the code copied into the container in the "build" stage of the Dockerfile. This means you cannot install system packages in these commands because they won't be available in the final stage. To do that, please use the `pkgs` property of the `image` object.
|
|
|
|
</ParamField>
|
|
|
|
<ParamField path="image" type="object">
|
|
<Expandable title="properties">
|
|
<ResponseField name="pkgs" type="string[]">
|
|
An array of system packages to install in the image build container.
|
|
</ResponseField>
|
|
<ResponseField name="instructions" type="string[]">
|
|
An array of instructions to add to the Dockerfile.
|
|
</ResponseField>
|
|
</Expandable>
|
|
</ParamField>
|
|
|
|
<ParamField path="build" type="object">
|
|
<Expandable title="properties">
|
|
<ResponseField name="env" type="Record<string, string>">
|
|
Environment variables to add to the image build container, but only during the "build" stage
|
|
of the Dockerfile. This is where you'd put environment variables that are needed when running
|
|
any of the commands in the `commands` array.
|
|
</ResponseField>
|
|
</Expandable>
|
|
</ParamField>
|
|
|
|
<ParamField path="deploy" type="object">
|
|
<Expandable title="properties">
|
|
<ResponseField name="env" type="Record<string, string>">
|
|
Environment variables that should sync to the Trigger.dev project, which will then be avalable
|
|
in your tasks at runtime. Importantly, these are NOT added to the image build container, but
|
|
are instead added to the Trigger.dev project and stored securely.
|
|
</ResponseField>
|
|
</Expandable>
|
|
</ParamField>
|
|
|
|
<ParamField path="dependencies" type="Record<string, string>">
|
|
An object of dependencies to add to the build. The key is the package name and the value is the
|
|
version.
|
|
|
|
```ts
|
|
dependencies: {
|
|
"my-dependency": "^1.0.0",
|
|
};
|
|
```
|
|
|
|
</ParamField>
|
|
|
|
### examples
|
|
|
|
Add a command that will echo the value of an environment variable:
|
|
|
|
```ts
|
|
context.addLayer({
|
|
id: "my-layer",
|
|
commands: [`echo $MY_ENV_VAR`],
|
|
build: {
|
|
env: {
|
|
MY_ENV_VAR: "Hello, world!",
|
|
},
|
|
},
|
|
});
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
When creating a build extension, you may run into issues with the build process. One thing that can help is turning on `debug` logging when running either `dev` or `deploy`:
|
|
|
|
```sh
|
|
npx trigger.dev@latest dev --log-level debug
|
|
npx trigger.dev@latest deploy --log-level debug
|
|
```
|
|
|
|
Another helpful tool is the `--dry-run` flag on the `deploy` command, which will bundle your project and generate the Containerfile (e.g. the Dockerfile) without actually deploying it. This can help you see what the final image will look like and debug any issues with the build process.
|
|
|
|
```sh
|
|
npx trigger.dev@latest deploy --dry-run
|
|
```
|
|
|
|
You should also take a look at our built in extensions for inspiration on how to create your own. You can find them in in [the source code here](https://github.com/triggerdotdev/trigger.dev/tree/main/packages/build/src/extensions).
|