Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Init flow demo #6218

Merged
merged 39 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
4147110
testing
svnsairam Aug 4, 2023
1c025f1
testing
svnsairam Aug 4, 2023
4b3e805
Init demo flow
svnsairam Aug 4, 2023
2fac766
Create Stack request body changed
svnsairam Sep 1, 2023
cefe927
Merge remote-tracking branch 'origin/master' into svnsairam-fsk-001
svnsairam Sep 12, 2023
90af159
Merge branch 'svnsairam-fsk-001' of https://github.com/firebase/fireb…
svnsairam Sep 12, 2023
a540248
Merge remote-tracking branch 'origin/master' into svnsairam-fsk-001
svnsairam Sep 12, 2023
18bc06c
Merge remote-tracking branch 'origin/master' into svnsairam-fsk-001
svnsairam Sep 13, 2023
1609091
Updated CRUD API's for stacks
svnsairam Sep 14, 2023
7c8b3e3
Updated changes
svnsairam Sep 14, 2023
64337b1
updated comments
svnsairam Sep 15, 2023
a73bfd6
Update CRUD options
svnsairam Sep 19, 2023
cc028c7
Merge remote-tracking branch 'origin/master' into svnsairam-fsk-001
svnsairam Sep 19, 2023
a956766
Merge branch 'master' into svnsairam-fsk-001
svnsairam Oct 10, 2023
9e58710
resolved comments
svnsairam Oct 10, 2023
4e43d68
resolved comments
svnsairam Oct 10, 2023
285d148
resolved comments
svnsairam Oct 10, 2023
76e0c6d
Merge remote-tracking branch 'origin/master' into svnsairam-fsk-001
svnsairam Oct 10, 2023
af5db62
Changing stack to backend in APIs
svnsairam Oct 10, 2023
f3bdc35
Added changes to modify api
svnsairam Oct 10, 2023
c8c3ce9
minor change to region
svnsairam Oct 10, 2023
9781527
Added error messages
svnsairam Oct 10, 2023
0243f7f
Merge branch 'master' into svnsairam-fsk-001
svnsairam Oct 11, 2023
efe259b
Merge branch 'master' into svnsairam-fsk-001
svnsairam Oct 11, 2023
d44fb6d
catch block added
svnsairam Oct 12, 2023
5d48639
Merge branch 'svnsairam-fsk-001' of https://github.com/firebase/fireb…
svnsairam Oct 12, 2023
b30ee48
Added few changes
svnsairam Oct 16, 2023
333d8f8
Merge branch 'master' into svnsairam-fsk-001
svnsairam Oct 17, 2023
5d008dd
Merge branch 'master' into svnsairam-fsk-001
svnsairam Oct 20, 2023
56d8a12
Merge branch 'master' into svnsairam-fsk-001
svnsairam Oct 23, 2023
7b80e73
Returned stack
svnsairam Oct 25, 2023
fcf5708
Return stack
svnsairam Oct 25, 2023
d992188
Added return statements
svnsairam Oct 25, 2023
8f942aa
Removed comments
svnsairam Oct 25, 2023
8db5e15
changed comments
svnsairam Oct 25, 2023
fdeda7c
Added minor change
svnsairam Oct 27, 2023
b5e7cf9
Merge branch 'master' into svnsairam-fsk-001
svnsairam Oct 27, 2023
71d534c
Format code
svnsairam Oct 27, 2023
681a695
Format code
svnsairam Oct 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/commands/frameworks-stacks-create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Command } from "../command";
import { Options } from "../options";
import { needProjectId } from "../projectUtils";
import requireInteractive from "../requireInteractive";
import { doSetup } from "../init/features/frameworks";

export const command = new Command("stacks:create")
.description("Create a stack in a Firebase project")
.before(requireInteractive)
.action(async (options: Options) => {
const projectId = needProjectId(options);
await doSetup(options, projectId);
});
42 changes: 42 additions & 0 deletions src/commands/frameworks-stacks-delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Command } from "../command";
import { Options } from "../options";
import { needProjectId } from "../projectUtils";
import { FirebaseError } from "../error";
import * as gcp from "../gcp/frameworks";
import { promptOnce } from "../prompt";
import * as utils from "../utils";

export const command = new Command("stacks:delete")
taeold marked this conversation as resolved.
Show resolved Hide resolved
.description("Delete a stack from a Firebase project")
.option("-l, --location <location>", "Stack backend location", "us-central1")
.option("-s, --stackId <stackId>", "Stack backend location", "")
.withForce()
.action(async (options: Options) => {
const projectId = needProjectId(options);
const location = options.location as string;
const stackId = options.stackId as string;
svnsairam marked this conversation as resolved.
Show resolved Hide resolved
if (!stackId) {
throw new FirebaseError("Stack id can't be empty.");
}
const confirmDeletion = await promptOnce(
{
type: "confirm",
name: "force",
default: false,
message: "You are about to delete the Stack with id: " + stackId + "\n Are you sure?",
},
options
);
if (!confirmDeletion) {
throw new FirebaseError("Deletion aborted.");
}

try {
await gcp.deleteStack(projectId, location, stackId);
utils.logSuccess(`Successfully deleted the stack: ${stackId}`);
} catch (err) {
throw new FirebaseError(
`Failed to delete stack: ${stackId}. Please check the parameters you have provided.`
svnsairam marked this conversation as resolved.
Show resolved Hide resolved
);
}
});
28 changes: 28 additions & 0 deletions src/commands/frameworks-stacks-get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Command } from "../command";
import { Options } from "../options";
import { needProjectId } from "../projectUtils";
import * as gcp from "../gcp/frameworks";
import { FirebaseError } from "../error";
import { logger } from "../logger";

export const command = new Command("stacks:get")
.description("Get stack details of a Firebase project")
.option("-l, --location <location>", "Stack backend location", "us-central1")
.option("--s, --stackId <stackId>", "Id for the stack", "")
.action(async (options: Options) => {
const projectId = needProjectId(options);
const location = options.location as string;
const stackId = options.stackId as string;
svnsairam marked this conversation as resolved.
Show resolved Hide resolved
if (!stackId) {
throw new FirebaseError("Stack id can't be empty.");
}

try {
const stack = await gcp.getStack(projectId, location, stackId);
logger.info(stack);
taeold marked this conversation as resolved.
Show resolved Hide resolved
} catch (err) {
throw new FirebaseError(
`Failed to get stack: ${stackId}. Please check the parameters you have provided.`
svnsairam marked this conversation as resolved.
Show resolved Hide resolved
);
}
taeold marked this conversation as resolved.
Show resolved Hide resolved
});
23 changes: 23 additions & 0 deletions src/commands/frameworks-stacks-list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Command } from "../command";
import { Options } from "../options";
import { needProjectId } from "../projectUtils";
import * as gcp from "../gcp/frameworks";
import { FirebaseError } from "../error";
import { logger } from "../logger";

export const command = new Command("stacks:list")
.description("List stacks of a Firebase project.")
.option("-l, --location <location>", "Stack backend location", "us-central1")
.action(async (options: Options) => {
const projectId = needProjectId(options);
const location = options.location as string;

try {
const stacks = await gcp.listStack(projectId, location);
logger.info(stacks);
svnsairam marked this conversation as resolved.
Show resolved Hide resolved
} catch (err) {
throw new FirebaseError(
`Unable to list stacks present in project: ${projectId}. Please check the parameters you have provided.`
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment - please return the stacks response

});
8 changes: 8 additions & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
/**
* Loads all commands for our parser.
*/
export function load(client: any): any {

Check warning on line 6 in src/commands/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Unexpected any. Specify a different type

Check warning on line 6 in src/commands/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Unexpected any. Specify a different type
function loadCommand(name: string) {

Check warning on line 7 in src/commands/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Missing return type on function
const t0 = process.hrtime.bigint();
const { command: cmd } = require(`./${name}`);

Check warning on line 9 in src/commands/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Unsafe assignment of an `any` value

Check warning on line 9 in src/commands/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Require statement not part of import statement
cmd.register(client);

Check warning on line 10 in src/commands/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Unsafe member access .register on an `any` value

Check warning on line 10 in src/commands/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Unsafe call of an `any` typed value
const t1 = process.hrtime.bigint();
const diffMS = (t1 - t0) / BigInt(1e6);
if (diffMS > 75) {
Expand All @@ -15,7 +15,7 @@
// console.error(`Loading ${name} took ${diffMS}ms`);
}

return cmd.runner();

Check warning on line 18 in src/commands/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Unsafe return of an `any` typed value

Check warning on line 18 in src/commands/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Unsafe member access .runner on an `any` value

Check warning on line 18 in src/commands/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Unsafe call of an `any` typed value
}

const t0 = process.hrtime.bigint();
Expand Down Expand Up @@ -151,6 +151,14 @@
client.internaltesting.functions = {};
client.internaltesting.functions.discover = loadCommand("internaltesting-functions-discover");
}
if (experiments.isEnabled("internalframeworks")) {
client.frameworks = {};
client.frameworks.stacks = {};
client.frameworks.stacks.list = loadCommand("frameworks-stacks-list");
client.frameworks.stacks.create = loadCommand("frameworks-stacks-create");
client.frameworks.stacks.create = loadCommand("frameworks-stacks-get");
client.frameworks.stacks.create = loadCommand("frameworks-stacks-delete");
}
client.login = loadCommand("login");
client.login.add = loadCommand("login-add");
client.login.ci = loadCommand("login-ci");
Expand Down
44 changes: 36 additions & 8 deletions src/gcp/frameworks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export interface Stack {
uri: string;
}

export type StackOutputOnlyFields = "createTime" | "updateTime" | "uri" | "codebase";
export type StackOutputOnlyFields = "name" | "createTime" | "updateTime" | "uri";

export interface Build {
name: string;
Expand Down Expand Up @@ -81,19 +81,23 @@ export interface Operation {
// end oneof result
}

export interface ListStacksResponse {
stacks: Stack[];
}

/**
* Creates a new Stack in a given project and location.
*/
export async function createStack(
projectId: string,
location: string,
stackInput: Omit<Stack, StackOutputOnlyFields>
stackReqBoby: Omit<Stack, StackOutputOnlyFields>,
backendId: string
): Promise<Operation> {
const stackId = stackInput.name;
const res = await client.post<Omit<Stack, StackOutputOnlyFields>, Operation>(
`projects/${projectId}/locations/${location}/stacks`,
stackInput,
{ queryParams: { stackId } }
`projects/${projectId}/locations/${location}/backends`,
stackReqBoby,
{ queryParams: { backendId } }
);

return res.body;
Expand All @@ -107,12 +111,36 @@ export async function getStack(
location: string,
stackId: string
): Promise<Stack> {
const name = `projects/${projectId}/locations/${location}/stacks/${stackId}`;
const name = `projects/${projectId}/locations/${location}/backends/${stackId}`;
const res = await client.get<Stack>(name);

return res.body;
}

/**
* List all stacks present in a project and region.
*/
export async function listStack(projectId: string, location: string): Promise<ListStacksResponse> {
const name = `projects/${projectId}/locations/${location}/backends/`;
svnsairam marked this conversation as resolved.
Show resolved Hide resolved
const res = await client.get<ListStacksResponse>(name);

return res.body;
}

/**
* Deletes a Stack with stackId in a given project and location.
*/
export async function deleteStack(
projectId: string,
location: string,
stackId: string
): Promise<Operation> {
const name = `projects/${projectId}/locations/${location}/backends/${stackId}`;
const res = await client.delete<Operation>(name);

return res.body;
}

/**
* Creates a new Build in a given project and location.
*/
Expand All @@ -124,7 +152,7 @@ export async function createBuild(
): Promise<Operation> {
const buildId = buildInput.name;
const res = await client.post<Omit<Build, BuildOutputOnlyFields>, Operation>(
`projects/${projectId}/locations/${location}/stacks/${stackId}/builds`,
`projects/${projectId}/locations/${location}/backends/${stackId}/builds`,
buildInput,
{ queryParams: { buildId } }
);
Expand Down
2 changes: 1 addition & 1 deletion src/init/features/frameworks/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const DEFAULT_REGION = "us-central1";
export const ALLOWED_REGIONS = [{ name: "us-central1 (Iowa)", value: "us-central1" }];
export const ALLOWED_REGIONS = [{ name: "us-central1", value: "us-central1" }];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: why did we get rid of Iowa in the name?

export const DEFAULT_DEPLOY_METHOD = "github";
export const ALLOWED_DEPLOY_METHODS = [{ name: "Deploy using github", value: "github" }];
35 changes: 17 additions & 18 deletions src/init/features/frameworks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ const frameworksPollerOptions: Omit<poller.OperationPollerOptions, "operationRes
/**
* Setup new frameworks project.
*/
export async function doSetup(setup: any): Promise<void> {
const projectId: string = setup?.rcfile?.projects?.default;
export async function doSetup(setup: any, projectId: string): Promise<void> {
setup.frameworks = {};

utils.logBullet("First we need a few details to create your service.");
Expand Down Expand Up @@ -71,15 +70,18 @@ export async function doSetup(setup: any): Promise<void> {
setup.frameworks
);

await getOrCreateStack(projectId, setup);
const stack: Stack | undefined = await getOrCreateStack(projectId, setup);
taeold marked this conversation as resolved.
Show resolved Hide resolved
if (stack) {
utils.logSuccess(`Successfully created a stack: ${stack.name}`);
}
}

function toStack(
cloudBuildConnRepo: Repository,
stackId: string
): Omit<Stack, StackOutputOnlyFields> {
function toStack(cloudBuildConnRepo: Repository): Omit<Stack, StackOutputOnlyFields> {
return {
name: stackId,
codebase: {
repository: `${cloudBuildConnRepo.name}`,
rootDirectory: "/",
},
labels: {},
};
}
Expand All @@ -96,13 +98,9 @@ export async function getOrCreateStack(projectId: string, setup: any): Promise<S
if ((err as FirebaseError).status === 404) {
logger.info("Creating new stack.");
if (deployMethod === "github") {
const cloudBuildConnRepo = await repo.linkGitHubRepository(
projectId,
location,
setup.frameworks.serviceName
);
const stackDetails = toStack(cloudBuildConnRepo, setup.frameworks.serviceName);
return await createStack(projectId, location, stackDetails);
const cloudBuildConnRepo = await repo.linkGitHubRepository(projectId, location);
const stackDetails = toStack(cloudBuildConnRepo);
return await createStack(projectId, location, stackDetails, setup.frameworks.serviceName);
}
} else {
throw new FirebaseError(
Expand Down Expand Up @@ -154,12 +152,13 @@ async function getExistingStack(projectId: string, setup: any, location: string)
export async function createStack(
projectId: string,
location: string,
stackInput: Omit<Stack, StackOutputOnlyFields>
stackReqBoby: Omit<Stack, StackOutputOnlyFields>,
stackId: string
): Promise<Stack> {
const op = await gcp.createStack(projectId, location, stackInput);
const op = await gcp.createStack(projectId, location, stackReqBoby, stackId);
const stack = await poller.pollOperation<Stack>({
...frameworksPollerOptions,
pollerName: `create-${projectId}-${location}-${stackInput.name}`,
pollerName: `create-${projectId}-${location}-${stackId}`,
operationResourceName: op.name,
});

Expand Down
27 changes: 17 additions & 10 deletions src/init/features/frameworks/repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,31 @@ function extractRepoSlugFromURI(remoteUri: string): string | undefined {

/**
* Generates a repository ID.
* N.B. The deterministic nature of the repository ID implies that each
* Cloud Build Connection will have one Cloud Build Repo child resource.
* The current implementation is subject to change in the event that
* the 1:1 Connection-to-Resource relationship no longer holds.
* The relation is 1:* between Cloud Build Connection and Github Repositories.
*/
function generateRepositoryId(): string | undefined {
return `composer-repo`;
function generateRepositoryId(remoteUri: string): string | undefined {
return extractRepoSlugFromURI(remoteUri)?.replaceAll("/", "-");
}

/**
* The 'frameworks-' is prefixed, to seperate the Cloud Build connections created from
* Frameworks platforms with rest of manually created Cloud Build connections.
*
* The reason suffix 'location' is because of
* 1:1 relation between location and Cloud Build connection.
*/
function generateConnectionId(location: string): string {
return `frameworks-${location}`;
}

/**
* Prompts the user to link their stack to a GitHub repository.
*/
export async function linkGitHubRepository(
projectId: string,
location: string,
stackId: string
location: string
): Promise<gcb.Repository> {
const connectionId = stackId;
const connectionId = generateConnectionId(location);
await getOrCreateConnection(projectId, location, connectionId);

let remoteUri = await promptRepositoryURI(projectId, location, connectionId);
Expand Down Expand Up @@ -147,7 +154,7 @@ export async function getOrCreateRepository(
connectionId: string,
remoteUri: string
): Promise<gcb.Repository> {
const repositoryId = generateRepositoryId();
const repositoryId = generateRepositoryId(remoteUri);
if (!repositoryId) {
throw new FirebaseError(`Failed to generate repositoryId for URI "${remoteUri}".`);
}
Expand Down
18 changes: 9 additions & 9 deletions src/test/init/frameworks/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@ describe("operationsConverter", () => {
const projectId = "projectId";
const location = "us-central1";
const stackId = "stackId";
const stackInput = {
name: stackId,
labels: {},
};
const op = {
name: `projects/${projectId}/locations/${location}/stacks/${stackId}`,
done: true,
Expand All @@ -58,17 +54,23 @@ describe("operationsConverter", () => {
},
};
const cloudBuildConnRepo = {
name: `projects/${projectId}/locations/${location}/stacks/${stackId}`,
name: `projects/${projectId}/locations/${location}/connections/framework-${location}/repositories/repoId`,
remoteUri: "remoteUri",
createTime: "0",
updateTime: "1",
};

const stackInput = {
codebase: {
repository: cloudBuildConnRepo.name,
rootDirectory: "/",
},
labels: {},
};
it("should createStack", async () => {
createStackStub.resolves(op);
pollOperationStub.resolves(completeStack);

await createStack(projectId, location, stackInput);
await createStack(projectId, location, stackInput, stackId);

expect(createStackStub).to.be.calledWith(projectId, location, stackInput);
});
Expand All @@ -86,10 +88,8 @@ describe("operationsConverter", () => {
const newStackId = "newStackId";
const newPath = `projects/${projectId}/locations/${location}/stacks/${newStackId}`;
setup.frameworks.serviceName = newStackId;
stackInput.name = newStackId;
op.name = newPath;
completeStack.name = newPath;
cloudBuildConnRepo.name = newPath;
getStackStub.throws(new FirebaseError("error", { status: 404 }));
linkGitHubRepositoryStub.resolves(cloudBuildConnRepo);
createStackStub.resolves(op);
Expand Down