@@ -169,7 +169,8 @@ export interface ListParam extends ParamBase<string[]> {
169169 delimiter ?: string ;
170170}
171171
172- export interface TextInput < T > { // eslint-disable-line
172+ export interface TextInput < T > {
173+ // eslint-disable-line
173174 text : {
174175 example ?: string ;
175176
@@ -224,6 +225,9 @@ interface SecretParam {
224225 // A long description of the parameter's purpose and allowed values. If omitted, UX will not
225226 // provide a description of the parameter
226227 description ?: string ;
228+
229+ // The format of the secret, e.g. "json"
230+ format ?: string ;
227231}
228232
229233export type Param = StringParam | IntParam | BooleanParam | ListParam | SecretParam ;
@@ -390,6 +394,23 @@ export async function resolveParams(
390394 }
391395
392396 const [ needSecret , needPrompt ] = partition ( outstanding , ( param ) => param . type === "secret" ) ;
397+
398+ // Check for missing secrets in non-interactive mode
399+ if ( nonInteractive && needSecret . length > 0 ) {
400+ const secretNames = needSecret . map ( ( p ) => p . name ) . join ( ", " ) ;
401+ const commands = needSecret
402+ . map (
403+ ( p ) =>
404+ `\tfirebase functions:secrets:set ${ p . name } ${ ( p as SecretParam ) . format === "json" ? " --format=json --data-file <file.json>" : "" } ` ,
405+ )
406+ . join ( "\n" ) ;
407+ throw new FirebaseError (
408+ `In non-interactive mode but have no value for the following secrets: ${ secretNames } \n\n` +
409+ "Set these secrets before deploying:\n" +
410+ commands ,
411+ ) ;
412+ }
413+
393414 // The functions emulator will handle secrets
394415 if ( ! isEmulator ) {
395416 for ( const param of needSecret ) {
@@ -398,7 +419,7 @@ export async function resolveParams(
398419 }
399420
400421 if ( nonInteractive && needPrompt . length > 0 ) {
401- const envNames = outstanding . map ( ( p ) => p . name ) . join ( ", " ) ;
422+ const envNames = needPrompt . map ( ( p ) => p . name ) . join ( ", " ) ;
402423 throw new FirebaseError (
403424 `In non-interactive mode but have no value for the following environment variables: ${ envNames } \n` +
404425 "To continue, either run `firebase deploy` with an interactive terminal, or add values to a dotenv file. " +
@@ -460,17 +481,39 @@ function populateDefaultParams(config: FirebaseConfig): Record<string, ParamValu
460481 * to read its environment variables. They are instead provided through GCF's own
461482 * Secret Manager integration.
462483 */
463- async function handleSecret ( secretParam : SecretParam , projectId : string ) {
484+ async function handleSecret ( secretParam : SecretParam , projectId : string ) : Promise < void > {
464485 const metadata = await secretManager . getSecretMetadata ( projectId , secretParam . name , "latest" ) ;
465486 if ( ! metadata . secret ) {
466- const secretValue = await password ( {
467- message : `This secret will be stored in Cloud Secret Manager (https://cloud.google.com/secret-manager/pricing) as ${
468- secretParam . name
469- } . Enter a value for ${ secretParam . label || secretParam . name } :`,
470- } ) ;
487+ let secretValue : string ;
488+ const promptMessage = `This secret will be stored in Cloud Secret Manager (https://cloud.google.com/secret-manager/pricing) as ${
489+ secretParam . name
490+ } . Enter ${ secretParam . format === "json" ? "a JSON value" : "a value" } for ${
491+ secretParam . label || secretParam . name
492+ } :`;
493+
494+ if ( secretParam . format === "json" ) {
495+ secretValue = await input ( {
496+ message : promptMessage ,
497+ } ) ;
498+ try {
499+ JSON . parse ( secretValue ) ;
500+ } catch ( e : any ) {
501+ throw new FirebaseError (
502+ `Provided value for ${ secretParam . name } is not valid JSON.\n\n` +
503+ `For complex JSON values, use:\n` +
504+ ` firebase functions:secrets:set ${ secretParam . name } --format=json --data-file <file.json>\n` +
505+ `Or pipe from stdin:\n` +
506+ ` cat config.json | firebase functions:secrets:set ${ secretParam . name } --format=json` ,
507+ ) ;
508+ }
509+ } else {
510+ secretValue = await password ( {
511+ message : promptMessage ,
512+ } ) ;
513+ }
471514 await secretManager . createSecret ( projectId , secretParam . name , secretLabels ( ) ) ;
472515 await secretManager . addVersion ( projectId , secretParam . name , secretValue ) ;
473- return secretValue ;
516+ return ;
474517 } else if ( ! metadata . secretVersion ) {
475518 throw new FirebaseError (
476519 `Cloud Secret Manager has no latest version of the secret defined by param ${
0 commit comments