You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 2e065ce
Browse filesBrowse the repository at this point in the historyBrowse files
feat(deploy): --external-id and --force for deploy idempotency
A deploy can carry an opaque external id (commit SHA, CI run id, release tag). Repeating an id that already deployed returns the existing version as a no-op instead of rebuilding; an id with a build in flight is rejected with 409 naming that version; a failed id rebuilds freely. --force is non-destructive to deployments that already succeeded - both persist and the higher version wins - but cancels a build still in flight, so one id never has two live builds racing to define it. Cancelling writes a terminal status and appends a finalized event, which aborts a build the platform drives; a build it does not drive keeps running but can never land, and the CLI says so. Ids are deliberately not unique - reuse is resolved in application code by highest version, never timestamps. The no-op path mints no build credentials and no event stream (TRI-12923).
What that means for callers: a --force rebuild leaves two deployments holding one id, and runs triggered with it go to the higher version once the rebuild lands, so the takeover needs no separate promotion. Until a successful build exists for an id, runs triggered with it park and then expire rather than falling back to current - a failed build is therefore visible to the caller as expired runs, not as runs on the wrong release.
`trigger.dev deploy --external-id` tags a deployment with an id of your own — a commit SHA, a CI run id, a release tag — so runs triggered by that release of your app go to that deployment. Deploying an id that is already deployed builds nothing and reports the existing version instead of creating a duplicate; use `--force` to rebuild it.
@@ -53,7 +97,10 @@ export class InitializeDeploymentService extends BaseService {
53
97
);
54
98
}
55
99
100
+
span.setAttribute("outcome","created");
101
+
56
102
return{
103
+
outcome: "created",
57
104
deployment: existingDeployment,
58
105
imageRef: existingDeployment.imageReference??"",
59
106
};
@@ -103,6 +150,56 @@ export class InitializeDeploymentService extends BaseService {
103
150
);
104
151
}
105
152
153
+
constdeploymentService=newDeploymentService();
154
+
155
+
constreuse=awaitresolveExternalIdReuse({
156
+
prisma: this._prisma,
157
+
environmentId: environment.id,
158
+
externalId: payload.externalId,
159
+
force: payload.force,
160
+
});
161
+
162
+
if(reuse.action==="reject"){
163
+
span.setAttribute("outcome","rejected");
164
+
165
+
thrownewServiceValidationError(
166
+
`A deployment for external id "${payload.externalId}" is already in progress (version ${reuse.deployment.version}). Wait for it to finish, or deploy again with --force to cancel it and start a new one.`,
// For the `PENDING` initial status, defer the creation of the Depot build until the deployment is started to avoid token expiration issues.
107
204
// For local and native builds we don't need to generate the Depot tokens. We still need to create an empty object sadly due to a bug in older CLI versions.
108
205
constgenerateExternalBuildToken=
@@ -140,7 +237,6 @@ export class InitializeDeploymentService extends BaseService {
0 commit comments