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,22 @@ 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+
37+ const DevArchiveCommandOptions = CommonCommandOptions . extend ( {
38+ branch : z . string ( ) . optional ( ) ,
39+ config : z . string ( ) . optional ( ) ,
40+ projectRef : z . string ( ) . optional ( ) ,
41+ skipUpdateCheck : z . boolean ( ) . default ( false ) ,
42+ } ) ;
43+
44+ type DevArchiveCommandOptions = z . infer < typeof DevArchiveCommandOptions > ;
3045
3146const DevCommandOptions = CommonCommandOptions . extend ( {
3247 debugOtel : z . boolean ( ) . default ( false ) ,
3348 config : z . string ( ) . optional ( ) ,
3449 projectRef : z . string ( ) . optional ( ) ,
50+ branch : z . string ( ) . optional ( ) ,
3551 skipUpdateCheck : z . boolean ( ) . default ( false ) ,
3652 skipPlatformNotifications : z . boolean ( ) . default ( false ) ,
3753 envFile : z . string ( ) . optional ( ) ,
@@ -48,15 +64,45 @@ const DevCommandOptions = CommonCommandOptions.extend({
4864export type DevCommandOptions = z . infer < typeof DevCommandOptions > ;
4965
5066export function configureDevCommand ( program : Command ) {
51- return commonOptions (
52- program
53- . command ( "dev" )
54- . description ( "Run your Trigger.dev tasks locally" )
67+ const devBase = program . command ( "dev" ) . description ( "Run your Trigger.dev tasks locally" ) ;
68+
69+ commonOptions (
70+ devBase
71+ . command ( "archive" )
72+ . description ( "Archive a dev branch" )
73+ . argument ( "[path]" , "The path to the project" , "." )
74+ . option (
75+ "-b, --branch <branch>" ,
76+ "The dev branch to archive. If not provided, we'll detect your local git branch."
77+ )
78+ . option ( "--skip-update-check" , "Skip checking for @trigger.dev package updates" )
79+ . option ( "-c, --config <config file>" , "The name of the config file, found at [path]" )
80+ . option (
81+ "-p, --project-ref <project ref>" ,
82+ "The project ref. Required if there is no config file. This will override the project specified in the config file."
83+ )
84+ . option (
85+ "--env-file <env file>" ,
86+ "Path to the .env file to load into the CLI process. Defaults to .env in the project directory."
87+ )
88+ ) . action ( async ( path , options ) => {
89+ await handleTelemetry ( async ( ) => {
90+ await printStandloneInitialBanner ( true , options . profile ) ;
91+ await devArchiveCommand ( path , options ) ;
92+ } ) ;
93+ } ) ;
94+
95+ commonOptions (
96+ devBase
5597 . option ( "-c, --config <config file>" , "The name of the config file" )
5698 . option (
5799 "-p, --project-ref <project ref>" ,
58100 "The project ref. Required if there is no config file."
59101 )
102+ . option (
103+ "-b, --branch <branch>" ,
104+ "The dev branch to use. If not provided, we'll use the default branch."
105+ )
60106 . option (
61107 "--env-file <env file>" ,
62108 "Path to the .env file to use for the dev session. Defaults to .env in the project directory."
@@ -164,8 +210,7 @@ export async function devCommand(options: DevCommandOptions) {
164210 ) ;
165211 } else {
166212 logger . log (
167- `${ chalkError ( "X Error:" ) } You must login first. Use the \`login\` CLI command.\n\n${
168- authorization . error
213+ `${ chalkError ( "X Error:" ) } You must login first. Use the \`login\` CLI command.\n\n${ authorization . error
169214 } `
170215 ) ;
171216 }
@@ -198,12 +243,14 @@ async function startDev(options: StartDevOptions) {
198243 logger . loggerLevel = options . logLevel ;
199244 }
200245
246+ const apiClient = new CliApiClient ( options . login . auth . apiUrl , options . login . auth . accessToken ) ;
247+
201248 const notificationPromise = options . skipPlatformNotifications
202249 ? undefined
203250 : fetchPlatformNotification ( {
204- apiClient : new CliApiClient ( options . login . auth . apiUrl , options . login . auth . accessToken ) ,
205- projectRef : options . projectRef ,
206- } ) ;
251+ apiClient,
252+ projectRef : options . projectRef ,
253+ } ) ;
207254
208255 await printStandloneInitialBanner ( true , options . profile ) ;
209256
@@ -215,7 +262,9 @@ async function startDev(options: StartDevOptions) {
215262 displayedUpdateMessage = await updateTriggerPackages ( options . cwd , { ...options } , true , true ) ;
216263 }
217264
218- const removeLockFile = await createLockFile ( options . cwd ) ;
265+ const branch = getDevBranch ( { specified : options . branch } ) ;
266+
267+ const removeLockFile = await createLockFile ( options . cwd , branch ) ;
219268
220269 let devInstance : DevSessionInstance | undefined ;
221270
@@ -246,13 +295,18 @@ async function startDev(options: StartDevOptions) {
246295
247296 logger . debug ( "Initial config" , watcher . config ) ;
248297
298+ if ( branch !== "default" ) {
299+ await apiClient . upsertBranch ( watcher . config . project , { branch, env : "development" } ) ;
300+ }
301+
249302 // eslint-disable-next-line no-inner-declarations
250303 async function bootDevSession ( configParam : ResolvedConfig ) {
251304 const projectClient = await getProjectClient ( {
252305 accessToken : options . login . auth . accessToken ,
253306 apiUrl : options . login . auth . apiUrl ,
254307 projectRef : configParam . project ,
255308 env : "dev" ,
309+ branch,
256310 profile : options . profile ,
257311 } ) ;
258312
@@ -262,6 +316,7 @@ async function startDev(options: StartDevOptions) {
262316
263317 return startDevSession ( {
264318 name : projectClient . name ,
319+ branch,
265320 rawArgs : options ,
266321 rawConfig : configParam ,
267322 client : projectClient . client ,
@@ -274,7 +329,7 @@ async function startDev(options: StartDevOptions) {
274329
275330 devInstance = await bootDevSession ( watcher . config ) ;
276331
277- const waitUntilExit = async ( ) => { } ;
332+ const waitUntilExit = async ( ) => { } ;
278333
279334 return {
280335 watcher,
@@ -290,3 +345,87 @@ async function startDev(options: StartDevOptions) {
290345 throw error ;
291346 }
292347}
348+
349+ async function devArchiveCommand ( dir : string , options : unknown ) {
350+ return await wrapCommandAction (
351+ "devArchiveCommand" ,
352+ DevArchiveCommandOptions ,
353+ options ,
354+ async ( opts ) => {
355+ return await archiveDevBranchCommand ( dir , opts ) ;
356+ }
357+ ) ;
358+ }
359+
360+
361+ async function archiveDevBranchCommand ( dir : string , options : DevArchiveCommandOptions ) {
362+ intro ( `Archiving dev branch` ) ;
363+
364+ if ( ! options . skipUpdateCheck ) {
365+ await updateTriggerPackages ( dir , { ...options } , true , true ) ;
366+ }
367+
368+ const cwd = process . cwd ( ) ;
369+ const projectPath = resolve ( cwd , dir ) ;
370+
371+ verifyDirectory ( dir , projectPath ) ;
372+
373+ const authorization = await login ( {
374+ embedded : true ,
375+ defaultApiUrl : options . apiUrl ,
376+ profile : options . profile ,
377+ } ) ;
378+
379+ if ( ! authorization . ok ) {
380+ if ( authorization . error === "fetch failed" ) {
381+ throw new Error (
382+ `Failed to connect to ${ authorization . auth ?. apiUrl } . Are you sure it's the correct URL?`
383+ ) ;
384+ } else {
385+ throw new Error (
386+ `You must login first. Use the \`login\` CLI command.\n\n${ authorization . error } `
387+ ) ;
388+ }
389+ }
390+
391+ const resolvedConfig = await loadConfig ( {
392+ cwd : projectPath ,
393+ overrides : { project : options . projectRef } ,
394+ configFile : options . config ,
395+ } ) ;
396+
397+ logger . debug ( "Resolved config" , resolvedConfig ) ;
398+
399+ const branch = getDevBranch ( { specified : options . branch } ) ;
400+
401+ if ( ! branch ) {
402+ throw new Error (
403+ "Didn't auto-detect branch, so you need to specify a dev branch. Use --branch <branch>."
404+ ) ;
405+ }
406+
407+ const $buildSpinner = spinner ( ) ;
408+ $buildSpinner . start ( `Archiving "${ branch } "` ) ;
409+ const result = await archiveDevBranch ( authorization , branch , resolvedConfig . project ) ;
410+ $buildSpinner . stop (
411+ result ? `Successfully archived "${ branch } "` : `Failed to archive "${ branch } ".`
412+ ) ;
413+ return result ;
414+ }
415+
416+ async function archiveDevBranch (
417+ authorization : LoginResultOk ,
418+ branch : string ,
419+ project : string
420+ ) {
421+ const apiClient = new CliApiClient ( authorization . auth . apiUrl , authorization . auth . accessToken ) ;
422+
423+ const result = await apiClient . archiveBranch ( project , "development" , branch ) ;
424+
425+ if ( result . success ) {
426+ return true ;
427+ } else {
428+ logger . error ( result . error ) ;
429+ return false ;
430+ }
431+ }
0 commit comments