1
1
import * as clc from "colorette" ;
2
2
import * as _ from "lodash" ;
3
3
4
- import { FirebaseError } from "../../error" ;
5
4
import {
6
5
addFirebaseToCloudProjectAndLog ,
7
6
createFirebaseProjectAndLog ,
8
7
getFirebaseProject ,
9
- getOrPromptProject ,
10
8
promptAvailableProjectId ,
11
9
promptProjectCreation ,
10
+ selectProjectInteractively ,
12
11
} from "../../management/projects" ;
13
12
import { FirebaseProjectMetadata } from "../../types/project" ;
14
13
import { logger } from "../../logger" ;
15
14
import * as utils from "../../utils" ;
16
15
import * as prompt from "../../prompt" ;
17
- import { Options } from "../../options" ;
18
- import { EmulatorHub } from "../../emulator/hub" ;
19
16
import { requireAuth } from "../../requireAuth" ;
17
+ import { Constants } from "../../emulator/constants" ;
20
18
21
19
const OPTION_NO_PROJECT = "Don't set up a default project" ;
22
20
const OPTION_USE_PROJECT = "Use an existing project" ;
23
21
const OPTION_NEW_PROJECT = "Create a new project" ;
24
22
const OPTION_ADD_FIREBASE = "Add Firebase to an existing Google Cloud Platform project" ;
25
23
26
- /**
27
- * Used in init flows to keep information about the project - basically
28
- * a shorter version of {@link FirebaseProjectMetadata} with some additional fields.
29
- */
30
- export interface InitProjectInfo {
31
- id : string ; // maps to FirebaseProjectMetadata.projectId
32
- label ?: string ;
33
- instance ?: string ; // maps to FirebaseProjectMetadata.resources.realtimeDatabaseInstance
34
- location ?: string ; // maps to FirebaseProjectMetadata.resources.locationId
35
- }
36
-
37
- function toInitProjectInfo ( projectMetaData : FirebaseProjectMetadata ) : InitProjectInfo {
38
- const { projectId, displayName, resources } = projectMetaData ;
39
- return {
40
- id : projectId ,
41
- label : `${ projectId } ` + ( displayName ? ` (${ displayName } )` : "" ) ,
42
- instance : resources ?. realtimeDatabaseInstance ,
43
- location : resources ?. locationId ,
44
- } ;
45
- }
46
-
47
- async function promptAndCreateNewProject ( options : Options ) : Promise < FirebaseProjectMetadata > {
48
- utils . logBullet (
49
- "If you want to create a project in a Google Cloud organization or folder, please use " +
50
- `"firebase projects:create" instead, and return to this command when you've created the project.` ,
51
- ) ;
52
- const { projectId, displayName } = await promptProjectCreation ( options ) ;
53
- // N.B. This shouldn't be possible because of the validator on the input field, but it
54
- // is being left around in case there's something I don't know.
55
- if ( ! projectId ) {
56
- throw new FirebaseError ( "Project ID cannot be empty" ) ;
57
- }
58
-
59
- return await createFirebaseProjectAndLog ( projectId , { displayName } ) ;
60
- }
61
-
62
- async function promptAndAddFirebaseToCloudProject ( ) : Promise < FirebaseProjectMetadata > {
63
- const projectId = await promptAvailableProjectId ( ) ;
64
- if ( ! projectId ) {
65
- // N.B. This shouldn't be possible because of the validator on the input field, but it
66
- // is being left around in case there's something I don't know.
67
- throw new FirebaseError ( "Project ID cannot be empty" ) ;
68
- }
69
- return await addFirebaseToCloudProjectAndLog ( projectId ) ;
70
- }
71
-
72
- /**
73
- * Prompt the user about how they would like to select a project.
74
- * @param options the Firebase CLI options object.
75
- * @return the project metadata, or undefined if no project was selected.
76
- */
77
- async function projectChoicePrompt ( options : any ) : Promise < FirebaseProjectMetadata | undefined > {
78
- const choices = [ OPTION_USE_PROJECT , OPTION_NEW_PROJECT , OPTION_ADD_FIREBASE , OPTION_NO_PROJECT ] ;
79
- const projectSetupOption : string = await prompt . select < ( typeof choices ) [ number ] > ( {
80
- message : "Please select an option:" ,
81
- choices,
82
- } ) ;
83
-
84
- switch ( projectSetupOption ) {
85
- case OPTION_USE_PROJECT :
86
- return getOrPromptProject ( options ) ;
87
- case OPTION_NEW_PROJECT :
88
- return promptAndCreateNewProject ( options ) ;
89
- case OPTION_ADD_FIREBASE :
90
- return promptAndAddFirebaseToCloudProject ( ) ;
91
- default :
92
- // Do nothing if user chooses NO_PROJECT
93
- return ;
94
- }
95
- }
96
-
97
24
/**
98
25
* Sets up the default project if provided and writes .firebaserc file.
99
26
* @param setup A helper object to use for the rest of the init features.
@@ -102,63 +29,95 @@ async function projectChoicePrompt(options: any): Promise<FirebaseProjectMetadat
102
29
*/
103
30
export async function doSetup ( setup : any , config : any , options : any ) : Promise < void > {
104
31
setup . project = { } ;
105
- if ( options . project === EmulatorHub . MISSING_PROJECT_PLACEHOLDER ) {
106
- logger . info ( `Skipping Firebase project setup given --project=${ options . project } ` ) ;
107
- return ;
108
- }
109
- await requireAuth ( options ) ;
110
32
111
33
logger . info ( ) ;
112
34
logger . info ( `First, let's associate this project directory with a Firebase project.` ) ;
113
35
logger . info (
114
36
`You can create multiple project aliases by running ${ clc . bold ( "firebase use --add" ) } , ` ,
115
37
) ;
116
- logger . info ( `but for now we'll just set up a default project.` ) ;
117
38
logger . info ( ) ;
118
39
40
+ if ( options . project ) {
41
+ // If the user presented a project with `--project`, try to fetch that project.
42
+ if ( Constants . isDemoProject ( options . project ) ) {
43
+ logger . info ( `Skipping Firebase project setup because a demo project is provided` ) ;
44
+ return ;
45
+ }
46
+ await requireAuth ( options ) ;
47
+ await usingProject ( setup , config , options . project , "--project flag" ) ;
48
+ return ;
49
+ }
119
50
const projectFromRcFile = setup . rcfile ?. projects ?. default ;
120
- if ( projectFromRcFile && ! options . project ) {
121
- utils . logBullet ( `.firebaserc already has a default project, using ${ projectFromRcFile } .` ) ;
122
- // we still need to get project info in case user wants to init firestore or storage, which
123
- // require a resource location:
124
- const rcProject : FirebaseProjectMetadata = await getFirebaseProject ( projectFromRcFile ) ;
125
- setup . projectId = rcProject . projectId ;
126
- setup . projectLocation = rcProject ?. resources ?. locationId ;
51
+ if ( projectFromRcFile ) {
52
+ await usingProject ( setup , config , projectFromRcFile as string , ".firebaserc" ) ;
127
53
return ;
128
54
}
129
-
130
- let projectMetaData ;
131
- if ( options . project ) {
132
- // If the user presented a project with `--project`, try to fetch that project.
133
- logger . debug ( `Using project from CLI flag: ${ options . project } ` ) ;
134
- projectMetaData = await getFirebaseProject ( options . project ) ;
135
- } else {
136
- const projectEnvVar = utils . envOverride ( "FIREBASE_PROJECT" , "" ) ;
55
+ const projectEnvVar = utils . envOverride ( "FIREBASE_PROJECT" , "" ) ;
56
+ if ( projectEnvVar ) {
137
57
// If env var $FIREBASE_PROJECT is set, try to fetch that project.
138
58
// This is used in some shell scripts e.g. under https://firebase.tools/.
139
- if ( projectEnvVar ) {
140
- logger . debug ( `Using project from $FIREBASE_PROJECT: ${ projectEnvVar } ` ) ;
141
- projectMetaData = await getFirebaseProject ( projectEnvVar ) ;
142
- } else {
143
- if ( options . nonInteractive ) {
144
- logger . info (
145
- "No default project found. Continuing without a project in non interactive mode." ,
146
- ) ;
147
- return ;
148
- }
149
- projectMetaData = await projectChoicePrompt ( options ) ;
150
- if ( ! projectMetaData ) {
151
- return ;
152
- }
59
+ await usingProject ( setup , config , projectEnvVar , "$FIREBASE_PROJECT" ) ;
60
+ return ;
61
+ }
62
+ if ( options . nonInteractive ) {
63
+ logger . info ( "No default project found. Continuing without a project in non interactive mode." ) ;
64
+ return ;
65
+ }
66
+
67
+ // Prompt users about how to setup a project.
68
+ const choices = [ OPTION_USE_PROJECT , OPTION_NEW_PROJECT , OPTION_ADD_FIREBASE , OPTION_NO_PROJECT ] ;
69
+ const projectSetupOption : string = await prompt . select < ( typeof choices ) [ number ] > ( {
70
+ message : "Please select an option:" ,
71
+ choices,
72
+ } ) ;
73
+ switch ( projectSetupOption ) {
74
+ case OPTION_USE_PROJECT : {
75
+ await requireAuth ( options ) ;
76
+ const pm = await selectProjectInteractively ( ) ;
77
+ return await usingProjectMetadata ( setup , config , pm ) ;
78
+ }
79
+ case OPTION_NEW_PROJECT : {
80
+ utils . logBullet (
81
+ "If you want to create a project in a Google Cloud organization or folder, please use " +
82
+ `"firebase projects:create" instead, and return to this command when you've created the project.` ,
83
+ ) ;
84
+ await requireAuth ( options ) ;
85
+ const { projectId, displayName } = await promptProjectCreation ( options ) ;
86
+ const pm = await createFirebaseProjectAndLog ( projectId , { displayName } ) ;
87
+ return await usingProjectMetadata ( setup , config , pm ) ;
153
88
}
89
+ case OPTION_ADD_FIREBASE : {
90
+ await requireAuth ( options ) ;
91
+ const pm = await addFirebaseToCloudProjectAndLog ( await promptAvailableProjectId ( ) ) ;
92
+ return await usingProjectMetadata ( setup , config , pm ) ;
93
+ }
94
+ default :
95
+ // Do nothing if user chooses NO_PROJECT
96
+ return ;
154
97
}
98
+ }
99
+
100
+ async function usingProject (
101
+ setup : any ,
102
+ config : any ,
103
+ projectId : string ,
104
+ from : string ,
105
+ ) : Promise < void > {
106
+ const pm = await getFirebaseProject ( projectId ) ;
107
+ const label = `${ pm . projectId } ` + ( pm . displayName ? ` (${ pm . displayName } )` : "" ) ;
108
+ utils . logBullet ( `Using project ${ label } from ${ from } .` ) ;
109
+ await usingProjectMetadata ( setup , config , pm ) ;
110
+ }
155
111
156
- const projectInfo = toInitProjectInfo ( projectMetaData ) ;
157
- utils . logBullet ( `Using project ${ projectInfo . label } ` ) ;
112
+ async function usingProjectMetadata (
113
+ setup : any ,
114
+ config : any ,
115
+ pm : FirebaseProjectMetadata ,
116
+ ) : Promise < void > {
158
117
// write "default" alias and activate it immediately
159
- _ . set ( setup . rcfile , "projects.default" , projectInfo . id ) ;
160
- setup . projectId = projectInfo . id ;
161
- setup . instance = projectInfo . instance ;
162
- setup . projectLocation = projectInfo . location ;
163
- utils . makeActiveProject ( config . projectDir , projectInfo . id ) ;
118
+ _ . set ( setup . rcfile , "projects.default" , pm . projectId ) ;
119
+ setup . projectId = pm . projectId ;
120
+ setup . instance = pm . resources ?. realtimeDatabaseInstance ;
121
+ setup . projectLocation = pm . resources ?. locationId ;
122
+ utils . makeActiveProject ( config . projectDir , pm . projectId ) ;
164
123
}
0 commit comments