firebase-tools 12.3.1 → 12.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/frameworks.js +21 -0
- package/lib/api.js +4 -3
- package/lib/appdistribution/client.js +52 -0
- package/lib/auth.js +3 -3
- package/lib/command.js +15 -4
- package/lib/commands/appdistribution-group-create.js +19 -0
- package/lib/commands/appdistribution-group-delete.js +24 -0
- package/lib/commands/appdistribution-testers-add.js +6 -1
- package/lib/commands/appdistribution-testers-remove.js +20 -13
- package/lib/commands/experiments-describe.js +1 -1
- package/lib/commands/ext-install.js +10 -4
- package/lib/commands/index.js +5 -0
- package/lib/commands/init.js +8 -0
- package/lib/commands/internaltesting-frameworks-init.js +14 -0
- package/lib/config.js +1 -0
- package/lib/deploy/extensions/prepare.js +1 -0
- package/lib/deploy/extensions/release.js +11 -1
- package/lib/deploy/functions/checkIam.js +4 -1
- package/lib/deploy/functions/prepare.js +2 -1
- package/lib/deploy/functions/runtimes/discovery/index.js +6 -0
- package/lib/deploy/functions/runtimes/node/index.js +12 -4
- package/lib/deploy/functions/runtimes/python/index.js +19 -25
- package/lib/deploy/hosting/deploy.js +0 -6
- package/lib/deploy/hosting/prepare.js +7 -1
- package/lib/deploy/index.js +11 -4
- package/lib/deploy/lifecycleHooks.js +3 -0
- package/lib/deploy/storage/prepare.js +1 -1
- package/lib/detectProjectRoot.js +4 -1
- package/lib/dynamicImport.js +11 -1
- package/lib/emulator/commandUtils.js +4 -4
- package/lib/emulator/controller.js +9 -7
- package/lib/emulator/downloadableEmulators.js +3 -3
- package/lib/emulator/functionsEmulator.js +1 -2
- package/lib/emulator/storage/index.js +6 -0
- package/lib/emulator/storage/rules/manager.js +0 -4
- package/lib/emulator/storage/server.js +52 -0
- package/lib/ensureApiEnabled.js +3 -1
- package/lib/experiments.js +5 -0
- package/lib/extensions/paramHelper.js +0 -5
- package/lib/frameworks/compose/discover/filesystem.js +52 -0
- package/lib/frameworks/compose/discover/frameworkMatcher.js +76 -0
- package/lib/frameworks/compose/discover/frameworkSpec.js +39 -0
- package/lib/frameworks/compose/discover/types.js +2 -0
- package/lib/frameworks/constants.js +2 -15
- package/lib/frameworks/index.js +13 -8
- package/lib/frameworks/utils.js +50 -20
- package/lib/functionsConfig.js +2 -2
- package/lib/gcp/cloudbuild.js +50 -0
- package/lib/gcp/storage.js +6 -5
- package/lib/init/features/composer/repo.js +121 -0
- package/lib/init/features/frameworks/constants.js +7 -0
- package/lib/init/features/frameworks/index.js +36 -0
- package/lib/init/features/index.js +3 -1
- package/lib/init/index.js +4 -0
- package/lib/management/projects.js +5 -1
- package/lib/monospace/index.js +82 -0
- package/lib/monospace/interfaces.js +2 -0
- package/lib/requireAuth.js +8 -0
- package/lib/track.js +91 -52
- package/lib/utils.js +6 -1
- package/package.json +1 -1
- package/schema/extension-yaml.json +432 -0
package/lib/frameworks/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.prepareFrameworks = exports.discover = exports.WebFrameworks = void 0;
|
|
3
|
+
exports.prepareFrameworks = exports.generateSSRCodebaseId = exports.discover = exports.WebFrameworks = void 0;
|
|
4
4
|
const path_1 = require("path");
|
|
5
5
|
const process_1 = require("process");
|
|
6
6
|
const child_process_1 = require("child_process");
|
|
@@ -25,21 +25,22 @@ const experiments = require("../experiments");
|
|
|
25
25
|
const implicitInit_1 = require("../hosting/implicitInit");
|
|
26
26
|
const utils_1 = require("./utils");
|
|
27
27
|
const constants_2 = require("./constants");
|
|
28
|
-
Object.defineProperty(exports, "WebFrameworks", { enumerable: true, get: function () { return constants_2.WebFrameworks; } });
|
|
29
28
|
const utils_2 = require("../utils");
|
|
30
29
|
const ensureTargeted_1 = require("../functions/ensureTargeted");
|
|
31
30
|
const util_1 = require("util");
|
|
32
31
|
const projectPath_1 = require("../projectPath");
|
|
33
32
|
const logger_1 = require("../logger");
|
|
33
|
+
const frameworks_1 = require("./frameworks");
|
|
34
|
+
Object.defineProperty(exports, "WebFrameworks", { enumerable: true, get: function () { return frameworks_1.WebFrameworks; } });
|
|
34
35
|
async function discover(dir, warn = true) {
|
|
35
36
|
const allFrameworkTypes = [
|
|
36
|
-
...new Set(Object.values(
|
|
37
|
+
...new Set(Object.values(frameworks_1.WebFrameworks).map(({ type }) => type)),
|
|
37
38
|
].sort();
|
|
38
39
|
for (const discoveryType of allFrameworkTypes) {
|
|
39
40
|
const frameworksDiscovered = [];
|
|
40
|
-
for (const framework in
|
|
41
|
-
if (
|
|
42
|
-
const { discover, type } =
|
|
41
|
+
for (const framework in frameworks_1.WebFrameworks) {
|
|
42
|
+
if (frameworks_1.WebFrameworks[framework]) {
|
|
43
|
+
const { discover, type } = frameworks_1.WebFrameworks[framework];
|
|
43
44
|
if (type !== discoveryType)
|
|
44
45
|
continue;
|
|
45
46
|
const result = await discover(dir);
|
|
@@ -72,6 +73,10 @@ function memoizeBuild(dir, build, deps, target) {
|
|
|
72
73
|
BUILD_MEMO.set(key, value);
|
|
73
74
|
return value;
|
|
74
75
|
}
|
|
76
|
+
function generateSSRCodebaseId(site) {
|
|
77
|
+
return `firebase-frameworks-${site}`;
|
|
78
|
+
}
|
|
79
|
+
exports.generateSSRCodebaseId = generateSSRCodebaseId;
|
|
75
80
|
async function prepareFrameworks(purpose, targetNames, context, options, emulators = []) {
|
|
76
81
|
var _a, _b, _c, _d, _e, _f;
|
|
77
82
|
var _g, _h, _j, _k, _l;
|
|
@@ -185,7 +190,7 @@ async function prepareFrameworks(purpose, targetNames, context, options, emulato
|
|
|
185
190
|
throw new error_1.FirebaseError((0, utils_1.frameworksCallToAction)("Unable to detect the web framework in use, check firebase-debug.log for more info."));
|
|
186
191
|
}
|
|
187
192
|
const { framework, mayWantBackend, publicDirectory } = results;
|
|
188
|
-
const { build, ɵcodegenPublicDirectory, ɵcodegenFunctionsDirectory: codegenProdModeFunctionsDirectory, getDevModeHandle, name, support, docsUrl, getValidBuildTargets = constants_2.GET_DEFAULT_BUILD_TARGETS, shouldUseDevModeHandle = constants_2.DEFAULT_SHOULD_USE_DEV_MODE_HANDLE, } =
|
|
193
|
+
const { build, ɵcodegenPublicDirectory, ɵcodegenFunctionsDirectory: codegenProdModeFunctionsDirectory, getDevModeHandle, name, support, docsUrl, getValidBuildTargets = constants_2.GET_DEFAULT_BUILD_TARGETS, shouldUseDevModeHandle = constants_2.DEFAULT_SHOULD_USE_DEV_MODE_HANDLE, } = frameworks_1.WebFrameworks[framework];
|
|
189
194
|
logger_1.logger.info(`\n${(0, utils_1.frameworksCallToAction)(constants_2.SupportLevelWarnings[support](name), docsUrl, " ")}\n`);
|
|
190
195
|
const hostingEmulatorInfo = emulators.find((e) => e.name === types_1.Emulators.HOSTING);
|
|
191
196
|
const validBuildTargets = await getValidBuildTargets(purpose, getProjectPath());
|
|
@@ -242,7 +247,7 @@ async function prepareFrameworks(purpose, targetNames, context, options, emulato
|
|
|
242
247
|
if (context === null || context === void 0 ? void 0 : context.hostingChannel) {
|
|
243
248
|
experiments.assertEnabled("pintags", "deploy an app that requires a backend to a preview channel");
|
|
244
249
|
}
|
|
245
|
-
const codebase =
|
|
250
|
+
const codebase = generateSSRCodebaseId(site);
|
|
246
251
|
const existingFunctionsConfig = options.config.get("functions")
|
|
247
252
|
? [].concat(options.config.get("functions"))
|
|
248
253
|
: [];
|
package/lib/frameworks/utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getFrameworksBuildTarget = exports.validateLocales = exports.frameworksCallToAction = exports.conjoinOptions = exports.relativeRequire = exports.findDependency = exports.getNodeModuleBin = exports.getNpmRoot = exports.simpleProxy = exports.warnIfCustomBuildScript = exports.readJSON = exports.isUrl = void 0;
|
|
3
|
+
exports.getFrameworksBuildTarget = exports.validateLocales = exports.frameworksCallToAction = exports.conjoinOptions = exports.relativeRequire = exports.findDependency = exports.getNodeModuleBin = exports.getNpmRoot = exports.simpleProxy = exports.proxyResponse = exports.warnIfCustomBuildScript = exports.readJSON = exports.isUrl = void 0;
|
|
4
4
|
const fs_extra_1 = require("fs-extra");
|
|
5
5
|
const path_1 = require("path");
|
|
6
6
|
const promises_1 = require("fs/promises");
|
|
@@ -32,21 +32,51 @@ async function warnIfCustomBuildScript(dir, framework, defaultBuildScripts) {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
exports.warnIfCustomBuildScript = warnIfCustomBuildScript;
|
|
35
|
-
function proxyResponse(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
35
|
+
function proxyResponse(req, res, next) {
|
|
36
|
+
const proxiedRes = new http_1.ServerResponse(req);
|
|
37
|
+
const buffer = [];
|
|
38
|
+
proxiedRes.write = new Proxy(proxiedRes.write.bind(proxiedRes), {
|
|
39
|
+
apply: (target, thisArg, args) => {
|
|
40
|
+
target.call(thisArg, ...args);
|
|
41
|
+
buffer.push(["write", args]);
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
proxiedRes.setHeader = new Proxy(proxiedRes.setHeader.bind(proxiedRes), {
|
|
45
|
+
apply: (target, thisArg, args) => {
|
|
46
|
+
target.call(thisArg, ...args);
|
|
47
|
+
buffer.push(["setHeader", args]);
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
proxiedRes.removeHeader = new Proxy(proxiedRes.removeHeader.bind(proxiedRes), {
|
|
51
|
+
apply: (target, thisArg, args) => {
|
|
52
|
+
target.call(thisArg, ...args);
|
|
53
|
+
buffer.push(["removeHeader", args]);
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
proxiedRes.writeHead = new Proxy(proxiedRes.writeHead.bind(proxiedRes), {
|
|
57
|
+
apply: (target, thisArg, args) => {
|
|
58
|
+
target.call(thisArg, ...args);
|
|
59
|
+
buffer.push(["writeHead", args]);
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
proxiedRes.end = new Proxy(proxiedRes.end.bind(proxiedRes), {
|
|
63
|
+
apply: (target, thisArg, args) => {
|
|
64
|
+
target.call(thisArg, ...args);
|
|
65
|
+
if (proxiedRes.statusCode === 404) {
|
|
66
|
+
next();
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
for (const [fn, args] of buffer) {
|
|
70
|
+
res[fn](...args);
|
|
71
|
+
}
|
|
72
|
+
res.end(...args);
|
|
73
|
+
buffer.length = 0;
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
return proxiedRes;
|
|
49
78
|
}
|
|
79
|
+
exports.proxyResponse = proxyResponse;
|
|
50
80
|
function simpleProxy(hostOrRequestHandler) {
|
|
51
81
|
const agent = new http_1.Agent({ keepAlive: true });
|
|
52
82
|
const firebaseDefaultsJSON = process.env.__FIREBASE_DEFAULTS__;
|
|
@@ -91,9 +121,8 @@ function simpleProxy(hostOrRequestHandler) {
|
|
|
91
121
|
});
|
|
92
122
|
}
|
|
93
123
|
else {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
proxyResponse(originalRes, next)(proxiedRes);
|
|
124
|
+
const proxiedRes = proxyResponse(originalReq, originalRes, next);
|
|
125
|
+
await hostOrRequestHandler(originalReq, proxiedRes, next);
|
|
97
126
|
}
|
|
98
127
|
};
|
|
99
128
|
}
|
|
@@ -153,12 +182,13 @@ function findDependency(name, options = {}) {
|
|
|
153
182
|
exports.findDependency = findDependency;
|
|
154
183
|
function relativeRequire(dir, mod) {
|
|
155
184
|
try {
|
|
156
|
-
const
|
|
185
|
+
const requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : require;
|
|
186
|
+
const path = requireFunc.resolve(mod, { paths: [dir] });
|
|
157
187
|
if ((0, path_1.extname)(path) === ".mjs") {
|
|
158
188
|
return dynamicImport((0, url_1.pathToFileURL)(path).toString());
|
|
159
189
|
}
|
|
160
190
|
else {
|
|
161
|
-
return
|
|
191
|
+
return requireFunc(path);
|
|
162
192
|
}
|
|
163
193
|
}
|
|
164
194
|
catch (e) {
|
package/lib/functionsConfig.js
CHANGED
|
@@ -82,7 +82,7 @@ exports.setVariablesRecursive = setVariablesRecursive;
|
|
|
82
82
|
async function materializeConfig(configName, output) {
|
|
83
83
|
const materializeVariable = async function (varName) {
|
|
84
84
|
const variable = await runtimeconfig.variables.get(varName);
|
|
85
|
-
const id =
|
|
85
|
+
const id = varNameToIds(variable.name);
|
|
86
86
|
const key = id.config + "." + id.variable.split("/").join(".");
|
|
87
87
|
_.set(output, key, variable.text);
|
|
88
88
|
};
|
|
@@ -106,7 +106,7 @@ async function materializeAll(projectId) {
|
|
|
106
106
|
if (config.name.match(new RegExp("configs/firebase"))) {
|
|
107
107
|
return;
|
|
108
108
|
}
|
|
109
|
-
return
|
|
109
|
+
return materializeConfig(config.name, output);
|
|
110
110
|
}));
|
|
111
111
|
return output;
|
|
112
112
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deleteRepository = exports.getRepository = exports.createRepository = exports.fetchLinkableRepositories = exports.deleteConnection = exports.getConnection = exports.createConnection = void 0;
|
|
4
|
+
const apiv2_1 = require("../apiv2");
|
|
5
|
+
const api_1 = require("../api");
|
|
6
|
+
const client = new apiv2_1.Client({
|
|
7
|
+
urlPrefix: api_1.cloudbuildOrigin,
|
|
8
|
+
auth: true,
|
|
9
|
+
apiVersion: "v2",
|
|
10
|
+
});
|
|
11
|
+
async function createConnection(projectId, location, connectionId) {
|
|
12
|
+
const res = await client.post(`projects/${projectId}/locations/${location}/connections`, { githubConfig: {} }, { queryParams: { connectionId } });
|
|
13
|
+
return res.body;
|
|
14
|
+
}
|
|
15
|
+
exports.createConnection = createConnection;
|
|
16
|
+
async function getConnection(projectId, location, connectionId) {
|
|
17
|
+
const name = `projects/${projectId}/locations/${location}/connections/${connectionId}`;
|
|
18
|
+
const res = await client.get(name);
|
|
19
|
+
return res.body;
|
|
20
|
+
}
|
|
21
|
+
exports.getConnection = getConnection;
|
|
22
|
+
async function deleteConnection(projectId, location, connectionId) {
|
|
23
|
+
const name = `projects/${projectId}/locations/${location}/connections/${connectionId}`;
|
|
24
|
+
const res = await client.delete(name);
|
|
25
|
+
return res.body;
|
|
26
|
+
}
|
|
27
|
+
exports.deleteConnection = deleteConnection;
|
|
28
|
+
async function fetchLinkableRepositories(projectId, location, connectionId) {
|
|
29
|
+
const name = `projects/${projectId}/locations/${location}/connections/${connectionId}:fetchLinkableRepositories`;
|
|
30
|
+
const res = await client.get(name);
|
|
31
|
+
return res.body;
|
|
32
|
+
}
|
|
33
|
+
exports.fetchLinkableRepositories = fetchLinkableRepositories;
|
|
34
|
+
async function createRepository(projectId, location, connectionId, repositoryId, remoteUri) {
|
|
35
|
+
const res = await client.post(`projects/${projectId}/locations/${location}/connections/${connectionId}/repositories`, { remoteUri }, { queryParams: { repositoryId } });
|
|
36
|
+
return res.body;
|
|
37
|
+
}
|
|
38
|
+
exports.createRepository = createRepository;
|
|
39
|
+
async function getRepository(projectId, location, connectionId, repositoryId) {
|
|
40
|
+
const name = `projects/${projectId}/locations/${location}/connections/${connectionId}/repositories/${repositoryId}`;
|
|
41
|
+
const res = await client.get(name);
|
|
42
|
+
return res.body;
|
|
43
|
+
}
|
|
44
|
+
exports.getRepository = getRepository;
|
|
45
|
+
async function deleteRepository(projectId, location, connectionId, repositoryId) {
|
|
46
|
+
const name = `projects/${projectId}/locations/${location}/connections/${connectionId}/repositories/${repositoryId}`;
|
|
47
|
+
const res = await client.delete(name);
|
|
48
|
+
return res.body;
|
|
49
|
+
}
|
|
50
|
+
exports.deleteRepository = deleteRepository;
|
package/lib/gcp/storage.js
CHANGED
|
@@ -4,17 +4,18 @@ exports.getServiceAccount = exports.listBuckets = exports.getBucket = exports.de
|
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const api_1 = require("../api");
|
|
6
6
|
const apiv2_1 = require("../apiv2");
|
|
7
|
-
const logger_1 = require("../logger");
|
|
8
7
|
const error_1 = require("../error");
|
|
8
|
+
const logger_1 = require("../logger");
|
|
9
|
+
const projects_1 = require("../management/projects");
|
|
9
10
|
async function getDefaultBucket(projectId) {
|
|
11
|
+
var _a;
|
|
10
12
|
try {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
if (resp.body.defaultBucket === "undefined") {
|
|
13
|
+
const metadata = await (0, projects_1.getFirebaseProject)(projectId);
|
|
14
|
+
if (!((_a = metadata.resources) === null || _a === void 0 ? void 0 : _a.storageBucket)) {
|
|
14
15
|
logger_1.logger.debug("Default storage bucket is undefined.");
|
|
15
16
|
throw new error_1.FirebaseError("Your project is being set up. Please wait a minute before deploying again.");
|
|
16
17
|
}
|
|
17
|
-
return
|
|
18
|
+
return metadata.resources.storageBucket;
|
|
18
19
|
}
|
|
19
20
|
catch (err) {
|
|
20
21
|
logger_1.logger.info("\n\nThere was an issue deploying your functions. Verify that your project has a Google App Engine instance setup at https://console.cloud.google.com/appengine and try again. If this issue persists, please contact support.");
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getOrCreateRepository = exports.getOrCreateConnection = exports.linkGitHubRepository = void 0;
|
|
4
|
+
const api_1 = require("../../../api");
|
|
5
|
+
const error_1 = require("../../../error");
|
|
6
|
+
const gcb = require("../../../gcp/cloudbuild");
|
|
7
|
+
const logger_1 = require("../../../logger");
|
|
8
|
+
const poller = require("../../../operation-poller");
|
|
9
|
+
const utils = require("../../../utils");
|
|
10
|
+
const prompt_1 = require("../../../prompt");
|
|
11
|
+
const gcbPollerOptions = {
|
|
12
|
+
apiOrigin: api_1.cloudbuildOrigin,
|
|
13
|
+
apiVersion: "v2",
|
|
14
|
+
masterTimeout: 25 * 60 * 1000,
|
|
15
|
+
maxBackoff: 10000,
|
|
16
|
+
};
|
|
17
|
+
function extractRepoSlugFromURI(remoteUri) {
|
|
18
|
+
const match = /github.com\/(.+).git/.exec(remoteUri);
|
|
19
|
+
if (!match) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
return match[1];
|
|
23
|
+
}
|
|
24
|
+
function generateConnectionId(stackId) {
|
|
25
|
+
return `composer-${stackId}-conn`;
|
|
26
|
+
}
|
|
27
|
+
function generateRepositoryId() {
|
|
28
|
+
return `composer-repo`;
|
|
29
|
+
}
|
|
30
|
+
async function linkGitHubRepository(projectId, location, stackId) {
|
|
31
|
+
const connectionId = generateConnectionId(stackId);
|
|
32
|
+
await getOrCreateConnection(projectId, location, connectionId);
|
|
33
|
+
let remoteUri = await promptRepositoryURI(projectId, location, connectionId);
|
|
34
|
+
while (remoteUri === "") {
|
|
35
|
+
await utils.openInBrowser("https://github.com/apps/google-cloud-build/installations/new");
|
|
36
|
+
await (0, prompt_1.promptOnce)({
|
|
37
|
+
type: "input",
|
|
38
|
+
message: "Press any key once you have finished configuring your installation's access settings.",
|
|
39
|
+
});
|
|
40
|
+
remoteUri = await promptRepositoryURI(projectId, location, connectionId);
|
|
41
|
+
}
|
|
42
|
+
const repo = await getOrCreateRepository(projectId, location, connectionId, remoteUri);
|
|
43
|
+
logger_1.logger.info(`Successfully linked GitHub repository at remote URI ${remoteUri}.`);
|
|
44
|
+
return repo;
|
|
45
|
+
}
|
|
46
|
+
exports.linkGitHubRepository = linkGitHubRepository;
|
|
47
|
+
async function promptRepositoryURI(projectId, location, connectionId) {
|
|
48
|
+
const resp = await gcb.fetchLinkableRepositories(projectId, location, connectionId);
|
|
49
|
+
if (!resp.repositories || resp.repositories.length === 0) {
|
|
50
|
+
throw new error_1.FirebaseError("The GitHub App does not have access to any repositories. Please configure " +
|
|
51
|
+
"your app installation permissions at https://github.com/settings/installations.");
|
|
52
|
+
}
|
|
53
|
+
const choices = resp.repositories.map((repo) => ({
|
|
54
|
+
name: extractRepoSlugFromURI(repo.remoteUri) || repo.remoteUri,
|
|
55
|
+
value: repo.remoteUri,
|
|
56
|
+
}));
|
|
57
|
+
choices.push({
|
|
58
|
+
name: "Missing a repo? Select this option to configure your installation's access settings",
|
|
59
|
+
value: "",
|
|
60
|
+
});
|
|
61
|
+
return await (0, prompt_1.promptOnce)({
|
|
62
|
+
type: "list",
|
|
63
|
+
message: "Which of the following repositories would you like to link?",
|
|
64
|
+
choices,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async function promptConnectionAuth(conn, projectId, location, connectionId) {
|
|
68
|
+
logger_1.logger.info(conn.installationState.message);
|
|
69
|
+
logger_1.logger.info(conn.installationState.actionUri);
|
|
70
|
+
await utils.openInBrowser(conn.installationState.actionUri);
|
|
71
|
+
await (0, prompt_1.promptOnce)({
|
|
72
|
+
type: "input",
|
|
73
|
+
message: "Press any key once you have authorized the app (Cloud Build) to access your GitHub repo.",
|
|
74
|
+
});
|
|
75
|
+
return await gcb.getConnection(projectId, location, connectionId);
|
|
76
|
+
}
|
|
77
|
+
async function getOrCreateConnection(projectId, location, connectionId) {
|
|
78
|
+
let conn;
|
|
79
|
+
try {
|
|
80
|
+
conn = await gcb.getConnection(projectId, location, connectionId);
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
if (err.status === 404) {
|
|
84
|
+
const op = await gcb.createConnection(projectId, location, connectionId);
|
|
85
|
+
conn = await poller.pollOperation(Object.assign(Object.assign({}, gcbPollerOptions), { pollerName: `create-${location}-${connectionId}`, operationResourceName: op.name }));
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
throw err;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
while (conn.installationState.stage !== "COMPLETE") {
|
|
92
|
+
conn = await promptConnectionAuth(conn, projectId, location, connectionId);
|
|
93
|
+
}
|
|
94
|
+
return conn;
|
|
95
|
+
}
|
|
96
|
+
exports.getOrCreateConnection = getOrCreateConnection;
|
|
97
|
+
async function getOrCreateRepository(projectId, location, connectionId, remoteUri) {
|
|
98
|
+
const repositoryId = generateRepositoryId();
|
|
99
|
+
if (!repositoryId) {
|
|
100
|
+
throw new error_1.FirebaseError(`Failed to generate repositoryId for URI "${remoteUri}".`);
|
|
101
|
+
}
|
|
102
|
+
let repo;
|
|
103
|
+
try {
|
|
104
|
+
repo = await gcb.getRepository(projectId, location, connectionId, repositoryId);
|
|
105
|
+
const repoSlug = extractRepoSlugFromURI(repo.remoteUri);
|
|
106
|
+
if (repoSlug) {
|
|
107
|
+
throw new error_1.FirebaseError(`${repoSlug} has already been linked.`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
if (err.status === 404) {
|
|
112
|
+
const op = await gcb.createRepository(projectId, location, connectionId, repositoryId, remoteUri);
|
|
113
|
+
repo = await poller.pollOperation(Object.assign(Object.assign({}, gcbPollerOptions), { pollerName: `create-${location}-${connectionId}-${repositoryId}`, operationResourceName: op.name }));
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
throw err;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return repo;
|
|
120
|
+
}
|
|
121
|
+
exports.getOrCreateRepository = getOrCreateRepository;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ALLOWED_DEPLOY_METHODS = exports.DEFAULT_DEPLOY_METHOD = exports.ALLOWED_REGIONS = exports.DEFAULT_REGION = void 0;
|
|
4
|
+
exports.DEFAULT_REGION = "us-central1";
|
|
5
|
+
exports.ALLOWED_REGIONS = [{ name: "us-central1 (Iowa)", value: "us-central1" }];
|
|
6
|
+
exports.DEFAULT_DEPLOY_METHOD = "github";
|
|
7
|
+
exports.ALLOWED_DEPLOY_METHODS = [{ name: "Deploy using github", value: "github" }];
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.doSetup = void 0;
|
|
4
|
+
const clc = require("colorette");
|
|
5
|
+
const utils = require("../../../utils");
|
|
6
|
+
const logger_1 = require("../../../logger");
|
|
7
|
+
const prompt_1 = require("../../../prompt");
|
|
8
|
+
const constants_1 = require("./constants");
|
|
9
|
+
async function doSetup(setup) {
|
|
10
|
+
setup.frameworks = {};
|
|
11
|
+
utils.logBullet("First we need a few details to create your service.");
|
|
12
|
+
await (0, prompt_1.promptOnce)({
|
|
13
|
+
name: "serviceName",
|
|
14
|
+
type: "input",
|
|
15
|
+
default: "acme-inc-web",
|
|
16
|
+
message: "Create a name for your service [6-32 characters]",
|
|
17
|
+
}, setup.frameworks);
|
|
18
|
+
await (0, prompt_1.promptOnce)({
|
|
19
|
+
name: "region",
|
|
20
|
+
type: "list",
|
|
21
|
+
default: constants_1.DEFAULT_REGION,
|
|
22
|
+
message: "Please select a region " +
|
|
23
|
+
`(${clc.yellow("info")}: Your region determines where your backend is located):\n`,
|
|
24
|
+
choices: constants_1.ALLOWED_REGIONS,
|
|
25
|
+
}, setup.frameworks);
|
|
26
|
+
utils.logSuccess(`Region set to ${setup.frameworks.region}.`);
|
|
27
|
+
logger_1.logger.info(clc.bold(`\n${clc.white("===")} Deploy Setup`));
|
|
28
|
+
await (0, prompt_1.promptOnce)({
|
|
29
|
+
name: "deployMethod",
|
|
30
|
+
type: "list",
|
|
31
|
+
default: constants_1.DEFAULT_DEPLOY_METHOD,
|
|
32
|
+
message: "How do you want to deploy",
|
|
33
|
+
choices: constants_1.ALLOWED_DEPLOY_METHODS,
|
|
34
|
+
}, setup.frameworks);
|
|
35
|
+
}
|
|
36
|
+
exports.doSetup = doSetup;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.hostingGithub = exports.remoteconfig = exports.project = exports.extensions = exports.emulators = exports.storage = exports.hosting = exports.functions = exports.firestore = exports.database = exports.account = void 0;
|
|
3
|
+
exports.frameworks = exports.hostingGithub = exports.remoteconfig = exports.project = exports.extensions = exports.emulators = exports.storage = exports.hosting = exports.functions = exports.firestore = exports.database = exports.account = void 0;
|
|
4
4
|
var account_1 = require("./account");
|
|
5
5
|
Object.defineProperty(exports, "account", { enumerable: true, get: function () { return account_1.doSetup; } });
|
|
6
6
|
var database_1 = require("./database");
|
|
@@ -23,3 +23,5 @@ var remoteconfig_1 = require("./remoteconfig");
|
|
|
23
23
|
Object.defineProperty(exports, "remoteconfig", { enumerable: true, get: function () { return remoteconfig_1.doSetup; } });
|
|
24
24
|
var github_1 = require("./hosting/github");
|
|
25
25
|
Object.defineProperty(exports, "hostingGithub", { enumerable: true, get: function () { return github_1.initGitHub; } });
|
|
26
|
+
var frameworks_1 = require("./frameworks");
|
|
27
|
+
Object.defineProperty(exports, "frameworks", { enumerable: true, get: function () { return frameworks_1.doSetup; } });
|
package/lib/init/index.js
CHANGED
|
@@ -6,6 +6,7 @@ const clc = require("colorette");
|
|
|
6
6
|
const error_1 = require("../error");
|
|
7
7
|
const logger_1 = require("../logger");
|
|
8
8
|
const features = require("./features");
|
|
9
|
+
const experiments_1 = require("../experiments");
|
|
9
10
|
const featureFns = new Map([
|
|
10
11
|
["account", features.account],
|
|
11
12
|
["database", features.database],
|
|
@@ -19,6 +20,9 @@ const featureFns = new Map([
|
|
|
19
20
|
["remoteconfig", features.remoteconfig],
|
|
20
21
|
["hosting:github", features.hostingGithub],
|
|
21
22
|
]);
|
|
23
|
+
if ((0, experiments_1.isEnabled)("frameworks")) {
|
|
24
|
+
featureFns.set("frameworks", features.frameworks);
|
|
25
|
+
}
|
|
22
26
|
async function init(setup, config, options) {
|
|
23
27
|
var _a;
|
|
24
28
|
const nextFeature = (_a = setup.features) === null || _a === void 0 ? void 0 : _a.shift();
|
|
@@ -300,7 +300,11 @@ async function getFirebaseProject(projectId) {
|
|
|
300
300
|
return res.body;
|
|
301
301
|
}
|
|
302
302
|
catch (err) {
|
|
303
|
-
|
|
303
|
+
let message = err.message;
|
|
304
|
+
if (err.original) {
|
|
305
|
+
message += ` (original: ${err.original.message})`;
|
|
306
|
+
}
|
|
307
|
+
logger_1.logger.debug(message);
|
|
304
308
|
throw new error_1.FirebaseError(`Failed to get Firebase project ${projectId}. ` +
|
|
305
309
|
"Please make sure the project exists and your account has permission to access it.", { exit: 2, original: err });
|
|
306
310
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isMonospaceEnv = exports.selectProjectInMonospace = void 0;
|
|
4
|
+
const node_fetch_1 = require("node-fetch");
|
|
5
|
+
const error_1 = require("../error");
|
|
6
|
+
const logger_1 = require("../logger");
|
|
7
|
+
const rc_1 = require("../rc");
|
|
8
|
+
const POLL_USER_RESPONSE_MILLIS = 2000;
|
|
9
|
+
async function selectProjectInMonospace({ projectRoot, project, isVSCE, }) {
|
|
10
|
+
const initFirebaseResponse = await initFirebase(project);
|
|
11
|
+
if (initFirebaseResponse.success === false) {
|
|
12
|
+
throw new Error(String(initFirebaseResponse.error));
|
|
13
|
+
}
|
|
14
|
+
const { rid } = initFirebaseResponse;
|
|
15
|
+
const authorizedProject = await pollAuthorizedProject(rid);
|
|
16
|
+
if (!authorizedProject)
|
|
17
|
+
return null;
|
|
18
|
+
if (isVSCE)
|
|
19
|
+
return authorizedProject;
|
|
20
|
+
if (projectRoot)
|
|
21
|
+
createFirebaseRc(projectRoot, authorizedProject);
|
|
22
|
+
}
|
|
23
|
+
exports.selectProjectInMonospace = selectProjectInMonospace;
|
|
24
|
+
async function pollAuthorizedProject(rid) {
|
|
25
|
+
const getInitFirebaseRes = await getInitFirebaseResponse(rid);
|
|
26
|
+
if ("userResponse" in getInitFirebaseRes) {
|
|
27
|
+
if (getInitFirebaseRes.userResponse.success) {
|
|
28
|
+
return getInitFirebaseRes.userResponse.projectId;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const { error } = getInitFirebaseRes;
|
|
33
|
+
if (error === "WAITING_FOR_RESPONSE") {
|
|
34
|
+
await new Promise((res) => setTimeout(res, POLL_USER_RESPONSE_MILLIS));
|
|
35
|
+
return pollAuthorizedProject(rid);
|
|
36
|
+
}
|
|
37
|
+
if (error === "USER_CANCELED") {
|
|
38
|
+
throw new error_1.FirebaseError("User canceled without authorizing any project");
|
|
39
|
+
}
|
|
40
|
+
throw new error_1.FirebaseError(`Unhandled /get-init-firebase-response error`, {
|
|
41
|
+
original: new Error(error),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async function initFirebase(project) {
|
|
45
|
+
const port = getMonospaceDaemonPort();
|
|
46
|
+
if (!port)
|
|
47
|
+
throw new error_1.FirebaseError("Undefined MONOSPACE_DAEMON_PORT");
|
|
48
|
+
const initFirebaseURL = new URL(`http://localhost:${port}/init-firebase`);
|
|
49
|
+
if (project) {
|
|
50
|
+
initFirebaseURL.searchParams.set("known_project", project);
|
|
51
|
+
}
|
|
52
|
+
const initFirebaseRes = await (0, node_fetch_1.default)(initFirebaseURL.toString(), {
|
|
53
|
+
method: "POST",
|
|
54
|
+
headers: {
|
|
55
|
+
"Content-Type": "application/json",
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
const initFirebaseResponse = (await initFirebaseRes.json());
|
|
59
|
+
return initFirebaseResponse;
|
|
60
|
+
}
|
|
61
|
+
async function getInitFirebaseResponse(rid) {
|
|
62
|
+
const port = getMonospaceDaemonPort();
|
|
63
|
+
if (!port)
|
|
64
|
+
throw new error_1.FirebaseError("Undefined MONOSPACE_DAEMON_PORT");
|
|
65
|
+
const getInitFirebaseRes = await (0, node_fetch_1.default)(`http://localhost:${port}/get-init-firebase-response?rid=${rid}`);
|
|
66
|
+
const getInitFirebaseJson = (await getInitFirebaseRes.json());
|
|
67
|
+
logger_1.logger.debug(`/get-init-firebase-response?rid=${rid} response:`);
|
|
68
|
+
logger_1.logger.debug(getInitFirebaseJson);
|
|
69
|
+
return getInitFirebaseJson;
|
|
70
|
+
}
|
|
71
|
+
function createFirebaseRc(projectDir, authorizedProject) {
|
|
72
|
+
const firebaseRc = (0, rc_1.loadRC)({ cwd: projectDir });
|
|
73
|
+
firebaseRc.addProjectAlias("default", authorizedProject);
|
|
74
|
+
return firebaseRc.save();
|
|
75
|
+
}
|
|
76
|
+
function isMonospaceEnv() {
|
|
77
|
+
return getMonospaceDaemonPort() !== undefined;
|
|
78
|
+
}
|
|
79
|
+
exports.isMonospaceEnv = isMonospaceEnv;
|
|
80
|
+
function getMonospaceDaemonPort() {
|
|
81
|
+
return process.env.MONOSPACE_DAEMON_PORT;
|
|
82
|
+
}
|
package/lib/requireAuth.js
CHANGED
|
@@ -10,6 +10,7 @@ const logger_1 = require("./logger");
|
|
|
10
10
|
const utils = require("./utils");
|
|
11
11
|
const scopes = require("./scopes");
|
|
12
12
|
const auth_1 = require("./auth");
|
|
13
|
+
const monospace_1 = require("./monospace");
|
|
13
14
|
const AUTH_ERROR_MESSAGE = `Command requires authentication, please run ${clc.bold("firebase login")}`;
|
|
14
15
|
let authClient;
|
|
15
16
|
function getAuthClient(config) {
|
|
@@ -23,6 +24,13 @@ async function autoAuth(options, authScopes) {
|
|
|
23
24
|
const client = getAuthClient({ scopes: authScopes, projectId: options.project });
|
|
24
25
|
const token = await client.getAccessToken();
|
|
25
26
|
token !== null ? apiv2.setAccessToken(token) : false;
|
|
27
|
+
if (!options.isVSCE && (0, monospace_1.isMonospaceEnv)()) {
|
|
28
|
+
await (0, monospace_1.selectProjectInMonospace)({
|
|
29
|
+
projectRoot: options.config.projectDir,
|
|
30
|
+
project: options.project,
|
|
31
|
+
isVSCE: options.isVSCE,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
26
34
|
}
|
|
27
35
|
async function requireAuth(options) {
|
|
28
36
|
api.setScopes([scopes.CLOUD_PLATFORM, scopes.FIREBASE_PLATFORM]);
|