Skip to content

Commit 837b170

Browse files
committed
Add utility commands for app hosting.
1 parent 02078a3 commit 837b170

File tree

6 files changed

+151
-10
lines changed

6 files changed

+151
-10
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import * as apphosting from "../gcp/apphosting";
2+
import { logger } from "../logger";
3+
import { Command } from "../command";
4+
import { Options } from "../options";
5+
import { needProjectId } from "../projectUtils";
6+
7+
function generateId(n = 6): string {
8+
const letters = "abcdefghijklmnopqrstuvwxyz";
9+
const allChars = "01234567890-abcdefghijklmnopqrstuvwxyz";
10+
let id = letters[Math.floor(Math.random() * letters.length)];
11+
for (let i = 1; i < n; i++) {
12+
const idx = Math.floor(Math.random() * allChars.length);
13+
id += allChars[idx];
14+
}
15+
return id;
16+
}
17+
18+
export const command = new Command("apphosting:builds:create <backendId>")
19+
.description("Create a build for an App Hosting backend")
20+
.option("-l, --location <location>", "Specify the region of the backend", "us-central1")
21+
.option("-i, --id <buildId>", "Id of the build. If not present, autogenerate a random id", "")
22+
.option("-b, --branch <branch>", "Repository branch to deploy. Defaults to 'main'", "main")
23+
.before(apphosting.ensureApiEnabled)
24+
.action(async (backendId: string, options: Options) => {
25+
const projectId = needProjectId(options);
26+
const location = options.location as string;
27+
const buildId = (options.buildId as string) || generateId();
28+
const branch = options.branch as string;
29+
30+
const op = await apphosting.createBuild(projectId, location, backendId, buildId, {
31+
source: {
32+
codebase: {
33+
branch: "main",
34+
},
35+
},
36+
});
37+
38+
logger.info(`Started a build for backend ${backendId} on branch ${branch}.`);
39+
logger.info("Check status by running:");
40+
logger.info(`\tfirebase apphosting:builds:get ${backendId} ${buildId} --location ${location}`);
41+
return op;
42+
});

src/commands/apphosting-builds-get.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import * as apphosting from "../gcp/apphosting";
2+
import { logger } from "../logger";
3+
import { Command } from "../command";
4+
import { Options } from "../options";
5+
import { needProjectId } from "../projectUtils";
6+
7+
export const command = new Command("apphosting:builds:get <backendId> <buildId>")
8+
.description("Create a build for an App Hosting backend")
9+
.option("-l, --location <location>", "Specify the region of the backend", "us-central1")
10+
.before(apphosting.ensureApiEnabled)
11+
.action(async (backendId: string, buildId: string, options: Options) => {
12+
const projectId = needProjectId(options);
13+
const location = options.location as string;
14+
const build = await apphosting.getBuild(projectId, location, backendId, buildId);
15+
logger.info(JSON.stringify(build, null, 2));
16+
return build;
17+
});
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import * as apphosting from "../gcp/apphosting";
2+
import { logger } from "../logger";
3+
import { Command } from "../command";
4+
import { Options } from "../options";
5+
import { needProjectId } from "../projectUtils";
6+
7+
function generateId(n = 6): string {
8+
const letters = "abcdefghijklmnopqrstuvwxyz";
9+
const allChars = "01234567890-abcdefghijklmnopqrstuvwxyz";
10+
let id = letters[Math.floor(Math.random() * letters.length)];
11+
for (let i = 1; i < n; i++) {
12+
const idx = Math.floor(Math.random() * allChars.length);
13+
id += allChars[idx];
14+
}
15+
return id;
16+
}
17+
18+
export const command = new Command("apphosting:rollouts:create <backendId> <buildId>")
19+
.description("Create a build for an App Hosting backend")
20+
.option("-l, --location <location>", "Specify the region of the backend", "us-central1")
21+
.option("-i, --id <rolloutId>", "Id of the rollout. If not present, autogenerate a random id", "")
22+
.before(apphosting.ensureApiEnabled)
23+
.action(async (backendId: string, buildId: string, options: Options) => {
24+
const projectId = needProjectId(options);
25+
const location = options.location as string;
26+
const rolloutId = (options.buildId as string) || generateId();
27+
const build = `projects/${projectId}/backends/${backendId}/builds/${buildId}`;
28+
const op = await apphosting.createRollout(projectId, location, backendId, rolloutId, {
29+
build,
30+
});
31+
logger.info(`Started a rollout for backend ${backendId} with build ${buildId}.`);
32+
logger.info("Check status by running:");
33+
logger.info(`\tfirebase apphosting:rollouts:list --location ${location}`);
34+
return op;
35+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import * as apphosting from "../gcp/apphosting";
2+
import { logger } from "../logger";
3+
import { Command } from "../command";
4+
import { Options } from "../options";
5+
import { needProjectId } from "../projectUtils";
6+
7+
export const command = new Command("apphosting:rollouts:list <backendId>")
8+
.description("List rollouts of an App Hosting backend")
9+
.option(
10+
"-l, --location <location>",
11+
"Rgion of the rollouts. Defaults to listing rollouts from all regions",
12+
"-"
13+
)
14+
.before(apphosting.ensureApiEnabled)
15+
.action(async (backendId: string, options: Options) => {
16+
const projectId = needProjectId(options);
17+
const location = options.location as string;
18+
const rollouts = await apphosting.listRollouts(projectId, location, backendId);
19+
logger.info(JSON.stringify(rollouts, null, 2));
20+
return rollouts;
21+
});

src/commands/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,12 @@ export function load(client: any): any {
157157
client.apphosting.backends.create = loadCommand("apphosting-backends-create");
158158
client.apphosting.backends.get = loadCommand("apphosting-backends-get");
159159
client.apphosting.backends.delete = loadCommand("apphosting-backends-delete");
160+
client.apphosting.builds = {};
161+
client.apphosting.builds.get = loadCommand("apphosting-builds-get");
162+
client.apphosting.builds.create = loadCommand("apphosting-builds-create");
163+
client.apphosting.rollouts = {};
164+
client.apphosting.rollouts.create = loadCommand("apphosting-rollouts-create");
165+
client.apphosting.rollouts.list = loadCommand("apphosting-rollouts-list");
160166
}
161167
client.login = loadCommand("login");
162168
client.login.add = loadCommand("login-add");

src/gcp/apphosting.ts

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,20 @@ export async function deleteBackend(
303303
return res.body;
304304
}
305305

306+
/**
307+
* Get a Build by Id
308+
*/
309+
export async function getBuild(
310+
projectId: string,
311+
location: string,
312+
backendId: string,
313+
buildId: string
314+
): Promise<Build> {
315+
const name = `projects/${projectId}/locations/${location}/backends/${backendId}/builds/${buildId}`;
316+
const res = await client.get<Build>(name);
317+
return res.body;
318+
}
319+
306320
/**
307321
* Creates a new Build in a given project and location.
308322
*/
@@ -322,7 +336,7 @@ export async function createBuild(
322336
}
323337

324338
/**
325-
* Create a new rollout for a backend
339+
* Create a new rollout for a backend.
326340
*/
327341
export async function createRollout(
328342
projectId: string,
@@ -340,7 +354,21 @@ export async function createRollout(
340354
}
341355

342356
/**
343-
* Update traffic of a backend
357+
* List all rollouts for a backend.
358+
*/
359+
export async function listRollouts(
360+
projectId: string,
361+
location: string,
362+
backendId: string
363+
): Promise<Rollout[]> {
364+
const res = await client.get<{ rollouts: Rollout[] }>(
365+
`projects/${projectId}/locations/${location}/backends/${backendId}/rollouts`
366+
);
367+
return res.body.rollouts;
368+
}
369+
370+
/**
371+
* Update traffic of a backend.
344372
*/
345373
export async function updateTraffic(
346374
projectId: string,
@@ -388,11 +416,3 @@ export async function listLocations(projectId: string): Promise<Location[]> {
388416
} while (pageToken);
389417
return locations;
390418
}
391-
392-
/**
393-
* Ensure that Frameworks API is enabled on the project.
394-
*/
395-
export async function ensureApiEnabled(options: any): Promise<void> {
396-
const projectId = needProjectId(options);
397-
return await ensure(projectId, API_HOST, "frameworks", true);
398-
}

0 commit comments

Comments
 (0)