|
1 | 1 | import * as clc from "colorette";
|
2 | 2 |
|
3 | 3 | import * as gcb from "../../../gcp/cloudbuild";
|
| 4 | +import * as rm from "../../../gcp/resourceManager"; |
4 | 5 | import * as poller from "../../../operation-poller";
|
5 | 6 | import * as utils from "../../../utils";
|
6 | 7 | import { cloudbuildOrigin } from "../../../api";
|
7 | 8 | import { FirebaseError } from "../../../error";
|
8 | 9 | import { logger } from "../../../logger";
|
9 | 10 | import { promptOnce } from "../../../prompt";
|
| 11 | +import { getProjectNumber } from "../../../getProjectNumber"; |
10 | 12 |
|
11 | 13 | export interface ConnectionNameParts {
|
12 | 14 | projectId: string;
|
@@ -81,6 +83,10 @@ export async function linkGitHubRepository(
|
81 | 83 | logger.info(clc.bold(`\n${clc.yellow("===")} Connect a GitHub repository`));
|
82 | 84 | const existingConns = await listFrameworksConnections(projectId);
|
83 | 85 | if (existingConns.length < 1) {
|
| 86 | + const grantSuccess = await promptSecretManagerAdminGrant(projectId); |
| 87 | + if (!grantSuccess) { |
| 88 | + throw new FirebaseError("Insufficient IAM permissions to create a new connection to GitHub"); |
| 89 | + } |
84 | 90 | let oauthConn = await getOrCreateConnection(projectId, location, FRAMEWORKS_OAUTH_CONN_NAME);
|
85 | 91 | while (oauthConn.installationState.stage === "PENDING_USER_OAUTH") {
|
86 | 92 | oauthConn = await promptConnectionAuth(oauthConn);
|
@@ -156,14 +162,40 @@ async function promptRepositoryUri(
|
156 | 162 | return { remoteUri, connection: remoteUriToConnection[remoteUri] };
|
157 | 163 | }
|
158 | 164 |
|
| 165 | +async function promptSecretManagerAdminGrant(projectId: string): Promise<Boolean> { |
| 166 | + const projectNumber = await getProjectNumber({ projectId }); |
| 167 | + const cbsaEmail = gcb.serviceAgentEmail(projectNumber); |
| 168 | + logger.info( |
| 169 | + "To create a new GitHub connection, Secret Manager Admin role (roles/secretmanager.admin) is required on the Cloud Build Service Agent." |
| 170 | + ); |
| 171 | + const grant = await promptOnce({ |
| 172 | + type: "confirm", |
| 173 | + message: "Grant the required role to the Cloud Build Service Agent?", |
| 174 | + }); |
| 175 | + if (!grant) { |
| 176 | + logger.info( |
| 177 | + "You, or your project administrator, should run the following command to grant the required role:\n\n" + |
| 178 | + `\tgcloud projects add-iam-policy-binding ${projectId} \\\n` + |
| 179 | + `\t --member="serviceAccount:${cbsaEmail} \\\n` + |
| 180 | + `\t --role="roles/secretmanager.admin\n` |
| 181 | + ); |
| 182 | + return false; |
| 183 | + } |
| 184 | + await rm.addServiceAccountToRoles(projectId, cbsaEmail, ["roles/secretmanager.admin"], true); |
| 185 | + logger.info("Successfully granted the required role to the Cloud Build Service Agent!"); |
| 186 | + return true; |
| 187 | +} |
| 188 | + |
159 | 189 | async function promptConnectionAuth(conn: gcb.Connection): Promise<gcb.Connection> {
|
160 | 190 | logger.info("You must authorize the Cloud Build GitHub app.");
|
161 | 191 | logger.info();
|
162 |
| - logger.info("First, sign in to GitHub and authorize Cloud Build GitHub app:"); |
163 |
| - const cleanup = await utils.openInBrowserPopup( |
| 192 | + logger.info("Sign in to GitHub and authorize Cloud Build GitHub app:"); |
| 193 | + const { url, cleanup } = await utils.openInBrowserPopup( |
164 | 194 | conn.installationState.actionUri,
|
165 | 195 | "Authorize the GitHub app"
|
166 | 196 | );
|
| 197 | + logger.info(`\t${url}`); |
| 198 | + logger.info(); |
167 | 199 | await promptOnce({
|
168 | 200 | type: "input",
|
169 | 201 | message: "Press Enter once you have authorized the app",
|
@@ -215,7 +247,7 @@ export async function getOrCreateConnection(
|
215 | 247 | try {
|
216 | 248 | conn = await gcb.getConnection(projectId, location, connectionId);
|
217 | 249 | } catch (err: unknown) {
|
218 |
| - if ((err as FirebaseError).status === 404) { |
| 250 | + if ((err as any).status === 404) { |
219 | 251 | conn = await createConnection(projectId, location, connectionId, githubConfig);
|
220 | 252 | } else {
|
221 | 253 | throw err;
|
|
0 commit comments