@@ -26,6 +26,9 @@ export default async function deploy(
2626 directory = process . cwd ( ) ;
2727 }
2828
29+ // Detect CI environment
30+ const isCI = process . env . CI === "true" || ! process . stdout . isTTY ;
31+
2932 // Auto-migrate data to .blink if it exists
3033 await migrateDataToBlink ( directory ) ;
3134
@@ -63,6 +66,14 @@ export default async function deploy(
6366 deployConfig = JSON . parse ( deployConfigContent ) ;
6467 }
6568
69+ // Environment variables take precedence over config file
70+ if ( process . env . BLINK_ORGANIZATION_ID ) {
71+ deployConfig . organizationId = process . env . BLINK_ORGANIZATION_ID ;
72+ }
73+ if ( process . env . BLINK_AGENT_ID ) {
74+ deployConfig . agentId = process . env . BLINK_AGENT_ID ;
75+ }
76+
6677 // Select organization
6778 let organizationName ! : string ;
6879 if ( deployConfig ?. organizationId ) {
@@ -79,6 +90,11 @@ export default async function deploy(
7990 if ( organizations . length === 1 ) {
8091 deployConfig . organizationId = organizations [ 0 ] ! . id ;
8192 organizationName = organizations [ 0 ] ! . name ;
93+ } else if ( isCI ) {
94+ throw new Error (
95+ "Multiple organizations found. In CI mode, you must first deploy locally to select an organization, " +
96+ "or set the organization in .blink/config.json"
97+ ) ;
8298 } else {
8399 const selectedId = await select ( {
84100 message : "Which organization should contain this agent?" ,
@@ -252,59 +268,78 @@ export default async function deploy(
252268 const missingEnvVars = Object . keys ( localEnv ) . filter ( ( key ) => ! prodEnv [ key ] ) ;
253269
254270 if ( missingEnvVars . length > 0 ) {
255- console . log ( "\n" + chalk . cyan ( "Environment Variables" ) ) ;
256- console . log (
257- chalk . dim (
258- ` Missing ${ missingEnvVars . length } var${ missingEnvVars . length === 1 ? "" : "s" } in .env.production: ${ missingEnvVars . join ( ", " ) } `
259- )
260- ) ;
261-
262- const confirmed = await confirm ( {
263- message : "Copy missing vars from .env.local to .env.production?" ,
264- initialValue : true ,
265- } ) ;
266- if ( isCancel ( confirmed ) ) {
267- return ;
268- }
269- // Add a newline for visual separation.
270- console . log ( ) ;
271- if ( confirmed ) {
272- for ( const key of missingEnvVars ) {
273- prodEnv [ key ] = localEnv [ key ] ! ;
274- }
275- await writeFile (
276- prodEnvFile ,
277- `# Environment variables for production deployment\n${ Object . entries (
278- prodEnv
271+ if ( isCI ) {
272+ console . log (
273+ chalk . yellow ( "Warning:" ) +
274+ ` Missing ${ missingEnvVars . length } var${ missingEnvVars . length === 1 ? "" : "s" } in .env.production: ${ missingEnvVars . join ( ", " ) } `
275+ ) ;
276+ console . log (
277+ chalk . dim (
278+ " Skipping in CI mode. Set these in .env.production if needed."
279+ )
280+ ) ;
281+ } else {
282+ console . log ( "\n" + chalk . cyan ( "Environment Variables" ) ) ;
283+ console . log (
284+ chalk . dim (
285+ ` Missing ${ missingEnvVars . length } var${ missingEnvVars . length === 1 ? "" : "s" } in .env.production: ${ missingEnvVars . join ( ", " ) } `
279286 )
280- . map ( ( [ key , value ] ) => `${ key } =${ value } ` )
281- . join ( "\n" ) } `,
282- "utf-8"
283287 ) ;
288+
289+ const confirmed = await confirm ( {
290+ message : "Copy missing vars from .env.local to .env.production?" ,
291+ initialValue : true ,
292+ } ) ;
293+ if ( isCancel ( confirmed ) ) {
294+ return ;
295+ }
296+ // Add a newline for visual separation.
297+ console . log ( ) ;
298+ if ( confirmed ) {
299+ for ( const key of missingEnvVars ) {
300+ prodEnv [ key ] = localEnv [ key ] ! ;
301+ }
302+ await writeFile (
303+ prodEnvFile ,
304+ `# Environment variables for production deployment\n${ Object . entries (
305+ prodEnv
306+ )
307+ . map ( ( [ key , value ] ) => `${ key } =${ value } ` )
308+ . join ( "\n" ) } `,
309+ "utf-8"
310+ ) ;
311+ }
284312 }
285313 }
286314
287315 // Prompt to migrate devhook to production
288316 const devhookID = getDevhookID ( directory ) ;
289317 if ( devhookID ) {
290- const productionUrl = `https://${ devhookID } .blink.host` ;
291- console . log ( "\n" + chalk . cyan ( "Webhook Tunnel" ) ) ;
292- console . log ( chalk . dim ( ` Current: ${ productionUrl } → local dev` ) ) ;
293- console . log ( chalk . dim ( ` After: ${ productionUrl } → production` ) ) ;
294- console . log (
295- chalk . dim ( " Migrating will keep your webhooks working in production" )
296- ) ;
318+ if ( isCI ) {
319+ // Skip devhook migration in CI mode
320+ console . log (
321+ chalk . dim ( " Skipping webhook tunnel migration in CI mode" )
322+ ) ;
323+ } else {
324+ const productionUrl = `https://${ devhookID } .blink.host` ;
325+ console . log ( "\n" + chalk . cyan ( "Webhook Tunnel" ) ) ;
326+ console . log ( chalk . dim ( ` Current: ${ productionUrl } → local dev` ) ) ;
327+ console . log ( chalk . dim ( ` After: ${ productionUrl } → production` ) ) ;
328+ console . log (
329+ chalk . dim ( " Migrating will keep your webhooks working in production" )
330+ ) ;
297331
298- const confirmed = await confirm ( {
299- message : "Migrate tunnel to production?" ,
300- } ) ;
301- if ( isCancel ( confirmed ) ) {
302- return ;
303- }
304- // Add a newline for visual separation.
305- console . log ( ) ;
306- if ( confirmed ) {
307- migratedDevhook = true ;
332+ const confirmed = await confirm ( {
333+ message : "Migrate tunnel to production?" ,
334+ } ) ;
335+ if ( isCancel ( confirmed ) ) {
336+ return ;
337+ }
338+ // Add a newline for visual separation.
339+ console . log ( ) ;
340+ if ( confirmed ) {
341+ migratedDevhook = true ;
342+ }
308343 }
309344 }
310345 }
@@ -362,17 +397,28 @@ export default async function deploy(
362397 ( key ) => ! Object . keys ( prodEnv ) . includes ( key )
363398 ) ;
364399 if ( missingEnvVars . length > 0 ) {
365- console . log (
366- "Warning: The following environment variables are set in .env.local but not in .env.production:"
367- ) ;
368- for ( const v of missingEnvVars ) {
369- console . log ( `- ${ v } ` ) ;
370- }
371- const confirmed = await confirm ( {
372- message : "Do you want to deploy anyway?" ,
373- } ) ;
374- if ( confirmed === false || isCancel ( confirmed ) ) {
375- return ;
400+ if ( isCI ) {
401+ console . log (
402+ chalk . yellow ( "Warning:" ) +
403+ " The following environment variables are set in .env.local but not in .env.production:"
404+ ) ;
405+ for ( const v of missingEnvVars ) {
406+ console . log ( `- ${ v } ` ) ;
407+ }
408+ console . log ( chalk . dim ( " Continuing deployment in CI mode" ) ) ;
409+ } else {
410+ console . log (
411+ "Warning: The following environment variables are set in .env.local but not in .env.production:"
412+ ) ;
413+ for ( const v of missingEnvVars ) {
414+ console . log ( `- ${ v } ` ) ;
415+ }
416+ const confirmed = await confirm ( {
417+ message : "Do you want to deploy anyway?" ,
418+ } ) ;
419+ if ( confirmed === false || isCancel ( confirmed ) ) {
420+ return ;
421+ }
376422 }
377423 }
378424
@@ -398,7 +444,9 @@ export default async function deploy(
398444 console . log ( chalk . gray ( `View Deployment ${ chalk . dim ( inspectUrl ) } ` ) ) ;
399445
400446 // Write deploy config on success
401- await writeDeployConfig ( deployConfigPath , deployConfig ) ;
447+ if ( ! isCI ) {
448+ await writeDeployConfig ( deployConfigPath , deployConfig ) ;
449+ }
402450
403451 // Poll for deployment completion
404452 const s = spinner ( ) ;
0 commit comments