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
5 changes: 5 additions & 0 deletions .changeset/great-pillows-look.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"trigger.dev": minor
---

feat(cli): enable zstd compression for deployment images
46 changes: 44 additions & 2 deletions packages/cli-v3/src/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const DeployCommandOptions = CommonCommandOptions.extend({
saveLogs: z.boolean().default(false),
skipUpdateCheck: z.boolean().default(false),
skipPromotion: z.boolean().default(false),
noCache: z.boolean().default(false),
cache: z.boolean().default(true),
envFile: z.string().optional(),
// Local build options
forceLocalBuild: z.boolean().optional(),
Expand All @@ -83,6 +83,10 @@ const DeployCommandOptions = CommonCommandOptions.extend({
nativeBuildServer: z.boolean().default(false),
detach: z.boolean().default(false),
plain: z.boolean().default(false),
compression: z.enum(["zstd", "gzip"]).default("zstd"),
cacheCompression: z.enum(["zstd", "gzip"]).default("zstd"),
compressionLevel: z.number().optional(),
forceCompression: z.boolean().default(true),
});

type DeployCommandOptions = z.infer<typeof DeployCommandOptions>;
Expand Down Expand Up @@ -157,6 +161,40 @@ export function configureDeployCommand(program: Command) {
"If provided, will save logs even for successful builds"
).hideHelp()
)
.addOption(
new CommandOption(
"--compression <algorithm>",
"Compression algorithm for image layers: zstd or gzip (default: zstd)"
)
.choices(["zstd", "gzip"])
.hideHelp()
)
.addOption(
new CommandOption(
"--cache-compression <algorithm>",
"Compression algorithm for build cache: zstd or gzip (default: zstd)"
)
.choices(["zstd", "gzip"])
.hideHelp()
)
.addOption(
new CommandOption(
"--compression-level <level>",
"The compression level to use when building the image."
).hideHelp()
)
.addOption(
new CommandOption(
"--force-compression",
"Force recompression of all layers. Enabled by default when using zstd."
).hideHelp()
)
.addOption(
new CommandOption(
"--no-force-compression",
"Disable forced recompression of layers."
).hideHelp()
)
// Local build options
.addOption(
new CommandOption("--force-local-build", "Deprecated alias for --local-build").implies({
Expand Down Expand Up @@ -480,7 +518,7 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
const buildResult = await buildImage({
isLocalBuild,
useRegistryCache: options.useRegistryCache,
noCache: options.noCache,
noCache: !options.cache,
deploymentId: deployment.id,
deploymentVersion: deployment.version,
imageTag: deployment.imageTag,
Expand All @@ -499,6 +537,10 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
authAccessToken: authorization.auth.accessToken,
compilationPath: destination.path,
buildEnvVars: buildManifest.build.env,
compression: options.compression,
cacheCompression: options.cacheCompression,
compressionLevel: options.compressionLevel,
forceCompression: options.forceCompression,
onLog: (logMessage) => {
if (options.plain || isCI) {
console.log(logMessage);
Expand Down
99 changes: 96 additions & 3 deletions packages/cli-v3/src/deploy/buildImage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ export interface BuildImageOptions {
imagePlatform: string;
noCache?: boolean;
load?: boolean;
compression?: "zstd" | "gzip";
cacheCompression?: "zstd" | "gzip";
compressionLevel?: number;
forceCompression?: boolean;

// Local build options
push?: boolean;
Expand Down Expand Up @@ -79,6 +83,10 @@ export async function buildImage(options: BuildImageOptions): Promise<BuildImage
buildEnvVars,
network,
builder,
compression,
cacheCompression,
compressionLevel,
forceCompression,
onLog,
} = options;

Expand All @@ -105,6 +113,10 @@ export async function buildImage(options: BuildImageOptions): Promise<BuildImage
buildEnvVars,
network,
builder,
compression,
cacheCompression,
compressionLevel,
forceCompression,
onLog,
});
}
Expand Down Expand Up @@ -134,6 +146,9 @@ export async function buildImage(options: BuildImageOptions): Promise<BuildImage
apiKey,
branchName,
buildEnvVars,
compression,
compressionLevel,
forceCompression,
onLog,
});
}
Expand All @@ -157,6 +172,9 @@ export interface DepotBuildImageOptions {
noCache?: boolean;
extraCACerts?: string;
buildEnvVars?: Record<string, string | undefined>;
compression?: "zstd" | "gzip";
compressionLevel?: number;
forceCompression?: boolean;
onLog?: (log: string) => void;
}

Expand All @@ -180,6 +198,14 @@ async function remoteBuildImage(options: DepotBuildImageOptions): Promise<BuildI
.filter(([key, value]) => value)
.flatMap(([key, value]) => ["--build-arg", `${key}=${value}`]);

const outputOptions = getOutputOptions({
imageTag: undefined, // This is already handled via the --save flag
push: true, // We always push the image to the registry
compression: options.compression,
compressionLevel: options.compressionLevel,
forceCompression: options.forceCompression,
});

const args = [
"build",
"-f",
Expand Down Expand Up @@ -214,6 +240,8 @@ async function remoteBuildImage(options: DepotBuildImageOptions): Promise<BuildI
"plain",
".",
"--save",
"--output",
outputOptions.join(","),
].filter(Boolean) as string[];

logger.debug(`depot ${args.join(" ")}`, { cwd: options.cwd });
Expand Down Expand Up @@ -316,11 +344,25 @@ interface SelfHostedBuildImageOptions {
network?: string;
builder: string;
load?: boolean;
compression?: "zstd" | "gzip";
cacheCompression?: "zstd" | "gzip";
compressionLevel?: number;
forceCompression?: boolean;
onLog?: (log: string) => void;
}

async function localBuildImage(options: SelfHostedBuildImageOptions): Promise<BuildImageResults> {
const { builder, imageTag, deploymentId, apiClient, useRegistryCache } = options;
const {
builder,
imageTag,
deploymentId,
apiClient,
useRegistryCache,
compression,
cacheCompression,
compressionLevel,
forceCompression,
} = options;

// Ensure multi-platform build is supported on the local machine
let builderExists = false;
Expand Down Expand Up @@ -489,6 +531,14 @@ async function localBuildImage(options: SelfHostedBuildImageOptions): Promise<Bu

const projectCacheRef = getProjectCacheRefFromImageTag(imageTag);

const outputOptions = getOutputOptions({
imageTag,
push,
compression,
compressionLevel,
forceCompression,
});

const args = [
"buildx",
"build",
Expand All @@ -500,16 +550,19 @@ async function localBuildImage(options: SelfHostedBuildImageOptions): Promise<Bu
...(useRegistryCache
? [
"--cache-to",
`type=registry,mode=max,image-manifest=true,oci-mediatypes=true,ref=${projectCacheRef}`,
`type=registry,mode=max,image-manifest=true,oci-mediatypes=true,ref=${projectCacheRef}${
cacheCompression === "zstd" ? ",compression=zstd" : ""
}`,
"--cache-from",
`type=registry,ref=${projectCacheRef}`,
]
: []),
"--output",
outputOptions.join(","),
"--platform",
options.imagePlatform,
options.network ? `--network=${options.network}` : undefined,
addHost ? `--add-host=${addHost}` : undefined,
push ? "--push" : undefined,
load ? "--load" : undefined,
"--provenance",
"false",
Expand Down Expand Up @@ -1072,3 +1125,43 @@ function shouldLoad(load?: boolean, push?: boolean) {
}
}
}

function getOutputOptions({
imageTag,
push,
compression,
compressionLevel,
forceCompression,
}: {
imageTag?: string;
push?: boolean;
compression?: "zstd" | "gzip";
compressionLevel?: number;
forceCompression?: boolean;
}): string[] {
// Always use OCI media types for compatibility
const outputOptions: string[] = ["type=image", "oci-mediatypes=true"];

if (imageTag) {
outputOptions.push(`name=${imageTag}`);
}

if (push) {
outputOptions.push("push=true");
}

// Only add compression args when using zstd (gzip is the default, no args needed)
if (compression === "zstd") {
outputOptions.push("compression=zstd");

if (compressionLevel !== undefined) {
outputOptions.push(`compression-level=${compressionLevel}`);
}
}

if (forceCompression) {
outputOptions.push("force-compression=true");
}

return outputOptions;
}