firebase-tools 13.34.0 → 13.35.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/apphosting/config.js +8 -6
- package/lib/apphosting/yaml.js +21 -48
- package/lib/commands/dataconnect-sdk-generate.js +4 -1
- package/lib/commands/dataconnect-sql-diff.js +1 -1
- package/lib/commands/dataconnect-sql-setup.js +6 -1
- package/lib/commands/functions-artifacts-setpolicy.js +123 -0
- package/lib/commands/index.js +2 -0
- package/lib/commands/open.js +3 -0
- package/lib/dataconnect/build.js +3 -1
- package/lib/dataconnect/fileUtils.js +8 -4
- package/lib/dataconnect/schemaMigration.js +27 -24
- package/lib/defaultCredentials.js +12 -1
- package/lib/deploy/dataconnect/prepare.js +1 -1
- package/lib/deploy/functions/containerCleaner.js +17 -2
- package/lib/deploy/functions/runtimes/discovery/index.js +3 -1
- package/lib/deploy/index.js +10 -4
- package/lib/emulator/ExpressBasedEmulator.js +1 -1
- package/lib/emulator/apphosting/index.js +1 -0
- package/lib/emulator/apphosting/serve.js +48 -7
- package/lib/emulator/controller.js +1 -0
- package/lib/emulator/dataconnect/pgliteServer.js +7 -2
- package/lib/emulator/dataconnectEmulator.js +15 -4
- package/lib/emulator/downloadableEmulators.js +9 -9
- package/lib/emulator/env.js +17 -1
- package/lib/emulator/functionsEmulator.js +2 -20
- package/lib/extensions/extensionsHelper.js +8 -4
- package/lib/frameworks/angular/utils.js +60 -42
- package/lib/functions/artifacts.js +105 -0
- package/lib/gcp/artifactregistry.js +27 -2
- package/lib/gcp/cloudsql/connect.js +17 -5
- package/lib/gcp/cloudsql/permissions_setup.js +28 -14
- package/lib/init/features/dataconnect/index.js +1 -1
- package/lib/init/features/dataconnect/sdk.js +23 -3
- package/package.json +1 -1
- package/templates/init/dataconnect/connector.yaml +1 -1
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getEmulatorEnvs = exports.start = void 0;
|
|
4
4
|
const net_1 = require("net");
|
|
5
|
+
const clc = require("colorette");
|
|
5
6
|
const portUtils_1 = require("../portUtils");
|
|
6
7
|
const developmentServer_1 = require("./developmentServer");
|
|
7
8
|
const constants_1 = require("../constants");
|
|
@@ -12,6 +13,9 @@ const config_1 = require("./config");
|
|
|
12
13
|
const projectPath_1 = require("../../projectPath");
|
|
13
14
|
const registry_1 = require("../registry");
|
|
14
15
|
const env_1 = require("../env");
|
|
16
|
+
const error_1 = require("../../error");
|
|
17
|
+
const secrets = require("../../gcp/secretManager");
|
|
18
|
+
const utils_1 = require("../../utils");
|
|
15
19
|
async function start(options) {
|
|
16
20
|
var _a;
|
|
17
21
|
const hostname = constants_1.DEFAULT_HOST;
|
|
@@ -19,19 +23,56 @@ async function start(options) {
|
|
|
19
23
|
while (!(await availablePort(hostname, port))) {
|
|
20
24
|
port += 1;
|
|
21
25
|
}
|
|
22
|
-
serve(port, options === null || options === void 0 ? void 0 : options.startCommand, options === null || options === void 0 ? void 0 : options.rootDirectory);
|
|
26
|
+
await serve(options === null || options === void 0 ? void 0 : options.projectId, port, options === null || options === void 0 ? void 0 : options.startCommand, options === null || options === void 0 ? void 0 : options.rootDirectory);
|
|
23
27
|
return { hostname, port };
|
|
24
28
|
}
|
|
25
29
|
exports.start = start;
|
|
26
|
-
|
|
30
|
+
const secretResourceRegex = /^projects\/([^/]+)\/secrets\/([^/]+)(?:\/versions\/((?:latest)|\d+))?$/;
|
|
31
|
+
const secretShorthandRegex = /^([^/@]+)(?:@((?:latest)|\d+))?$/;
|
|
32
|
+
async function loadSecret(project, name) {
|
|
33
|
+
var _a, _b, _c, _d;
|
|
34
|
+
let projectId;
|
|
35
|
+
let secretId;
|
|
36
|
+
let version;
|
|
37
|
+
const match = secretResourceRegex.exec(name);
|
|
38
|
+
if (match) {
|
|
39
|
+
projectId = match[1];
|
|
40
|
+
secretId = match[2];
|
|
41
|
+
version = match[3] || "latest";
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const match = secretShorthandRegex.exec(name);
|
|
45
|
+
if (!match) {
|
|
46
|
+
throw new error_1.FirebaseError(`Invalid secret name: ${name}`);
|
|
47
|
+
}
|
|
48
|
+
if (!project) {
|
|
49
|
+
throw new error_1.FirebaseError(`Cannot load secret ${match[1]} without a project. ` +
|
|
50
|
+
`Please use ${clc.bold("firebase use")} or pass the --project flag.`);
|
|
51
|
+
}
|
|
52
|
+
projectId = project;
|
|
53
|
+
secretId = match[1];
|
|
54
|
+
version = match[2] || "latest";
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
return await secrets.accessSecretVersion(projectId, secretId, version);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
if (((_a = err === null || err === void 0 ? void 0 : err.original) === null || _a === void 0 ? void 0 : _a.code) === 403 || ((_d = (_c = (_b = err === null || err === void 0 ? void 0 : err.original) === null || _b === void 0 ? void 0 : _b.context) === null || _c === void 0 ? void 0 : _c.response) === null || _d === void 0 ? void 0 : _d.statusCode) === 403) {
|
|
61
|
+
(0, utils_1.logLabeledError)(types_1.Emulators.APPHOSTING, `Permission denied to access secret ${secretId}. Use ` +
|
|
62
|
+
`${clc.bold("firebase apphosting:secrets:grantaccess")} to get permissions.`);
|
|
63
|
+
}
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async function serve(projectId, port, startCommand, backendRelativeDir) {
|
|
27
68
|
backendRelativeDir = backendRelativeDir !== null && backendRelativeDir !== void 0 ? backendRelativeDir : "./";
|
|
28
69
|
const backendRoot = (0, projectPath_1.resolveProjectPath)({}, backendRelativeDir);
|
|
29
70
|
const apphostingLocalConfig = await (0, config_1.getLocalAppHostingConfiguration)(backendRoot);
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const environmentVariablesToInject = Object.assign(Object.assign(Object.assign({}, getEmulatorEnvs()),
|
|
71
|
+
const resolveEnv = Object.entries(apphostingLocalConfig.env).map(async ([key, value]) => [
|
|
72
|
+
key,
|
|
73
|
+
value.value ? value.value : await loadSecret(projectId, value.secret),
|
|
74
|
+
]);
|
|
75
|
+
const environmentVariablesToInject = Object.assign(Object.assign(Object.assign({}, getEmulatorEnvs()), Object.fromEntries(await Promise.all(resolveEnv))), { PORT: port.toString() });
|
|
35
76
|
if (startCommand) {
|
|
36
77
|
developmentServer_2.logger.logLabeled("BULLET", types_1.Emulators.APPHOSTING, `running custom start command: '${startCommand}'`);
|
|
37
78
|
await (0, spawn_1.spawnWithCommandString)(startCommand, backendRoot, environmentVariablesToInject);
|
|
@@ -635,6 +635,7 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
635
635
|
apphostingLogger.logLabeled("WARN", types_1.Emulators.APPHOSTING, "The `firebase.json#emulators.apphosting.startCommandOverride` config is deprecated, please use `firebase.json#emulators.apphosting.startCommand` to set a custom start command instead");
|
|
636
636
|
}
|
|
637
637
|
const apphostingEmulator = new apphosting_1.AppHostingEmulator({
|
|
638
|
+
projectId: options.project,
|
|
638
639
|
host: apphostingAddr.host,
|
|
639
640
|
port: apphostingAddr.port,
|
|
640
641
|
startCommand: (apphostingConfig === null || apphostingConfig === void 0 ? void 0 : apphostingConfig.startCommand) || (apphostingConfig === null || apphostingConfig === void 0 ? void 0 : apphostingConfig.startCommandOverride),
|
|
@@ -149,6 +149,7 @@ class PGliteExtendedQueryPatch {
|
|
|
149
149
|
constructor(connection) {
|
|
150
150
|
this.connection = connection;
|
|
151
151
|
this.isExtendedQuery = false;
|
|
152
|
+
this.eqpErrored = false;
|
|
152
153
|
}
|
|
153
154
|
filterResponse(message, response) {
|
|
154
155
|
return __asyncGenerator(this, arguments, function* filterResponse_1() {
|
|
@@ -166,6 +167,7 @@ class PGliteExtendedQueryPatch {
|
|
|
166
167
|
}
|
|
167
168
|
if (message[0] === index_1.FrontendMessageCode.Sync) {
|
|
168
169
|
this.isExtendedQuery = false;
|
|
170
|
+
this.eqpErrored = false;
|
|
169
171
|
return yield __await(this.connection.createReadyForQuery());
|
|
170
172
|
}
|
|
171
173
|
try {
|
|
@@ -174,8 +176,11 @@ class PGliteExtendedQueryPatch {
|
|
|
174
176
|
_d = false;
|
|
175
177
|
try {
|
|
176
178
|
const message = _c;
|
|
177
|
-
if (
|
|
178
|
-
|
|
179
|
+
if (this.eqpErrored) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (this.isExtendedQuery && message[0] === index_1.BackendMessageCode.ErrorMessage) {
|
|
183
|
+
this.eqpErrored = true;
|
|
179
184
|
}
|
|
180
185
|
if (this.isExtendedQuery && message[0] === index_1.BackendMessageCode.ReadyForQuery) {
|
|
181
186
|
logger_1.logger.debug("Filtered out a ReadyForQuery.");
|
|
@@ -20,6 +20,7 @@ const load_1 = require("../dataconnect/load");
|
|
|
20
20
|
const pgliteServer_1 = require("./dataconnect/pgliteServer");
|
|
21
21
|
const controller_1 = require("./controller");
|
|
22
22
|
const utils_1 = require("../utils");
|
|
23
|
+
const env_1 = require("./env");
|
|
23
24
|
exports.dataConnectEmulatorEvents = new events_1.EventEmitter();
|
|
24
25
|
class DataConnectEmulator {
|
|
25
26
|
constructor(args) {
|
|
@@ -33,7 +34,10 @@ class DataConnectEmulator {
|
|
|
33
34
|
let resolvedConfigDir;
|
|
34
35
|
try {
|
|
35
36
|
resolvedConfigDir = this.args.config.path(this.args.configDir);
|
|
36
|
-
const info = await DataConnectEmulator.build({
|
|
37
|
+
const info = await DataConnectEmulator.build({
|
|
38
|
+
configDir: resolvedConfigDir,
|
|
39
|
+
account: this.args.account,
|
|
40
|
+
});
|
|
37
41
|
if ((0, types_2.requiresVector)(info.metadata)) {
|
|
38
42
|
if (constants_1.Constants.isDemoProject(this.args.projectId)) {
|
|
39
43
|
this.logger.logLabeled("WARN", "dataconnect", "Detected a 'demo-' project, but vector embeddings require a real project. Operations that use vector_embed will fail.");
|
|
@@ -46,13 +50,14 @@ class DataConnectEmulator {
|
|
|
46
50
|
catch (err) {
|
|
47
51
|
this.logger.log("DEBUG", `'fdc build' failed with error: ${err.message}`);
|
|
48
52
|
}
|
|
53
|
+
const env = await DataConnectEmulator.getEnv(this.args.account, this.args.extraEnv);
|
|
49
54
|
await (0, downloadableEmulators_1.start)(types_1.Emulators.DATACONNECT, {
|
|
50
55
|
auto_download: this.args.auto_download,
|
|
51
56
|
listen: (0, portUtils_1.listenSpecsToString)(this.args.listen),
|
|
52
57
|
config_dir: resolvedConfigDir,
|
|
53
58
|
enable_output_schema_extensions: this.args.enable_output_schema_extensions,
|
|
54
59
|
enable_output_generated_sdk: this.args.enable_output_generated_sdk,
|
|
55
|
-
},
|
|
60
|
+
}, env);
|
|
56
61
|
this.usingExistingEmulator = false;
|
|
57
62
|
if (this.args.autoconnectToPostgres) {
|
|
58
63
|
const info = await (0, load_1.load)(this.args.projectId, this.args.config, this.args.configDir);
|
|
@@ -159,7 +164,8 @@ class DataConnectEmulator {
|
|
|
159
164
|
if (args.watch) {
|
|
160
165
|
cmd.push("--watch");
|
|
161
166
|
}
|
|
162
|
-
const
|
|
167
|
+
const env = await DataConnectEmulator.getEnv(args.account);
|
|
168
|
+
const res = childProcess.spawnSync(commandInfo.binary, cmd, { encoding: "utf-8", env });
|
|
163
169
|
if ((0, downloadableEmulators_1.isIncomaptibleArchError)(res.error)) {
|
|
164
170
|
throw new error_1.FirebaseError(`Unknown system error when running the Data Connect toolkit. ` +
|
|
165
171
|
`You may be able to fix this by installing Rosetta: ` +
|
|
@@ -183,7 +189,8 @@ class DataConnectEmulator {
|
|
|
183
189
|
if (args.projectId) {
|
|
184
190
|
cmd.push(`--project_id=${args.projectId}`);
|
|
185
191
|
}
|
|
186
|
-
const
|
|
192
|
+
const env = await DataConnectEmulator.getEnv(args.account);
|
|
193
|
+
const res = childProcess.spawnSync(commandInfo.binary, cmd, { encoding: "utf-8", env });
|
|
187
194
|
if ((0, downloadableEmulators_1.isIncomaptibleArchError)(res.error)) {
|
|
188
195
|
throw new error_1.FirebaseError(`Unkown system error when running the Data Connect toolkit. ` +
|
|
189
196
|
`You may be able to fix this by installing Rosetta: ` +
|
|
@@ -236,6 +243,10 @@ class DataConnectEmulator {
|
|
|
236
243
|
}
|
|
237
244
|
return false;
|
|
238
245
|
}
|
|
246
|
+
static async getEnv(account, extraEnv = {}) {
|
|
247
|
+
const credsEnv = await (0, env_1.getCredentialsEnvironment)(account, emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.DATACONNECT), "dataconnect");
|
|
248
|
+
return Object.assign(Object.assign(Object.assign({}, process.env), extraEnv), credsEnv);
|
|
249
|
+
}
|
|
239
250
|
}
|
|
240
251
|
exports.DataConnectEmulator = DataConnectEmulator;
|
|
241
252
|
class DataConnectEmulatorClient {
|
|
@@ -48,20 +48,20 @@ const EMULATOR_UPDATE_DETAILS = {
|
|
|
48
48
|
},
|
|
49
49
|
dataconnect: process.platform === "darwin"
|
|
50
50
|
? {
|
|
51
|
-
version: "1.
|
|
52
|
-
expectedSize:
|
|
53
|
-
expectedChecksum: "
|
|
51
|
+
version: "1.9.2",
|
|
52
|
+
expectedSize: 26403584,
|
|
53
|
+
expectedChecksum: "a0a957bb5d564059ed883fee9e9fd67a",
|
|
54
54
|
}
|
|
55
55
|
: process.platform === "win32"
|
|
56
56
|
? {
|
|
57
|
-
version: "1.
|
|
58
|
-
expectedSize:
|
|
59
|
-
expectedChecksum: "
|
|
57
|
+
version: "1.9.2",
|
|
58
|
+
expectedSize: 26846208,
|
|
59
|
+
expectedChecksum: "80f49b574aa69ef76fb49e2e96c8a699",
|
|
60
60
|
}
|
|
61
61
|
: {
|
|
62
|
-
version: "1.
|
|
63
|
-
expectedSize:
|
|
64
|
-
expectedChecksum: "
|
|
62
|
+
version: "1.9.2",
|
|
63
|
+
expectedSize: 26316952,
|
|
64
|
+
expectedChecksum: "cc3c0318e453d9ddf098b582ee0f2b77",
|
|
65
65
|
},
|
|
66
66
|
};
|
|
67
67
|
exports.DownloadDetails = {
|
package/lib/emulator/env.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.setEnvVarsForEmulators = void 0;
|
|
3
|
+
exports.getCredentialsEnvironment = exports.setEnvVarsForEmulators = void 0;
|
|
4
4
|
const constants_1 = require("./constants");
|
|
5
5
|
const types_1 = require("./types");
|
|
6
6
|
const functionsEmulatorShared_1 = require("./functionsEmulatorShared");
|
|
7
|
+
const defaultCredentials_1 = require("../defaultCredentials");
|
|
7
8
|
function setEnvVarsForEmulators(env, emulators) {
|
|
8
9
|
for (const emu of emulators) {
|
|
9
10
|
const host = (0, functionsEmulatorShared_1.formatHost)(emu);
|
|
@@ -40,3 +41,18 @@ function setEnvVarsForEmulators(env, emulators) {
|
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
exports.setEnvVarsForEmulators = setEnvVarsForEmulators;
|
|
44
|
+
async function getCredentialsEnvironment(account, logger, logLabel) {
|
|
45
|
+
const credentialEnv = {};
|
|
46
|
+
if (await (0, defaultCredentials_1.hasDefaultCredentials)()) {
|
|
47
|
+
logger.logLabeled("WARN", logLabel, `Application Default Credentials detected. Non-emulated services will access production using these credentials. Be careful!`);
|
|
48
|
+
}
|
|
49
|
+
else if (account) {
|
|
50
|
+
const defaultCredPath = await (0, defaultCredentials_1.getCredentialPathAsync)(account);
|
|
51
|
+
if (defaultCredPath) {
|
|
52
|
+
logger.log("DEBUG", `Setting GAC to ${defaultCredPath}`);
|
|
53
|
+
credentialEnv.GOOGLE_APPLICATION_CREDENTIALS = defaultCredPath;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return credentialEnv;
|
|
57
|
+
}
|
|
58
|
+
exports.getCredentialsEnvironment = getCredentialsEnvironment;
|
|
@@ -25,7 +25,6 @@ const functionsRuntimeWorker_1 = require("./functionsRuntimeWorker");
|
|
|
25
25
|
const error_1 = require("../error");
|
|
26
26
|
const workQueue_1 = require("./workQueue");
|
|
27
27
|
const utils_1 = require("../utils");
|
|
28
|
-
const defaultCredentials_1 = require("../defaultCredentials");
|
|
29
28
|
const adminSdkConfig_1 = require("./adminSdkConfig");
|
|
30
29
|
const validate_1 = require("../deploy/functions/validate");
|
|
31
30
|
const secretManager_1 = require("../gcp/secretManager");
|
|
@@ -111,7 +110,7 @@ class FunctionsEmulator {
|
|
|
111
110
|
this.dynamicBackends =
|
|
112
111
|
this.args.extensionsEmulator.filterUnemulatedTriggers(unfilteredBackends);
|
|
113
112
|
const mode = this.debugMode ? types_1.FunctionsExecutionMode.SEQUENTIAL : types_1.FunctionsExecutionMode.AUTO;
|
|
114
|
-
const credentialEnv = await
|
|
113
|
+
const credentialEnv = await (0, env_1.getCredentialsEnvironment)(this.args.account, this.logger, "functions");
|
|
115
114
|
for (const backend of this.dynamicBackends) {
|
|
116
115
|
backend.env = Object.assign(Object.assign({}, credentialEnv), backend.env);
|
|
117
116
|
if (this.workerPools[backend.codebase]) {
|
|
@@ -130,23 +129,6 @@ class FunctionsEmulator {
|
|
|
130
129
|
}
|
|
131
130
|
}
|
|
132
131
|
}
|
|
133
|
-
async getCredentialsEnvironment() {
|
|
134
|
-
const credentialEnv = {};
|
|
135
|
-
if (process.env.GOOGLE_APPLICATION_CREDENTIALS) {
|
|
136
|
-
this.logger.logLabeled("WARN", "functions", `Your GOOGLE_APPLICATION_CREDENTIALS environment variable points to ${process.env.GOOGLE_APPLICATION_CREDENTIALS}. Non-emulated services will access production using these credentials. Be careful!`);
|
|
137
|
-
}
|
|
138
|
-
else if (this.args.account) {
|
|
139
|
-
const defaultCredPath = await (0, defaultCredentials_1.getCredentialPathAsync)(this.args.account);
|
|
140
|
-
if (defaultCredPath) {
|
|
141
|
-
this.logger.log("DEBUG", `Setting GAC to ${defaultCredPath}`);
|
|
142
|
-
credentialEnv.GOOGLE_APPLICATION_CREDENTIALS = defaultCredPath;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
146
|
-
this.logger.logLabeled("WARN", "functions", "You are not signed in to the Firebase CLI. If you have authorized this machine using gcloud application-default credentials those may be discovered and used to access production services.");
|
|
147
|
-
}
|
|
148
|
-
return credentialEnv;
|
|
149
|
-
}
|
|
150
132
|
createHubServer() {
|
|
151
133
|
this.workQueue.start();
|
|
152
134
|
const hub = express();
|
|
@@ -255,7 +237,7 @@ class FunctionsEmulator {
|
|
|
255
237
|
});
|
|
256
238
|
}
|
|
257
239
|
async start() {
|
|
258
|
-
const credentialEnv = await
|
|
240
|
+
const credentialEnv = await (0, env_1.getCredentialsEnvironment)(this.args.account, this.logger, "functions");
|
|
259
241
|
for (const e of this.staticBackends) {
|
|
260
242
|
e.env = Object.assign(Object.assign({}, credentialEnv), e.env);
|
|
261
243
|
}
|
|
@@ -868,10 +868,14 @@ function isUrlPath(extInstallPath) {
|
|
|
868
868
|
exports.isUrlPath = isUrlPath;
|
|
869
869
|
function isLocalPath(extInstallPath) {
|
|
870
870
|
const trimmedPath = extInstallPath.trim();
|
|
871
|
-
return (trimmedPath.startsWith(
|
|
872
|
-
trimmedPath.startsWith(
|
|
873
|
-
trimmedPath.startsWith(
|
|
874
|
-
trimmedPath.startsWith(
|
|
871
|
+
return (trimmedPath.startsWith(`~${path.sep}`) ||
|
|
872
|
+
trimmedPath.startsWith(`.${path.sep}`) ||
|
|
873
|
+
trimmedPath.startsWith(`..${path.sep}`) ||
|
|
874
|
+
trimmedPath.startsWith(`${path.sep}`) ||
|
|
875
|
+
trimmedPath.startsWith(`~/`) ||
|
|
876
|
+
trimmedPath.startsWith(`./`) ||
|
|
877
|
+
trimmedPath.startsWith(`../`) ||
|
|
878
|
+
trimmedPath.startsWith(`/`) ||
|
|
875
879
|
[".", ".."].includes(trimmedPath));
|
|
876
880
|
}
|
|
877
881
|
exports.isLocalPath = isLocalPath;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.tryToGetOptionsForTarget = exports.getAngularVersion = exports.getBuildConfig = exports.getServerConfig = exports.getBrowserConfig = exports.getContext = exports.getAllTargets = void 0;
|
|
3
|
+
exports.getBuilderType = exports.tryToGetOptionsForTarget = exports.getAngularVersion = exports.getBuildConfig = exports.getServerConfig = exports.getBrowserConfig = exports.getContext = exports.getAllTargets = exports.BuilderType = void 0;
|
|
4
4
|
const utils_1 = require("../utils");
|
|
5
5
|
const error_1 = require("../../error");
|
|
6
6
|
const path_1 = require("path");
|
|
@@ -54,24 +54,30 @@ async function localesForTarget(dir, architectHost, target, workspaceProject) {
|
|
|
54
54
|
(0, utils_1.validateLocales)(locales);
|
|
55
55
|
return { locales, defaultLocale };
|
|
56
56
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
];
|
|
62
|
-
|
|
57
|
+
var BuilderType;
|
|
58
|
+
(function (BuilderType) {
|
|
59
|
+
BuilderType["DEPLOY"] = "deploy";
|
|
60
|
+
BuilderType["DEV_SERVER"] = "dev-server";
|
|
61
|
+
BuilderType["SSR_DEV_SERVER"] = "ssr-dev-server";
|
|
62
|
+
BuilderType["SERVER"] = "server";
|
|
63
|
+
BuilderType["BROWSER"] = "browser";
|
|
64
|
+
BuilderType["BROWSER_ESBUILD"] = "browser-esbuild";
|
|
65
|
+
BuilderType["APPLICATION"] = "application";
|
|
66
|
+
BuilderType["PRERENDER"] = "prerender";
|
|
67
|
+
})(BuilderType = exports.BuilderType || (exports.BuilderType = {}));
|
|
68
|
+
const DEV_SERVER_TARGETS = [BuilderType.DEV_SERVER, BuilderType.SSR_DEV_SERVER];
|
|
69
|
+
function getValidBuilderTypes(purpose) {
|
|
63
70
|
return [
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
"@nguniversal/builders:prerender",
|
|
71
|
+
BuilderType.APPLICATION,
|
|
72
|
+
BuilderType.BROWSER_ESBUILD,
|
|
73
|
+
BuilderType.DEPLOY,
|
|
74
|
+
BuilderType.BROWSER,
|
|
75
|
+
BuilderType.PRERENDER,
|
|
70
76
|
...(purpose === "deploy" ? [] : DEV_SERVER_TARGETS),
|
|
71
77
|
];
|
|
72
78
|
}
|
|
73
79
|
async function getAllTargets(purpose, dir) {
|
|
74
|
-
const
|
|
80
|
+
const validBuilderTypes = getValidBuilderTypes(purpose);
|
|
75
81
|
const [{ NodeJsAsyncHost }, { workspaces }, { targetStringFromTarget }] = await Promise.all([
|
|
76
82
|
(0, utils_1.relativeRequire)(dir, "@angular-devkit/core/node"),
|
|
77
83
|
(0, utils_1.relativeRequire)(dir, "@angular-devkit/core"),
|
|
@@ -84,8 +90,10 @@ async function getAllTargets(purpose, dir) {
|
|
|
84
90
|
if (projectDefinition.extensions.projectType !== "application")
|
|
85
91
|
return;
|
|
86
92
|
projectDefinition.targets.forEach((targetDefinition, target) => {
|
|
87
|
-
|
|
93
|
+
const builderType = getBuilderType(targetDefinition.builder);
|
|
94
|
+
if (builderType && !validBuilderTypes.includes(builderType)) {
|
|
88
95
|
return;
|
|
96
|
+
}
|
|
89
97
|
const configurations = Object.keys(targetDefinition.configurations || {});
|
|
90
98
|
if (!configurations.includes("production"))
|
|
91
99
|
configurations.push("production");
|
|
@@ -152,34 +160,32 @@ async function getContext(dir, targetOrConfiguration) {
|
|
|
152
160
|
throw new error_1.FirebaseError(`No project ${project} found.`);
|
|
153
161
|
if (overrideTarget) {
|
|
154
162
|
const target = workspaceProject.targets.get(overrideTarget.target);
|
|
155
|
-
const
|
|
156
|
-
switch (
|
|
157
|
-
case
|
|
163
|
+
const builderType = getBuilderType(target.builder);
|
|
164
|
+
switch (builderType) {
|
|
165
|
+
case BuilderType.DEPLOY:
|
|
158
166
|
deployTarget = overrideTarget;
|
|
159
167
|
break;
|
|
160
|
-
case
|
|
168
|
+
case BuilderType.APPLICATION:
|
|
161
169
|
buildTarget = overrideTarget;
|
|
162
170
|
break;
|
|
163
|
-
case
|
|
164
|
-
case
|
|
171
|
+
case BuilderType.BROWSER:
|
|
172
|
+
case BuilderType.BROWSER_ESBUILD:
|
|
165
173
|
browserTarget = overrideTarget;
|
|
166
174
|
break;
|
|
167
|
-
case
|
|
168
|
-
case "@nguniversal/builders:prerender":
|
|
175
|
+
case BuilderType.PRERENDER:
|
|
169
176
|
prerenderTarget = overrideTarget;
|
|
170
177
|
break;
|
|
171
|
-
case
|
|
172
|
-
case
|
|
173
|
-
case "@angular-devkit/build-angular:ssr-dev-server":
|
|
178
|
+
case BuilderType.DEV_SERVER:
|
|
179
|
+
case BuilderType.SSR_DEV_SERVER:
|
|
174
180
|
serveTarget = overrideTarget;
|
|
175
181
|
break;
|
|
176
182
|
default:
|
|
177
|
-
throw new error_1.FirebaseError(`builder ${
|
|
183
|
+
throw new error_1.FirebaseError(`builder type ${builderType} not known.`);
|
|
178
184
|
}
|
|
179
185
|
}
|
|
180
186
|
else if (workspaceProject.targets.has("deploy")) {
|
|
181
187
|
const { builder, defaultConfiguration = "production" } = workspaceProject.targets.get("deploy");
|
|
182
|
-
if (builder ===
|
|
188
|
+
if (getBuilderType(builder) === BuilderType.DEPLOY) {
|
|
183
189
|
deployTarget = {
|
|
184
190
|
project,
|
|
185
191
|
target: "deploy",
|
|
@@ -251,13 +257,13 @@ async function getContext(dir, targetOrConfiguration) {
|
|
|
251
257
|
}
|
|
252
258
|
if (!buildTarget && !browserTarget && workspaceProject.targets.has("build")) {
|
|
253
259
|
const { builder, defaultConfiguration = "production" } = workspaceProject.targets.get("build");
|
|
260
|
+
const builderType = getBuilderType(builder);
|
|
254
261
|
const target = {
|
|
255
262
|
project,
|
|
256
263
|
target: "build",
|
|
257
264
|
configuration: configuration || defaultConfiguration,
|
|
258
265
|
};
|
|
259
|
-
if (
|
|
260
|
-
builder === "@angular-devkit/build-angular:browser-esbuild") {
|
|
266
|
+
if (builderType === BuilderType.BROWSER || builderType === BuilderType.BROWSER_ESBUILD) {
|
|
261
267
|
browserTarget = target;
|
|
262
268
|
}
|
|
263
269
|
else {
|
|
@@ -307,27 +313,30 @@ async function getContext(dir, targetOrConfiguration) {
|
|
|
307
313
|
if (!definition)
|
|
308
314
|
throw new error_1.FirebaseError(`${target} could not be found in your angular.json`);
|
|
309
315
|
const { builder } = definition;
|
|
310
|
-
|
|
316
|
+
const builderType = getBuilderType(builder);
|
|
317
|
+
if (target === deployTarget && builderType === BuilderType.DEPLOY)
|
|
318
|
+
continue;
|
|
319
|
+
if (target === buildTarget && builderType === BuilderType.APPLICATION)
|
|
311
320
|
continue;
|
|
312
|
-
if (target === buildTarget &&
|
|
321
|
+
if (target === buildTarget && builderType === BuilderType.BROWSER)
|
|
313
322
|
continue;
|
|
314
|
-
if (target ===
|
|
323
|
+
if (target === browserTarget && builderType === BuilderType.BROWSER_ESBUILD)
|
|
315
324
|
continue;
|
|
316
|
-
if (target === browserTarget &&
|
|
325
|
+
if (target === browserTarget && builderType === BuilderType.BROWSER)
|
|
317
326
|
continue;
|
|
318
|
-
if (target === browserTarget &&
|
|
327
|
+
if (target === browserTarget && builderType === BuilderType.APPLICATION)
|
|
319
328
|
continue;
|
|
320
|
-
if (target === prerenderTarget &&
|
|
329
|
+
if (target === prerenderTarget && builderType === BuilderType.PRERENDER)
|
|
321
330
|
continue;
|
|
322
|
-
if (target === prerenderTarget &&
|
|
331
|
+
if (target === prerenderTarget && builderType === BuilderType.PRERENDER)
|
|
323
332
|
continue;
|
|
324
|
-
if (target === serverTarget &&
|
|
333
|
+
if (target === serverTarget && builderType === BuilderType.SERVER)
|
|
325
334
|
continue;
|
|
326
|
-
if (target === serveTarget &&
|
|
335
|
+
if (target === serveTarget && builderType === BuilderType.SSR_DEV_SERVER)
|
|
327
336
|
continue;
|
|
328
|
-
if (target === serveTarget &&
|
|
337
|
+
if (target === serveTarget && builderType === BuilderType.DEV_SERVER)
|
|
329
338
|
continue;
|
|
330
|
-
if (target === serveTarget &&
|
|
339
|
+
if (target === serveTarget && builderType === BuilderType.SERVER)
|
|
331
340
|
continue;
|
|
332
341
|
throw new error_1.FirebaseError(`${definition.builder} (${targetString}) is not a recognized builder. Please check your angular.json`);
|
|
333
342
|
}
|
|
@@ -373,7 +382,7 @@ async function getBrowserConfig(sourceDir, configuration) {
|
|
|
373
382
|
architectHost.getBuilderNameForTarget(buildOrBrowserTarget),
|
|
374
383
|
]);
|
|
375
384
|
(0, utils_2.assertIsString)(targetOptions === null || targetOptions === void 0 ? void 0 : targetOptions.outputPath);
|
|
376
|
-
const outputPath = (0, path_1.join)(targetOptions.outputPath, buildTarget && builderName ===
|
|
385
|
+
const outputPath = (0, path_1.join)(targetOptions.outputPath, buildTarget && getBuilderType(builderName) === BuilderType.APPLICATION ? "browser" : "");
|
|
377
386
|
return { locales, baseHref, outputPath, defaultLocale };
|
|
378
387
|
}
|
|
379
388
|
exports.getBrowserConfig = getBrowserConfig;
|
|
@@ -475,3 +484,12 @@ exports.tryToGetOptionsForTarget = tryToGetOptionsForTarget;
|
|
|
475
484
|
function throwCannotDetermineTarget(error) {
|
|
476
485
|
throw new error_1.FirebaseError(`Unable to determine the application to deploy, specify a target via the FIREBASE_FRAMEWORKS_BUILD_TARGET environment variable.`, { original: error });
|
|
477
486
|
}
|
|
487
|
+
function getBuilderType(builder) {
|
|
488
|
+
const colonIndex = builder.lastIndexOf(":");
|
|
489
|
+
const builderType = colonIndex >= 0 ? builder.slice(colonIndex + 1) : undefined;
|
|
490
|
+
if (!builderType || !Object.values(BuilderType).includes(builderType)) {
|
|
491
|
+
return null;
|
|
492
|
+
}
|
|
493
|
+
return builderType;
|
|
494
|
+
}
|
|
495
|
+
exports.getBuilderType = getBuilderType;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hasCleanupOptOut = exports.hasSameCleanupPolicy = exports.setCleanupPolicy = exports.optOutRepository = exports.updateRepository = exports.generateCleanupPolicy = exports.parseDaysFromPolicy = exports.daysToSeconds = exports.findExistingPolicy = exports.makeRepoPath = exports.DEFAULT_CLEANUP_DAYS = exports.OPT_OUT_LABEL_KEY = exports.CLEANUP_POLICY_ID = exports.GCF_REPO_ID = void 0;
|
|
4
|
+
const artifactregistry = require("../gcp/artifactregistry");
|
|
5
|
+
const error_1 = require("../error");
|
|
6
|
+
exports.GCF_REPO_ID = "gcf-artifacts";
|
|
7
|
+
exports.CLEANUP_POLICY_ID = "firebase-functions-cleanup";
|
|
8
|
+
exports.OPT_OUT_LABEL_KEY = "firebase-functions-cleanup-opted-out";
|
|
9
|
+
exports.DEFAULT_CLEANUP_DAYS = 1;
|
|
10
|
+
const SECONDS_IN_DAY = 24 * 60 * 60;
|
|
11
|
+
function makeRepoPath(projectId, location, repoName = exports.GCF_REPO_ID) {
|
|
12
|
+
return `projects/${projectId}/locations/${location}/repositories/${repoName}`;
|
|
13
|
+
}
|
|
14
|
+
exports.makeRepoPath = makeRepoPath;
|
|
15
|
+
function findExistingPolicy(repository) {
|
|
16
|
+
var _a;
|
|
17
|
+
return (_a = repository === null || repository === void 0 ? void 0 : repository.cleanupPolicies) === null || _a === void 0 ? void 0 : _a[exports.CLEANUP_POLICY_ID];
|
|
18
|
+
}
|
|
19
|
+
exports.findExistingPolicy = findExistingPolicy;
|
|
20
|
+
function daysToSeconds(days) {
|
|
21
|
+
const seconds = days * SECONDS_IN_DAY;
|
|
22
|
+
return `${seconds}s`;
|
|
23
|
+
}
|
|
24
|
+
exports.daysToSeconds = daysToSeconds;
|
|
25
|
+
function parseDaysFromPolicy(olderThan) {
|
|
26
|
+
const match = olderThan.match(/^(\d+)s$/);
|
|
27
|
+
if (match && match[1]) {
|
|
28
|
+
const seconds = parseInt(match[1], 10);
|
|
29
|
+
return Math.floor(seconds / SECONDS_IN_DAY);
|
|
30
|
+
}
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
exports.parseDaysFromPolicy = parseDaysFromPolicy;
|
|
34
|
+
function generateCleanupPolicy(daysToKeep) {
|
|
35
|
+
return {
|
|
36
|
+
[exports.CLEANUP_POLICY_ID]: {
|
|
37
|
+
id: exports.CLEANUP_POLICY_ID,
|
|
38
|
+
condition: {
|
|
39
|
+
tagState: "ANY",
|
|
40
|
+
olderThan: daysToSeconds(daysToKeep),
|
|
41
|
+
},
|
|
42
|
+
action: "DELETE",
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
exports.generateCleanupPolicy = generateCleanupPolicy;
|
|
47
|
+
async function updateRepository(repo) {
|
|
48
|
+
try {
|
|
49
|
+
await artifactregistry.updateRepository(repo);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
if (err.status === 403) {
|
|
53
|
+
throw new error_1.FirebaseError(`You don't have permission to update this repository.\n` +
|
|
54
|
+
`To update repository settings, ask your administrator to grant you the ` +
|
|
55
|
+
`Artifact Registry Administrator (roles/artifactregistry.admin) IAM role on the repository project.`, { original: err, exit: 1 });
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
throw new error_1.FirebaseError("Failed to update artifact registry repository", {
|
|
59
|
+
original: err,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
exports.updateRepository = updateRepository;
|
|
65
|
+
async function optOutRepository(repository) {
|
|
66
|
+
const policies = Object.assign({}, repository.cleanupPolicies);
|
|
67
|
+
if (exports.CLEANUP_POLICY_ID in policies) {
|
|
68
|
+
delete policies[exports.CLEANUP_POLICY_ID];
|
|
69
|
+
}
|
|
70
|
+
const update = {
|
|
71
|
+
name: repository.name,
|
|
72
|
+
labels: Object.assign(Object.assign({}, repository.labels), { [exports.OPT_OUT_LABEL_KEY]: "true" }),
|
|
73
|
+
cleanupPolicies: policies,
|
|
74
|
+
};
|
|
75
|
+
await exports.updateRepository(update);
|
|
76
|
+
}
|
|
77
|
+
exports.optOutRepository = optOutRepository;
|
|
78
|
+
async function setCleanupPolicy(repository, daysToKeep) {
|
|
79
|
+
const labels = Object.assign({}, repository.labels);
|
|
80
|
+
delete labels[exports.OPT_OUT_LABEL_KEY];
|
|
81
|
+
const update = {
|
|
82
|
+
name: repository.name,
|
|
83
|
+
cleanupPolicies: Object.assign(Object.assign({}, repository.cleanupPolicies), generateCleanupPolicy(daysToKeep)),
|
|
84
|
+
labels,
|
|
85
|
+
};
|
|
86
|
+
await exports.updateRepository(update);
|
|
87
|
+
}
|
|
88
|
+
exports.setCleanupPolicy = setCleanupPolicy;
|
|
89
|
+
function hasSameCleanupPolicy(repository, daysToKeep) {
|
|
90
|
+
var _a, _b;
|
|
91
|
+
const existingPolicy = findExistingPolicy(repository);
|
|
92
|
+
if (!existingPolicy) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if (((_a = existingPolicy.condition) === null || _a === void 0 ? void 0 : _a.tagState) !== "ANY" || !((_b = existingPolicy.condition) === null || _b === void 0 ? void 0 : _b.olderThan)) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
const existingSeconds = parseDaysFromPolicy(existingPolicy.condition.olderThan);
|
|
99
|
+
return existingSeconds === daysToKeep;
|
|
100
|
+
}
|
|
101
|
+
exports.hasSameCleanupPolicy = hasSameCleanupPolicy;
|
|
102
|
+
function hasCleanupOptOut(repo) {
|
|
103
|
+
return !!(repo.labels && repo.labels[exports.OPT_OUT_LABEL_KEY] === "true");
|
|
104
|
+
}
|
|
105
|
+
exports.hasCleanupOptOut = hasCleanupOptOut;
|