@@ -29,7 +29,7 @@ const apphostingPollerOptions: Omit<poller.OperationPollerOptions, "operationRes
2929/**
3030 * Set up a new App Hosting backend.
3131 */
32- export async function doSetup ( setup : any , projectId : string ) : Promise < void > {
32+ export async function doSetup ( projectId : string , location : string | null ) : Promise < void > {
3333 await Promise . all ( [
3434 ensure ( projectId , "cloudbuild.googleapis.com" , "apphosting" , true ) ,
3535 ensure ( projectId , "secretmanager.googleapis.com" , "apphosting" , true ) ,
@@ -39,70 +39,52 @@ export async function doSetup(setup: any, projectId: string): Promise<void> {
3939
4040 const allowedLocations = ( await apphosting . listLocations ( projectId ) ) . map ( ( loc ) => loc . locationId ) ;
4141
42- if ( setup . location ) {
43- if ( ! allowedLocations . includes ( setup . location ) ) {
42+ if ( location ) {
43+ if ( ! allowedLocations . includes ( location ) ) {
4444 throw new FirebaseError (
45- `Invalid location ${ setup . location } . Valid choices are ${ allowedLocations . join ( ", " ) } ` ,
45+ `Invalid location ${ location } . Valid choices are ${ allowedLocations . join ( ", " ) } ` ,
4646 ) ;
4747 }
4848 }
4949
5050 logBullet ( "First we need a few details to create your backend." ) ;
5151
52- const location : string = setup . location || ( await promptLocation ( projectId , allowedLocations ) ) ;
52+ location =
53+ location ||
54+ ( ( await promptOnce ( {
55+ name : "region" ,
56+ type : "list" ,
57+ default : DEFAULT_REGION ,
58+ message :
59+ "Please select a region " +
60+ `(${ clc . yellow ( "info" ) } : Your region determines where your backend is located):\n` ,
61+ choices : allowedLocations . map ( ( loc ) => ( { value : loc } ) ) ,
62+ } ) ) as string ) ;
5363
5464 logSuccess ( `Region set to ${ location } .\n` ) ;
5565
56- let backendId : string ;
57- while ( true ) {
58- backendId = await promptOnce ( {
59- name : "backendId" ,
60- type : "input" ,
61- default : "my-web-app" ,
62- message : "Create a name for your backend [1-30 characters]" ,
63- } ) ;
64- try {
65- await apphosting . getBackend ( projectId , location , backendId ) ;
66- } catch ( err : any ) {
67- if ( err . status === 404 ) {
68- break ;
69- }
70- throw new FirebaseError (
71- `Failed to check if backend with id ${ backendId } already exists in ${ location } ` ,
72- { original : err } ,
73- ) ;
74- }
75- logWarning ( `Backend with id ${ backendId } already exists in ${ location } ` ) ;
76- }
66+ const backendId = await promptNewBackendId ( projectId , location , {
67+ name : "backendId" ,
68+ type : "input" ,
69+ default : "my-web-app" ,
70+ message : "Create a name for your backend [1-30 characters]" ,
71+ } ) ;
7772
78- const backend = await onboardBackend ( projectId , location , backendId ) ;
73+ const cloudBuildConnRepo = await repo . linkGitHubRepository ( projectId , location ) ;
74+
75+ const backend = await createBackend ( projectId , location , backendId , cloudBuildConnRepo ) ;
7976
8077 // TODO: Once tag patterns are implemented, prompt which method the user
81- // prefers. We could reduce the nubmer of questions asked by letting people
78+ // prefers. We could reduce the number of questions asked by letting people
8279 // enter tag:<pattern>?
8380 const branch = await promptOnce ( {
8481 name : "branch" ,
8582 type : "input" ,
8683 default : "main" ,
8784 message : "Pick a branch for continuous deployment" ,
8885 } ) ;
89- const traffic : DeepOmit < apphosting . Traffic , apphosting . TrafficOutputOnlyFields | "name" > = {
90- rolloutPolicy : {
91- codebaseBranch : branch ,
92- stages : [
93- {
94- progression : "IMMEDIATE" ,
95- targetPercent : 100 ,
96- } ,
97- ] ,
98- } ,
99- } ;
100- const op = await apphosting . updateTraffic ( projectId , location , backendId , traffic ) ;
101- await poller . pollOperation < apphosting . Traffic > ( {
102- ...apphostingPollerOptions ,
103- pollerName : `updateTraffic-${ projectId } -${ location } -${ backendId } ` ,
104- operationResourceName : op . name ,
105- } ) ;
86+
87+ await setDefaultTrafficPolicy ( projectId , location , backendId , branch ) ;
10688
10789 const confirmRollout = await promptOnce ( {
10890 type : "confirm" ,
@@ -117,71 +99,41 @@ export async function doSetup(setup: any, projectId: string): Promise<void> {
11799 return ;
118100 }
119101
120- const { build } = await onboardRollout ( projectId , location , backendId , {
102+ await orchestrateRollout ( projectId , location , backendId , {
121103 source : {
122104 codebase : {
123105 branch,
124106 } ,
125107 } ,
126108 } ) ;
127109
128- if ( build . state !== "READY" ) {
129- if ( ! build . buildLogsUri ) {
130- throw new FirebaseError (
131- "Failed to build your app, but failed to get build logs as well. " +
132- "This is an internal error and should be reported" ,
133- ) ;
134- }
135- throw new FirebaseError (
136- `Failed to build your app. Please inspect the build logs at ${ build . buildLogsUri } .` ,
137- { children : [ build . error ] } ,
138- ) ;
139- }
140-
141110 logSuccess ( `Successfully created backend:\n\t${ backend . name } ` ) ;
142111 logSuccess ( `Your site is now deployed at:\n\thttps://${ backend . uri } ` ) ;
143- logSuccess (
144- `View the rollout status by running:\n\tfirebase apphosting:backends:get ${ backendId } --project ${ projectId } ` ,
145- ) ;
146- logSuccess (
147- `See your new App Hosting backend in the Firebase console at:\n\thttps://console.firebase.google.com/project/${ projectId } /apphosting` ,
148- ) ;
149- }
150-
151- async function promptLocation ( projectId : string , locations : string [ ] ) : Promise < string > {
152- return ( await promptOnce ( {
153- name : "region" ,
154- type : "list" ,
155- default : DEFAULT_REGION ,
156- message :
157- "Please select a region " +
158- `(${ clc . yellow ( "info" ) } : Your region determines where your backend is located):\n` ,
159- choices : locations . map ( ( loc ) => ( { value : loc } ) ) ,
160- } ) ) as string ;
161- }
162-
163- function toBackend ( cloudBuildConnRepo : Repository ) : DeepOmit < Backend , BackendOutputOnlyFields > {
164- return {
165- servingLocality : "GLOBAL_ACCESS" ,
166- codebase : {
167- repository : `${ cloudBuildConnRepo . name } ` ,
168- rootDirectory : "/" ,
169- } ,
170- labels : deploymentTool . labels ( ) ,
171- } ;
172112}
173113
174114/**
175- * Walkthrough the flow for creating a new backend.
115+ * Prompts the user for a backend id and verifies that it doesn't match a pre-existing backend.
176116 */
177- export async function onboardBackend (
117+ async function promptNewBackendId (
178118 projectId : string ,
179119 location : string ,
180- backendId : string ,
181- ) : Promise < Backend > {
182- const cloudBuildConnRepo = await repo . linkGitHubRepository ( projectId , location ) ;
183- const backendDetails = toBackend ( cloudBuildConnRepo ) ;
184- return await createBackend ( projectId , location , backendDetails , backendId ) ;
120+ prompt : any ,
121+ ) : Promise < string > {
122+ while ( true ) {
123+ const backendId = await promptOnce ( prompt ) ;
124+ try {
125+ await apphosting . getBackend ( projectId , location , backendId ) ;
126+ } catch ( err : any ) {
127+ if ( err . status === 404 ) {
128+ return backendId ;
129+ }
130+ throw new FirebaseError (
131+ `Failed to check if backend with id ${ backendId } already exists in ${ location } ` ,
132+ { original : err } ,
133+ ) ;
134+ }
135+ logWarning ( `Backend with id ${ backendId } already exists in ${ location } ` ) ;
136+ }
185137}
186138
187139/**
@@ -190,10 +142,19 @@ export async function onboardBackend(
190142export async function createBackend (
191143 projectId : string ,
192144 location : string ,
193- backendReqBoby : Omit < Backend , BackendOutputOnlyFields > ,
194145 backendId : string ,
146+ repository : Repository ,
195147) : Promise < Backend > {
196- const op = await apphosting . createBackend ( projectId , location , backendReqBoby , backendId ) ;
148+ const backendReqBody : Omit < Backend , BackendOutputOnlyFields > = {
149+ servingLocality : "GLOBAL_ACCESS" ,
150+ codebase : {
151+ repository : `${ repository . name } ` ,
152+ rootDirectory : "/" ,
153+ } ,
154+ labels : deploymentTool . labels ( ) ,
155+ } ;
156+
157+ const op = await apphosting . createBackend ( projectId , location , backendReqBody , backendId ) ;
197158 const backend = await poller . pollOperation < Backend > ( {
198159 ...apphostingPollerOptions ,
199160 pollerName : `create-${ projectId } -${ location } -${ backendId } ` ,
@@ -203,9 +164,37 @@ export async function createBackend(
203164}
204165
205166/**
206- * Onboard a new rollout by creating a build and rollout
167+ * Sets the default rollout policy to route 100% of traffic to the latest deploy.
168+ */
169+ export async function setDefaultTrafficPolicy (
170+ projectId : string ,
171+ location : string ,
172+ backendId : string ,
173+ codebaseBranch : string ,
174+ ) : Promise < void > {
175+ const traffic : DeepOmit < apphosting . Traffic , apphosting . TrafficOutputOnlyFields | "name" > = {
176+ rolloutPolicy : {
177+ codebaseBranch : codebaseBranch ,
178+ stages : [
179+ {
180+ progression : "IMMEDIATE" ,
181+ targetPercent : 100 ,
182+ } ,
183+ ] ,
184+ } ,
185+ } ;
186+ const op = await apphosting . updateTraffic ( projectId , location , backendId , traffic ) ;
187+ await poller . pollOperation < apphosting . Traffic > ( {
188+ ...apphostingPollerOptions ,
189+ pollerName : `updateTraffic-${ projectId } -${ location } -${ backendId } ` ,
190+ operationResourceName : op . name ,
191+ } ) ;
192+ }
193+
194+ /**
195+ * Creates a new build and rollout and polls both to completion.
207196 */
208- export async function onboardRollout (
197+ export async function orchestrateRollout (
209198 projectId : string ,
210199 location : string ,
211200 backendId : string ,
@@ -232,5 +221,18 @@ export async function onboardRollout(
232221
233222 const [ rollout , build ] = await Promise . all ( [ rolloutPoll , buildPoll ] ) ;
234223 logSuccess ( "Rollout completed." ) ;
224+
225+ if ( build . state !== "READY" ) {
226+ if ( ! build . buildLogsUri ) {
227+ throw new FirebaseError (
228+ "Failed to build your app, but failed to get build logs as well. " +
229+ "This is an internal error and should be reported" ,
230+ ) ;
231+ }
232+ throw new FirebaseError (
233+ `Failed to build your app. Please inspect the build logs at ${ build . buildLogsUri } .` ,
234+ { children : [ build . error ] } ,
235+ ) ;
236+ }
235237 return { rollout, build } ;
236238}
0 commit comments