Skip to content

Commit

Permalink
Add utility commands for app hosting. (#6616)
Browse files Browse the repository at this point in the history
We haven't finalized the design of these commands, but they sure are handy for local testing.
  • Loading branch information
taeold authored Dec 14, 2023
1 parent f35a365 commit f0206c2
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 17 deletions.
32 changes: 32 additions & 0 deletions src/commands/apphosting-builds-create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as apphosting from "../gcp/apphosting";
import { logger } from "../logger";
import { Command } from "../command";
import { Options } from "../options";
import { generateId } from "../utils";
import { needProjectId } from "../projectUtils";

export const command = new Command("apphosting:builds:create <backendId>")
.description("Create a build for an App Hosting backend")
.option("-l, --location <location>", "Specify the region of the backend", "us-central1")
.option("-i, --id <buildId>", "Id of the build. If not present, autogenerate a random id", "")
.option("-b, --branch <branch>", "Repository branch to deploy. Defaults to 'main'", "main")
.before(apphosting.ensureApiEnabled)
.action(async (backendId: string, options: Options) => {
const projectId = needProjectId(options);
const location = options.location as string;
const buildId = (options.buildId as string) || generateId();
const branch = options.branch as string;

const op = await apphosting.createBuild(projectId, location, backendId, buildId, {
source: {
codebase: {
branch: "main",
},
},
});

logger.info(`Started a build for backend ${backendId} on branch ${branch}.`);
logger.info("Check status by running:");
logger.info(`\tfirebase apphosting:builds:get ${backendId} ${buildId} --location ${location}`);
return op;
});
17 changes: 17 additions & 0 deletions src/commands/apphosting-builds-get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as apphosting from "../gcp/apphosting";
import { logger } from "../logger";
import { Command } from "../command";
import { Options } from "../options";
import { needProjectId } from "../projectUtils";

export const command = new Command("apphosting:builds:get <backendId> <buildId>")
.description("Create a build for an App Hosting backend")
.option("-l, --location <location>", "Specify the region of the backend", "us-central1")
.before(apphosting.ensureApiEnabled)
.action(async (backendId: string, buildId: string, options: Options) => {
const projectId = needProjectId(options);
const location = options.location as string;
const build = await apphosting.getBuild(projectId, location, backendId, buildId);
logger.info(JSON.stringify(build, null, 2));
return build;
});
25 changes: 25 additions & 0 deletions src/commands/apphosting-rollouts-create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as apphosting from "../gcp/apphosting";
import { logger } from "../logger";
import { Command } from "../command";
import { Options } from "../options";
import { needProjectId } from "../projectUtils";
import { generateId } from "../utils";

export const command = new Command("apphosting:rollouts:create <backendId> <buildId>")
.description("Create a build for an App Hosting backend")
.option("-l, --location <location>", "Specify the region of the backend", "us-central1")
.option("-i, --id <rolloutId>", "Id of the rollout. If not present, autogenerate a random id", "")
.before(apphosting.ensureApiEnabled)
.action(async (backendId: string, buildId: string, options: Options) => {
const projectId = needProjectId(options);
const location = options.location as string;
const rolloutId = (options.buildId as string) || generateId();
const build = `projects/${projectId}/backends/${backendId}/builds/${buildId}`;
const op = await apphosting.createRollout(projectId, location, backendId, rolloutId, {
build,
});
logger.info(`Started a rollout for backend ${backendId} with build ${buildId}.`);
logger.info("Check status by running:");
logger.info(`\tfirebase apphosting:rollouts:list --location ${location}`);
return op;
});
21 changes: 21 additions & 0 deletions src/commands/apphosting-rollouts-list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as apphosting from "../gcp/apphosting";
import { logger } from "../logger";
import { Command } from "../command";
import { Options } from "../options";
import { needProjectId } from "../projectUtils";

export const command = new Command("apphosting:rollouts:list <backendId>")
.description("List rollouts of an App Hosting backend")
.option(
"-l, --location <location>",
"Rgion of the rollouts. Defaults to listing rollouts from all regions",
"-"
)
.before(apphosting.ensureApiEnabled)
.action(async (backendId: string, options: Options) => {
const projectId = needProjectId(options);
const location = options.location as string;
const rollouts = await apphosting.listRollouts(projectId, location, backendId);
logger.info(JSON.stringify(rollouts, null, 2));
return rollouts;
});
6 changes: 6 additions & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@ export function load(client: any): any {
client.apphosting.backends.create = loadCommand("apphosting-backends-create");
client.apphosting.backends.get = loadCommand("apphosting-backends-get");
client.apphosting.backends.delete = loadCommand("apphosting-backends-delete");
client.apphosting.builds = {};
client.apphosting.builds.get = loadCommand("apphosting-builds-get");
client.apphosting.builds.create = loadCommand("apphosting-builds-create");
client.apphosting.rollouts = {};
client.apphosting.rollouts.create = loadCommand("apphosting-rollouts-create");
client.apphosting.rollouts.list = loadCommand("apphosting-rollouts-list");
}
client.login = loadCommand("login");
client.login.add = loadCommand("login-add");
Expand Down
32 changes: 30 additions & 2 deletions src/gcp/apphosting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,20 @@ export async function deleteBackend(
return res.body;
}

/**
* Get a Build by Id
*/
export async function getBuild(
projectId: string,
location: string,
backendId: string,
buildId: string
): Promise<Build> {
const name = `projects/${projectId}/locations/${location}/backends/${backendId}/builds/${buildId}`;
const res = await client.get<Build>(name);
return res.body;
}

/**
* Creates a new Build in a given project and location.
*/
Expand All @@ -322,7 +336,7 @@ export async function createBuild(
}

/**
* Create a new rollout for a backend
* Create a new rollout for a backend.
*/
export async function createRollout(
projectId: string,
Expand All @@ -340,7 +354,21 @@ export async function createRollout(
}

/**
* Update traffic of a backend
* List all rollouts for a backend.
*/
export async function listRollouts(
projectId: string,
location: string,
backendId: string
): Promise<Rollout[]> {
const res = await client.get<{ rollouts: Rollout[] }>(
`projects/${projectId}/locations/${location}/backends/${backendId}/rollouts`
);
return res.body.rollouts;
}

/**
* Update traffic of a backend.
*/
export async function updateTraffic(
projectId: string,
Expand Down
16 changes: 1 addition & 15 deletions src/init/features/apphosting/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as clc from "colorette";
import * as repo from "./repo";
import * as poller from "../../../operation-poller";
import * as apphosting from "../../../gcp/apphosting";
import { logBullet, logSuccess, logWarning } from "../../../utils";
import { generateId, logBullet, logSuccess, logWarning } from "../../../utils";
import { apphostingOrigin } from "../../../api";
import {
Backend,
Expand Down Expand Up @@ -205,17 +205,3 @@ export async function onboardRollout(
logSuccess("Rollout completed.");
return { rollout, build };
}

/**
* Only lowercase, digits, and hyphens; must begin with letter, and cannot end with hyphen
*/
function generateId(n = 6): string {
const letters = "abcdefghijklmnopqrstuvwxyz";
const allChars = "01234567890-abcdefghijklmnopqrstuvwxyz";
let id = letters[Math.floor(Math.random() * letters.length)];
for (let i = 1; i < n; i++) {
const idx = Math.floor(Math.random() * allChars.length);
id += allChars[idx];
}
return id;
}
17 changes: 17 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -810,3 +810,20 @@ export function getHostnameFromUrl(url: string): string | null {
return null;
}
}

/**
* Generate id meeting the following criterias:
* - Lowercase, digits, and hyphens only
* - Must begin with letter
* - Cannot end with hyphen
*/
export function generateId(n = 6): string {
const letters = "abcdefghijklmnopqrstuvwxyz";
const allChars = "01234567890-abcdefghijklmnopqrstuvwxyz";
let id = letters[Math.floor(Math.random() * letters.length)];
for (let i = 1; i < n; i++) {
const idx = Math.floor(Math.random() * allChars.length);
id += allChars[idx];
}
return id;
}

0 comments on commit f0206c2

Please sign in to comment.