1+ import { intro } from "@clack/prompts" ;
2+ import { resolve } from "node:path" ;
3+ import { spinner } from "../utilities/windows.js" ;
4+ import { loadConfig } from "../config.js" ;
5+ import { verifyDirectory } from "./deploy.js" ;
16import { ResolvedConfig } from "@trigger.dev/core/v3/build" ;
27import { Command , Option as CommandOption } from "commander" ;
38import { z } from "zod" ;
49import { CliApiClient } from "../apiClient.js" ;
5- import { CommonCommandOptions , commonOptions , wrapCommandAction } from "../cli/common.js" ;
10+ import { CommonCommandOptions , commonOptions , handleTelemetry , wrapCommandAction } from "../cli/common.js" ;
611import { watchConfig } from "../config.js" ;
712import { DevSessionInstance , startDevSession } from "../dev/devSession.js" ;
813import { createLockFile } from "../dev/lock.js" ;
@@ -27,11 +32,23 @@ import { installMcpServer } from "./install-mcp.js";
2732import { tryCatch } from "@trigger.dev/core/utils" ;
2833import { VERSION } from "@trigger.dev/core" ;
2934import { initiateSkillsInstallWizard } from "./skills.js" ;
35+ import { getDevBranch } from "@trigger.dev/core/v3" ;
36+ import { isDefaultDevBranch } from "@trigger.dev/core/v3/utils/gitBranch" ;
37+
38+ const DevArchiveCommandOptions = CommonCommandOptions . extend ( {
39+ branch : z . string ( ) . optional ( ) ,
40+ config : z . string ( ) . optional ( ) ,
41+ projectRef : z . string ( ) . optional ( ) ,
42+ skipUpdateCheck : z . boolean ( ) . default ( false ) ,
43+ } ) ;
44+
45+ type DevArchiveCommandOptions = z . infer < typeof DevArchiveCommandOptions > ;
3046
3147const DevCommandOptions = CommonCommandOptions . extend ( {
3248 debugOtel : z . boolean ( ) . default ( false ) ,
3349 config : z . string ( ) . optional ( ) ,
3450 projectRef : z . string ( ) . optional ( ) ,
51+ branch : z . string ( ) . optional ( ) ,
3552 skipUpdateCheck : z . boolean ( ) . default ( false ) ,
3653 skipPlatformNotifications : z . boolean ( ) . default ( false ) ,
3754 envFile : z . string ( ) . optional ( ) ,
@@ -48,15 +65,45 @@ const DevCommandOptions = CommonCommandOptions.extend({
4865export type DevCommandOptions = z . infer < typeof DevCommandOptions > ;
4966
5067export function configureDevCommand ( program : Command ) {
51- return commonOptions (
52- program
53- . command ( "dev" )
54- . description ( "Run your Trigger.dev tasks locally" )
68+ const devBase = program . command ( "dev" ) . description ( "Run your Trigger.dev tasks locally" ) ;
69+
70+ commonOptions (
71+ devBase
72+ . command ( "archive" )
73+ . description ( "Archive a dev branch" )
74+ . argument ( "[path]" , "The path to the project" , "." )
75+ . option (
76+ "-b, --branch <branch>" ,
77+ "The dev branch to archive. If not provided, we'll detect your local git branch."
78+ )
79+ . option ( "--skip-update-check" , "Skip checking for @trigger.dev package updates" )
80+ . option ( "-c, --config <config file>" , "The name of the config file, found at [path]" )
81+ . option (
82+ "-p, --project-ref <project ref>" ,
83+ "The project ref. Required if there is no config file. This will override the project specified in the config file."
84+ )
85+ . option (
86+ "--env-file <env file>" ,
87+ "Path to the .env file to load into the CLI process. Defaults to .env in the project directory."
88+ )
89+ ) . action ( async ( path , options ) => {
90+ await handleTelemetry ( async ( ) => {
91+ await printStandloneInitialBanner ( true , options . profile ) ;
92+ await devArchiveCommand ( path , options ) ;
93+ } ) ;
94+ } ) ;
95+
96+ commonOptions (
97+ devBase
5598 . option ( "-c, --config <config file>" , "The name of the config file" )
5699 . option (
57100 "-p, --project-ref <project ref>" ,
58101 "The project ref. Required if there is no config file."
59102 )
103+ . option (
104+ "-b, --branch <branch>" ,
105+ "The dev branch to use. If not provided, we'll use the default branch."
106+ )
60107 . option (
61108 "--env-file <env file>" ,
62109 "Path to the .env file to use for the dev session. Defaults to .env in the project directory."
@@ -164,8 +211,7 @@ export async function devCommand(options: DevCommandOptions) {
164211 ) ;
165212 } else {
166213 logger . log (
167- `${ chalkError ( "X Error:" ) } You must login first. Use the \`login\` CLI command.\n\n${
168- authorization . error
214+ `${ chalkError ( "X Error:" ) } You must login first. Use the \`login\` CLI command.\n\n${ authorization . error
169215 } `
170216 ) ;
171217 }
@@ -198,12 +244,14 @@ async function startDev(options: StartDevOptions) {
198244 logger . loggerLevel = options . logLevel ;
199245 }
200246
247+ const apiClient = new CliApiClient ( options . login . auth . apiUrl , options . login . auth . accessToken ) ;
248+
201249 const notificationPromise = options . skipPlatformNotifications
202250 ? undefined
203251 : fetchPlatformNotification ( {
204- apiClient : new CliApiClient ( options . login . auth . apiUrl , options . login . auth . accessToken ) ,
205- projectRef : options . projectRef ,
206- } ) ;
252+ apiClient,
253+ projectRef : options . projectRef ,
254+ } ) ;
207255
208256 await printStandloneInitialBanner ( true , options . profile ) ;
209257
@@ -215,7 +263,9 @@ async function startDev(options: StartDevOptions) {
215263 displayedUpdateMessage = await updateTriggerPackages ( options . cwd , { ...options } , true , true ) ;
216264 }
217265
218- const removeLockFile = await createLockFile ( options . cwd ) ;
266+ const branch = getDevBranch ( { specified : options . branch } ) ;
267+
268+ const removeLockFile = await createLockFile ( options . cwd , branch ) ;
219269
220270 let devInstance : DevSessionInstance | undefined ;
221271
@@ -246,13 +296,18 @@ async function startDev(options: StartDevOptions) {
246296
247297 logger . debug ( "Initial config" , watcher . config ) ;
248298
299+ if ( ! isDefaultDevBranch ( branch ) ) {
300+ await apiClient . upsertBranch ( watcher . config . project , { branch, env : "development" } ) ;
301+ }
302+
249303 // eslint-disable-next-line no-inner-declarations
250304 async function bootDevSession ( configParam : ResolvedConfig ) {
251305 const projectClient = await getProjectClient ( {
252306 accessToken : options . login . auth . accessToken ,
253307 apiUrl : options . login . auth . apiUrl ,
254308 projectRef : configParam . project ,
255309 env : "dev" ,
310+ branch,
256311 profile : options . profile ,
257312 } ) ;
258313
@@ -262,6 +317,7 @@ async function startDev(options: StartDevOptions) {
262317
263318 return startDevSession ( {
264319 name : projectClient . name ,
320+ branch,
265321 rawArgs : options ,
266322 rawConfig : configParam ,
267323 client : projectClient . client ,
@@ -274,7 +330,7 @@ async function startDev(options: StartDevOptions) {
274330
275331 devInstance = await bootDevSession ( watcher . config ) ;
276332
277- const waitUntilExit = async ( ) => { } ;
333+ const waitUntilExit = async ( ) => { } ;
278334
279335 return {
280336 watcher,
@@ -290,3 +346,87 @@ async function startDev(options: StartDevOptions) {
290346 throw error ;
291347 }
292348}
349+
350+ async function devArchiveCommand ( dir : string , options : unknown ) {
351+ return await wrapCommandAction (
352+ "devArchiveCommand" ,
353+ DevArchiveCommandOptions ,
354+ options ,
355+ async ( opts ) => {
356+ return await archiveDevBranchCommand ( dir , opts ) ;
357+ }
358+ ) ;
359+ }
360+
361+
362+ async function archiveDevBranchCommand ( dir : string , options : DevArchiveCommandOptions ) {
363+ intro ( `Archiving dev branch` ) ;
364+
365+ if ( ! options . skipUpdateCheck ) {
366+ await updateTriggerPackages ( dir , { ...options } , true , true ) ;
367+ }
368+
369+ const cwd = process . cwd ( ) ;
370+ const projectPath = resolve ( cwd , dir ) ;
371+
372+ verifyDirectory ( dir , projectPath ) ;
373+
374+ const authorization = await login ( {
375+ embedded : true ,
376+ defaultApiUrl : options . apiUrl ,
377+ profile : options . profile ,
378+ } ) ;
379+
380+ if ( ! authorization . ok ) {
381+ if ( authorization . error === "fetch failed" ) {
382+ throw new Error (
383+ `Failed to connect to ${ authorization . auth ?. apiUrl } . Are you sure it's the correct URL?`
384+ ) ;
385+ } else {
386+ throw new Error (
387+ `You must login first. Use the \`login\` CLI command.\n\n${ authorization . error } `
388+ ) ;
389+ }
390+ }
391+
392+ const resolvedConfig = await loadConfig ( {
393+ cwd : projectPath ,
394+ overrides : { project : options . projectRef } ,
395+ configFile : options . config ,
396+ } ) ;
397+
398+ logger . debug ( "Resolved config" , resolvedConfig ) ;
399+
400+ const branch = getDevBranch ( { specified : options . branch } ) ;
401+
402+ if ( ! branch ) {
403+ throw new Error (
404+ "Didn't auto-detect branch, so you need to specify a dev branch. Use --branch <branch>."
405+ ) ;
406+ }
407+
408+ const $buildSpinner = spinner ( ) ;
409+ $buildSpinner . start ( `Archiving "${ branch } "` ) ;
410+ const result = await archiveDevBranch ( authorization , branch , resolvedConfig . project ) ;
411+ $buildSpinner . stop (
412+ result ? `Successfully archived "${ branch } "` : `Failed to archive "${ branch } ".`
413+ ) ;
414+ return result ;
415+ }
416+
417+ async function archiveDevBranch (
418+ authorization : LoginResultOk ,
419+ branch : string ,
420+ project : string
421+ ) {
422+ const apiClient = new CliApiClient ( authorization . auth . apiUrl , authorization . auth . accessToken ) ;
423+
424+ const result = await apiClient . archiveBranch ( project , "development" , branch ) ;
425+
426+ if ( result . success ) {
427+ return true ;
428+ } else {
429+ logger . error ( result . error ) ;
430+ return false ;
431+ }
432+ }
0 commit comments