Make the image builds deterministic
This commit is contained in:
@@ -92,10 +92,10 @@
|
||||
"@opentelemetry/resources": "2.0.1",
|
||||
"@opentelemetry/sdk-trace-node": "2.0.1",
|
||||
"@opentelemetry/semantic-conventions": "1.36.0",
|
||||
"@s2-dev/streamstore": "^0.17.6",
|
||||
"@trigger.dev/build": "workspace:4.2.0",
|
||||
"@trigger.dev/core": "workspace:4.2.0",
|
||||
"@trigger.dev/schema-to-json": "workspace:4.2.0",
|
||||
"@s2-dev/streamstore": "^0.17.6",
|
||||
"ansi-escapes": "^7.0.0",
|
||||
"braces": "^3.0.3",
|
||||
"c12": "^1.11.1",
|
||||
@@ -117,6 +117,7 @@
|
||||
"import-in-the-middle": "1.11.0",
|
||||
"import-meta-resolve": "^4.1.0",
|
||||
"ini": "^5.0.0",
|
||||
"json-stable-stringify": "^1.3.0",
|
||||
"jsonc-parser": "3.2.1",
|
||||
"magicast": "^0.3.4",
|
||||
"minimatch": "^10.0.1",
|
||||
|
||||
@@ -12,7 +12,7 @@ import { join, relative, sep } from "node:path";
|
||||
import { generateContainerfile } from "../deploy/buildImage.js";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import { buildManifestToJSON } from "../utilities/buildManifest.js";
|
||||
import { readPackageJSON, writePackageJSON } from "pkg-types";
|
||||
import { readPackageJSON } from "pkg-types";
|
||||
import { writeJSONFile } from "../utilities/fileSystem.js";
|
||||
import { isWindows } from "std-env";
|
||||
import { pathToFileURL } from "node:url";
|
||||
@@ -192,20 +192,23 @@ async function writeDeployFiles({
|
||||
) ?? {};
|
||||
|
||||
// Step 3: Write the resolved dependencies to the package.json file
|
||||
await writePackageJSON(join(outputPath, "package.json"), {
|
||||
...packageJson,
|
||||
name: packageJson.name ?? "trigger-project",
|
||||
dependencies: {
|
||||
...dependencies,
|
||||
await writeJSONFile(
|
||||
join(outputPath, "package.json"),
|
||||
{
|
||||
...packageJson,
|
||||
name: packageJson.name ?? "trigger-project",
|
||||
dependencies: {
|
||||
...dependencies,
|
||||
},
|
||||
trustedDependencies: Object.keys(dependencies).sort(),
|
||||
devDependencies: {},
|
||||
peerDependencies: {},
|
||||
scripts: {},
|
||||
},
|
||||
trustedDependencies: Object.keys(dependencies),
|
||||
devDependencies: {},
|
||||
peerDependencies: {},
|
||||
scripts: {},
|
||||
});
|
||||
true
|
||||
);
|
||||
|
||||
await writeJSONFile(join(outputPath, "build.json"), buildManifestToJSON(buildManifest));
|
||||
await writeJSONFile(join(outputPath, "metafile.json"), bundleResult.metafile);
|
||||
await writeContainerfile(outputPath, buildManifest);
|
||||
}
|
||||
|
||||
|
||||
@@ -414,7 +414,9 @@ export async function createBuildManifestFromBundle({
|
||||
otelImportHook: {
|
||||
include: resolvedConfig.instrumentedPackageNames ?? [],
|
||||
},
|
||||
outputHashes: bundle.outputHashes,
|
||||
// `outputHashes` is only needed for dev builds for the deduplication mechanism during rebuilds.
|
||||
// For deploys builds, we omit it to ensure deterministic builds
|
||||
outputHashes: target === "dev" ? bundle.outputHashes : {},
|
||||
};
|
||||
|
||||
if (!workerDir) {
|
||||
|
||||
@@ -193,12 +193,12 @@ async function remoteBuildImage(options: DepotBuildImageOptions): Promise<BuildI
|
||||
"--metadata-file",
|
||||
"metadata.json",
|
||||
"--build-arg",
|
||||
`SOURCE_DATE_EPOCH=0`,
|
||||
"--build-arg",
|
||||
`TRIGGER_PROJECT_ID=${options.projectId}`,
|
||||
"--build-arg",
|
||||
`TRIGGER_DEPLOYMENT_ID=${options.deploymentId}`,
|
||||
"--build-arg",
|
||||
`TRIGGER_DEPLOYMENT_VERSION=${options.deploymentVersion}`,
|
||||
"--build-arg",
|
||||
`TRIGGER_CONTENT_HASH=${options.contentHash}`,
|
||||
"--build-arg",
|
||||
`TRIGGER_PROJECT_REF=${options.projectRef}`,
|
||||
@@ -210,6 +210,8 @@ async function remoteBuildImage(options: DepotBuildImageOptions): Promise<BuildI
|
||||
`TRIGGER_SECRET_KEY=${options.apiKey}`,
|
||||
...(buildArgs || []),
|
||||
...(options.extraCACerts ? ["--build-arg", `NODE_EXTRA_CA_CERTS=${options.extraCACerts}`] : []),
|
||||
"--output",
|
||||
"type=image,rewrite-timestamp=true",
|
||||
"--progress",
|
||||
"plain",
|
||||
".",
|
||||
@@ -509,19 +511,17 @@ async function localBuildImage(options: SelfHostedBuildImageOptions): Promise<Bu
|
||||
options.imagePlatform,
|
||||
options.network ? `--network=${options.network}` : undefined,
|
||||
addHost ? `--add-host=${addHost}` : undefined,
|
||||
push ? "--push" : undefined,
|
||||
load ? "--load" : undefined,
|
||||
"--provenance",
|
||||
"false",
|
||||
"--metadata-file",
|
||||
"metadata.json",
|
||||
"--build-arg",
|
||||
`SOURCE_DATE_EPOCH=0`,
|
||||
"--build-arg",
|
||||
`TRIGGER_PROJECT_ID=${options.projectId}`,
|
||||
"--build-arg",
|
||||
`TRIGGER_DEPLOYMENT_ID=${options.deploymentId}`,
|
||||
"--build-arg",
|
||||
`TRIGGER_DEPLOYMENT_VERSION=${options.deploymentVersion}`,
|
||||
"--build-arg",
|
||||
`TRIGGER_CONTENT_HASH=${options.contentHash}`,
|
||||
"--build-arg",
|
||||
`TRIGGER_PROJECT_REF=${options.projectRef}`,
|
||||
@@ -533,10 +533,12 @@ async function localBuildImage(options: SelfHostedBuildImageOptions): Promise<Bu
|
||||
`TRIGGER_SECRET_KEY=${options.apiKey}`,
|
||||
...(buildArgs || []),
|
||||
...(options.extraCACerts ? ["--build-arg", `NODE_EXTRA_CA_CERTS=${options.extraCACerts}`] : []),
|
||||
"--output",
|
||||
`type=image,name=${imageTag},rewrite-timestamp=true${push ? ",push=true" : ""}${
|
||||
load ? ",load=true" : ""
|
||||
}`,
|
||||
"--progress",
|
||||
"plain",
|
||||
"-t",
|
||||
imageTag,
|
||||
".", // The build context
|
||||
].filter(Boolean) as string[];
|
||||
|
||||
@@ -761,15 +763,11 @@ USER bun
|
||||
WORKDIR /app
|
||||
|
||||
ARG TRIGGER_PROJECT_ID
|
||||
ARG TRIGGER_DEPLOYMENT_ID
|
||||
ARG TRIGGER_DEPLOYMENT_VERSION
|
||||
ARG TRIGGER_CONTENT_HASH
|
||||
ARG TRIGGER_PROJECT_REF
|
||||
ARG NODE_EXTRA_CA_CERTS
|
||||
|
||||
ENV TRIGGER_PROJECT_ID=\${TRIGGER_PROJECT_ID} \
|
||||
TRIGGER_DEPLOYMENT_ID=\${TRIGGER_DEPLOYMENT_ID} \
|
||||
TRIGGER_DEPLOYMENT_VERSION=\${TRIGGER_DEPLOYMENT_VERSION} \
|
||||
TRIGGER_CONTENT_HASH=\${TRIGGER_CONTENT_HASH} \
|
||||
TRIGGER_PROJECT_REF=\${TRIGGER_PROJECT_REF} \
|
||||
UV_USE_IO_URING=0 \
|
||||
@@ -875,15 +873,11 @@ USER node
|
||||
WORKDIR /app
|
||||
|
||||
ARG TRIGGER_PROJECT_ID
|
||||
ARG TRIGGER_DEPLOYMENT_ID
|
||||
ARG TRIGGER_DEPLOYMENT_VERSION
|
||||
ARG TRIGGER_CONTENT_HASH
|
||||
ARG TRIGGER_PROJECT_REF
|
||||
ARG NODE_EXTRA_CA_CERTS
|
||||
|
||||
ENV TRIGGER_PROJECT_ID=\${TRIGGER_PROJECT_ID} \
|
||||
TRIGGER_DEPLOYMENT_ID=\${TRIGGER_DEPLOYMENT_ID} \
|
||||
TRIGGER_DEPLOYMENT_VERSION=\${TRIGGER_DEPLOYMENT_VERSION} \
|
||||
TRIGGER_CONTENT_HASH=\${TRIGGER_CONTENT_HASH} \
|
||||
TRIGGER_PROJECT_REF=\${TRIGGER_PROJECT_REF} \
|
||||
UV_USE_IO_URING=0 \
|
||||
|
||||
@@ -10,6 +10,7 @@ import { CliApiClient } from "../apiClient.js";
|
||||
import { indexWorkerManifest } from "../indexing/indexWorkerManifest.js";
|
||||
import { resolveSourceFiles } from "../utilities/sourceFiles.js";
|
||||
import { execOptionsForRuntime } from "@trigger.dev/core/v3/build";
|
||||
import { writeJSONFile } from "../utilities/fileSystem.js";
|
||||
|
||||
async function loadBuildManifest() {
|
||||
const manifestContents = await readFile("./build.json", "utf-8");
|
||||
@@ -88,7 +89,8 @@ async function indexDeployment({
|
||||
|
||||
console.log("Writing index.json", process.cwd());
|
||||
|
||||
await writeFile(join(process.cwd(), "index.json"), JSON.stringify(workerManifest, null, 2));
|
||||
const { timings, ...manifestWithoutTimings } = workerManifest;
|
||||
await writeJSONFile(join(process.cwd(), "index.json"), manifestWithoutTimings, true);
|
||||
|
||||
const sourceFiles = resolveSourceFiles(buildManifest.sources, workerManifest.tasks);
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@ const DateEnv = z
|
||||
const Env = z.object({
|
||||
// Set at build time
|
||||
TRIGGER_CONTENT_HASH: z.string(),
|
||||
TRIGGER_DEPLOYMENT_ID: z.string(),
|
||||
TRIGGER_DEPLOYMENT_VERSION: z.string(),
|
||||
TRIGGER_PROJECT_ID: z.string(),
|
||||
TRIGGER_PROJECT_REF: z.string(),
|
||||
NODE_ENV: z.string().default("production"),
|
||||
@@ -21,6 +19,8 @@ const Env = z.object({
|
||||
UV_USE_IO_URING: z.string().optional(),
|
||||
|
||||
// Set at runtime
|
||||
TRIGGER_DEPLOYMENT_ID: z.string(),
|
||||
TRIGGER_DEPLOYMENT_VERSION: z.string(),
|
||||
TRIGGER_WORKLOAD_CONTROLLER_ID: z.string().default(`controller_${randomUUID()}`),
|
||||
TRIGGER_ENV_ID: z.string(),
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: z.string().url(),
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { BuildManifest } from "@trigger.dev/core/v3/schemas";
|
||||
|
||||
export function buildManifestToJSON(manifest: BuildManifest): BuildManifest {
|
||||
const { deploy, build, ...rest } = manifest;
|
||||
const { deploy, build, externals, ...rest } = manifest;
|
||||
|
||||
return {
|
||||
...rest,
|
||||
// sort externals for deterministic builds
|
||||
externals: externals?.slice().sort((a, b) => a.name.localeCompare(b.name)),
|
||||
deploy: {},
|
||||
build: {},
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import fsSync from "fs";
|
||||
import stringify from "json-stable-stringify";
|
||||
import fsModule, { writeFile } from "fs/promises";
|
||||
import fs from "node:fs";
|
||||
import { homedir, tmpdir } from "node:os";
|
||||
@@ -159,7 +160,7 @@ export async function safeReadJSONFile(path: string) {
|
||||
}
|
||||
|
||||
export async function writeJSONFile(path: string, json: any, pretty = false) {
|
||||
await safeWriteFile(path, JSON.stringify(json, undefined, pretty ? 2 : undefined));
|
||||
await safeWriteFile(path, stringify(json, pretty ? { space: 2 } : undefined) ?? "");
|
||||
}
|
||||
|
||||
// Will create the directory if it doesn't exist
|
||||
|
||||
Generated
+34
-11
@@ -1444,6 +1444,9 @@ importers:
|
||||
ini:
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0
|
||||
json-stable-stringify:
|
||||
specifier: ^1.3.0
|
||||
version: 1.3.0
|
||||
jsonc-parser:
|
||||
specifier: 3.2.1
|
||||
version: 3.2.1
|
||||
@@ -11803,13 +11806,14 @@ packages:
|
||||
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
call-bind@1.0.2:
|
||||
resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
|
||||
|
||||
call-bind@1.0.7:
|
||||
resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
call-bind@1.0.8:
|
||||
resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
call-bound@1.0.4:
|
||||
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -14834,6 +14838,10 @@ packages:
|
||||
json-stable-stringify-without-jsonify@1.0.1:
|
||||
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
|
||||
|
||||
json-stable-stringify@1.3.0:
|
||||
resolution: {integrity: sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
json-stringify-safe@5.0.1:
|
||||
resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==}
|
||||
|
||||
@@ -14860,6 +14868,9 @@ packages:
|
||||
jsonfile@6.1.0:
|
||||
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
|
||||
|
||||
jsonify@0.0.1:
|
||||
resolution: {integrity: sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==}
|
||||
|
||||
jsonpath-plus@10.3.0:
|
||||
resolution: {integrity: sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
@@ -31881,7 +31892,7 @@ snapshots:
|
||||
|
||||
array.prototype.flat@1.3.2:
|
||||
dependencies:
|
||||
call-bind: 1.0.2
|
||||
call-bind: 1.0.7
|
||||
define-properties: 1.2.1
|
||||
es-abstract: 1.23.3
|
||||
es-shim-unscopables: 1.0.0
|
||||
@@ -31895,7 +31906,7 @@ snapshots:
|
||||
|
||||
array.prototype.flatmap@1.3.2:
|
||||
dependencies:
|
||||
call-bind: 1.0.2
|
||||
call-bind: 1.0.7
|
||||
define-properties: 1.2.1
|
||||
es-abstract: 1.23.3
|
||||
es-shim-unscopables: 1.0.0
|
||||
@@ -32328,11 +32339,6 @@ snapshots:
|
||||
es-errors: 1.3.0
|
||||
function-bind: 1.1.2
|
||||
|
||||
call-bind@1.0.2:
|
||||
dependencies:
|
||||
function-bind: 1.1.2
|
||||
get-intrinsic: 1.3.0
|
||||
|
||||
call-bind@1.0.7:
|
||||
dependencies:
|
||||
es-define-property: 1.0.1
|
||||
@@ -32341,6 +32347,13 @@ snapshots:
|
||||
get-intrinsic: 1.3.0
|
||||
set-function-length: 1.2.2
|
||||
|
||||
call-bind@1.0.8:
|
||||
dependencies:
|
||||
call-bind-apply-helpers: 1.0.2
|
||||
es-define-property: 1.0.1
|
||||
get-intrinsic: 1.3.0
|
||||
set-function-length: 1.2.2
|
||||
|
||||
call-bound@1.0.4:
|
||||
dependencies:
|
||||
call-bind-apply-helpers: 1.0.2
|
||||
@@ -35816,6 +35829,14 @@ snapshots:
|
||||
|
||||
json-stable-stringify-without-jsonify@1.0.1: {}
|
||||
|
||||
json-stable-stringify@1.3.0:
|
||||
dependencies:
|
||||
call-bind: 1.0.8
|
||||
call-bound: 1.0.4
|
||||
isarray: 2.0.5
|
||||
jsonify: 0.0.1
|
||||
object-keys: 1.1.1
|
||||
|
||||
json-stringify-safe@5.0.1: {}
|
||||
|
||||
json5@1.0.2:
|
||||
@@ -35842,6 +35863,8 @@ snapshots:
|
||||
optionalDependencies:
|
||||
graceful-fs: 4.2.11
|
||||
|
||||
jsonify@0.0.1: {}
|
||||
|
||||
jsonpath-plus@10.3.0:
|
||||
dependencies:
|
||||
'@jsep-plugin/assignment': 1.3.0(jsep@1.4.0)
|
||||
@@ -40313,7 +40336,7 @@ snapshots:
|
||||
|
||||
string.prototype.padend@3.1.4:
|
||||
dependencies:
|
||||
call-bind: 1.0.2
|
||||
call-bind: 1.0.7
|
||||
define-properties: 1.1.4
|
||||
es-abstract: 1.21.1
|
||||
|
||||
|
||||
Reference in New Issue
Block a user