@@ -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,10 @@ 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. To use CI mode, please deploy in interactive mode first to select an organization and generate .blink/config.json"
96+ ) ;
8297 } else {
8398 const selectedId = await select ( {
8499 message : "Which organization should contain this agent?" ,
@@ -252,59 +267,78 @@ export default async function deploy(
252267 const missingEnvVars = Object . keys ( localEnv ) . filter ( ( key ) => ! prodEnv [ key ] ) ;
253268
254269 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
270+ if ( isCI ) {
271+ console . log (
272+ chalk . yellow ( "Warning:" ) +
273+ ` Missing ${ missingEnvVars . length } var${ missingEnvVars . length === 1 ? "" : "s" } in .env.production: ${ missingEnvVars . join ( ", " ) } `
274+ ) ;
275+ console . log (
276+ chalk . dim (
277+ " Skipping in CI mode. Set these in .env.production if needed."
278+ )
279+ ) ;
280+ } else {
281+ console . log ( "\n" + chalk . cyan ( "Environment Variables" ) ) ;
282+ console . log (
283+ chalk . dim (
284+ ` Missing ${ missingEnvVars . length } var${ missingEnvVars . length === 1 ? "" : "s" } in .env.production: ${ missingEnvVars . join ( ", " ) } `
279285 )
280- . map ( ( [ key , value ] ) => `${ key } =${ value } ` )
281- . join ( "\n" ) } `,
282- "utf-8"
283286 ) ;
287+
288+ const confirmed = await confirm ( {
289+ message : "Copy missing vars from .env.local to .env.production?" ,
290+ initialValue : true ,
291+ } ) ;
292+ if ( isCancel ( confirmed ) ) {
293+ return ;
294+ }
295+ // Add a newline for visual separation.
296+ console . log ( ) ;
297+ if ( confirmed ) {
298+ for ( const key of missingEnvVars ) {
299+ prodEnv [ key ] = localEnv [ key ] ! ;
300+ }
301+ await writeFile (
302+ prodEnvFile ,
303+ `# Environment variables for production deployment\n${ Object . entries (
304+ prodEnv
305+ )
306+ . map ( ( [ key , value ] ) => `${ key } =${ value } ` )
307+ . join ( "\n" ) } `,
308+ "utf-8"
309+ ) ;
310+ }
284311 }
285312 }
286313
287314 // Prompt to migrate devhook to production
288315 const devhookID = getDevhookID ( directory ) ;
289316 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- ) ;
317+ if ( isCI ) {
318+ // Skip devhook migration in CI mode
319+ console . log (
320+ chalk . dim ( " Skipping webhook tunnel migration in CI mode" )
321+ ) ;
322+ } else {
323+ const productionUrl = `https://${ devhookID } .blink.host` ;
324+ console . log ( "\n" + chalk . cyan ( "Webhook Tunnel" ) ) ;
325+ console . log ( chalk . dim ( ` Current: ${ productionUrl } → local dev` ) ) ;
326+ console . log ( chalk . dim ( ` After: ${ productionUrl } → production` ) ) ;
327+ console . log (
328+ chalk . dim ( " Migrating will keep your webhooks working in production" )
329+ ) ;
297330
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 ;
331+ const confirmed = await confirm ( {
332+ message : "Migrate tunnel to production?" ,
333+ } ) ;
334+ if ( isCancel ( confirmed ) ) {
335+ return ;
336+ }
337+ // Add a newline for visual separation.
338+ console . log ( ) ;
339+ if ( confirmed ) {
340+ migratedDevhook = true ;
341+ }
308342 }
309343 }
310344 }
@@ -362,17 +396,28 @@ export default async function deploy(
362396 ( key ) => ! Object . keys ( prodEnv ) . includes ( key )
363397 ) ;
364398 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 ;
399+ if ( isCI ) {
400+ console . log (
401+ chalk . yellow ( "Warning:" ) +
402+ " The following environment variables are set in .env.local but not in .env.production:"
403+ ) ;
404+ for ( const v of missingEnvVars ) {
405+ console . log ( `- ${ v } ` ) ;
406+ }
407+ console . log ( chalk . dim ( " Continuing deployment in CI mode" ) ) ;
408+ } else {
409+ console . log (
410+ "Warning: The following environment variables are set in .env.local but not in .env.production:"
411+ ) ;
412+ for ( const v of missingEnvVars ) {
413+ console . log ( `- ${ v } ` ) ;
414+ }
415+ const confirmed = await confirm ( {
416+ message : "Do you want to deploy anyway?" ,
417+ } ) ;
418+ if ( confirmed === false || isCancel ( confirmed ) ) {
419+ return ;
420+ }
376421 }
377422 }
378423
@@ -398,7 +443,9 @@ export default async function deploy(
398443 console . log ( chalk . gray ( `View Deployment ${ chalk . dim ( inspectUrl ) } ` ) ) ;
399444
400445 // Write deploy config on success
401- await writeDeployConfig ( deployConfigPath , deployConfig ) ;
446+ if ( ! isCI ) {
447+ await writeDeployConfig ( deployConfigPath , deployConfig ) ;
448+ }
402449
403450 // Poll for deployment completion
404451 const s = spinner ( ) ;
0 commit comments