firebase-tools 15.10.1 → 15.11.0
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/appdistribution/client.js +17 -0
- package/lib/apptesting/parseTestFiles.js +11 -8
- package/lib/commands/apptesting.js +16 -7
- package/lib/commands/studio-export.js +2 -2
- package/lib/deploy/firestore/prepare.js +17 -0
- package/lib/deploy/functions/runtimes/dart/index.js +282 -0
- package/lib/deploy/functions/runtimes/index.js +1 -1
- package/lib/deploy/functions/runtimes/supported/index.js +4 -0
- package/lib/emulator/downloadableEmulatorInfo.json +30 -30
- package/lib/emulator/functionsEmulator.js +103 -24
- package/lib/emulator/functionsRuntimeWorker.js +21 -18
- package/lib/firebase_studio/migrate.js +69 -25
- package/lib/init/features/functions/dart.js +31 -0
- package/lib/init/features/functions/index.js +14 -0
- package/lib/tsconfig.publish.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/schema/firebase-config.json +7 -0
- package/templates/firebase-studio-export/readme_template.md +2 -0
- package/templates/init/functions/dart/_gitignore +11 -0
- package/templates/init/functions/dart/pubspec.yaml +14 -0
- package/templates/init/functions/dart/server.dart +15 -0
- package/lib/deploy/functions/runtimes/dart.js +0 -42
|
@@ -298,5 +298,22 @@ class AppDistributionClient {
|
|
|
298
298
|
throw new error_1.FirebaseError(`Failed to upsert test cases ${(0, error_1.getErrMsg)(err)}`);
|
|
299
299
|
}
|
|
300
300
|
}
|
|
301
|
+
async getLatestRelease(appName) {
|
|
302
|
+
try {
|
|
303
|
+
const response = await this.appDistroV1Client.get(`/${appName}/releases`, {
|
|
304
|
+
queryParams: {
|
|
305
|
+
pageSize: "1",
|
|
306
|
+
orderBy: "createTime desc",
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
if (!response.body.releases?.length) {
|
|
310
|
+
return undefined;
|
|
311
|
+
}
|
|
312
|
+
return response.body.releases[0];
|
|
313
|
+
}
|
|
314
|
+
catch (err) {
|
|
315
|
+
throw new error_1.FirebaseError(`Failed to get latest release for app ${appName}: ${(0, error_1.getErrMsg)(err)}`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
301
318
|
}
|
|
302
319
|
exports.AppDistributionClient = AppDistributionClient;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseTestFiles = parseTestFiles;
|
|
4
|
+
exports.pluralizeTests = pluralizeTests;
|
|
4
5
|
const fsutils_1 = require("../fsutils");
|
|
5
6
|
const path_1 = require("path");
|
|
6
7
|
const logger_1 = require("../logger");
|
|
@@ -77,26 +78,25 @@ function createFilter(pattern, context) {
|
|
|
77
78
|
async function parseTestFilesRecursive(params) {
|
|
78
79
|
const testDir = params.testDir;
|
|
79
80
|
const targetUri = params.targetUri;
|
|
80
|
-
const
|
|
81
|
+
const filenames = (0, fsutils_1.listFiles)(testDir);
|
|
81
82
|
const results = [];
|
|
82
|
-
for (const
|
|
83
|
-
const path = (0, path_1.join)(testDir,
|
|
83
|
+
for (const filename of filenames) {
|
|
84
|
+
const path = (0, path_1.join)(testDir, filename);
|
|
84
85
|
if ((0, fsutils_1.dirExistsSync)(path)) {
|
|
85
86
|
results.push(...(await parseTestFilesRecursive({ testDir: path, targetUri })));
|
|
86
87
|
}
|
|
87
|
-
else if ((0, fsutils_1.fileExistsSync)(path)) {
|
|
88
|
+
else if ((0, fsutils_1.fileExistsSync)(path) && (path.endsWith(".yaml") || path.endsWith(".yml"))) {
|
|
88
89
|
try {
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
logger_1.logger.debug(`Reading ${path}.`);
|
|
91
|
+
const file = await (0, utils_1.readFileFromDirectory)(testDir, filename);
|
|
91
92
|
const parsedFile = (0, utils_1.wrappedSafeLoad)(file.source);
|
|
92
|
-
logger_1.logger.debug(`Parsed the file.`);
|
|
93
93
|
const tests = parsedFile.tests;
|
|
94
|
-
logger_1.logger.debug(`There are ${tests.length} tests.`);
|
|
95
94
|
const defaultConfig = parsedFile.defaultConfig;
|
|
96
95
|
if (!tests || !tests.length) {
|
|
97
96
|
logger_1.logger.debug(`No tests found in ${path}. Ignoring.`);
|
|
98
97
|
continue;
|
|
99
98
|
}
|
|
99
|
+
logger_1.logger.debug(`File contains ${pluralizeTests(tests.length)}.`);
|
|
100
100
|
const invocations = [];
|
|
101
101
|
for (const rawTestDef of tests) {
|
|
102
102
|
const invocation = toTestCaseInvocation(rawTestDef, targetUri, defaultConfig);
|
|
@@ -114,6 +114,9 @@ async function parseTestFilesRecursive(params) {
|
|
|
114
114
|
}
|
|
115
115
|
return results;
|
|
116
116
|
}
|
|
117
|
+
function pluralizeTests(numTests) {
|
|
118
|
+
return `${numTests} test${numTests === 1 ? "" : "s"}`;
|
|
119
|
+
}
|
|
117
120
|
function toTestCaseInvocation(testDef, targetUri, defaultConfig) {
|
|
118
121
|
const steps = testDef.steps ?? [];
|
|
119
122
|
const route = testDef.testConfig?.route ?? defaultConfig?.route ?? "";
|
|
@@ -20,7 +20,7 @@ const defaultDevices = [
|
|
|
20
20
|
orientation: "portrait",
|
|
21
21
|
},
|
|
22
22
|
];
|
|
23
|
-
exports.command = new command_1.Command("apptesting:execute
|
|
23
|
+
exports.command = new command_1.Command("apptesting:execute [release-binary-file]")
|
|
24
24
|
.description("Run mobile automated tests written in natural language driven by AI")
|
|
25
25
|
.option("--app <app_id>", "The app id of your Firebase web app. Optional if the project contains exactly one web app.")
|
|
26
26
|
.option("--test-file-pattern <pattern>", "Test file pattern. Only tests contained in files that match this pattern will be executed.")
|
|
@@ -39,17 +39,29 @@ exports.command = new command_1.Command("apptesting:execute <release-binary-file
|
|
|
39
39
|
const tests = await (0, parseTestFiles_1.parseTestFiles)(testDir, undefined, options.testFilePattern, options.testNamePattern);
|
|
40
40
|
const testDevices = (0, options_parser_util_1.parseTestDevices)(options.testDevices, options.testDevicesFile);
|
|
41
41
|
if (!tests.length) {
|
|
42
|
-
throw new error_1.FirebaseError(
|
|
42
|
+
throw new error_1.FirebaseError(`No tests found under test directory ${testDir}`);
|
|
43
43
|
}
|
|
44
|
+
utils.logBullet(`Found ${(0, parseTestFiles_1.pluralizeTests)(tests.length)} to run under test directory ${testDir}`);
|
|
44
45
|
const invokeSpinner = ora("Requesting test execution");
|
|
45
46
|
const client = new client_1.AppDistributionClient();
|
|
46
47
|
let releaseTests;
|
|
47
48
|
let release;
|
|
48
49
|
try {
|
|
49
|
-
|
|
50
|
+
if (target) {
|
|
51
|
+
release = await (0, distribution_1.upload)(client, appName, new distribution_1.Distribution(target));
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
utils.logBullet("release-binary-file not provided, using the latest App Distribution release.");
|
|
55
|
+
const latestRelease = await client.getLatestRelease(appName);
|
|
56
|
+
if (!latestRelease) {
|
|
57
|
+
throw new error_1.FirebaseError(`No app binary found for ${appName}. Call apptesting:execute with a local app binary file, or upload a release to App Distribution.`);
|
|
58
|
+
}
|
|
59
|
+
release = latestRelease;
|
|
60
|
+
utils.logBullet(`Using release ${release.displayVersion} created at ${release.createTime}`);
|
|
61
|
+
}
|
|
50
62
|
invokeSpinner.start();
|
|
51
63
|
releaseTests = await invokeTests(client, release.name, tests, !testDevices.length ? defaultDevices : testDevices);
|
|
52
|
-
invokeSpinner.text = `${pluralizeTests(releaseTests.length)} started successfully!`;
|
|
64
|
+
invokeSpinner.text = `${(0, parseTestFiles_1.pluralizeTests)(releaseTests.length)} started successfully!`;
|
|
53
65
|
invokeSpinner.succeed();
|
|
54
66
|
}
|
|
55
67
|
catch (ex) {
|
|
@@ -64,9 +76,6 @@ exports.command = new command_1.Command("apptesting:execute <release-binary-file
|
|
|
64
76
|
utils.logBullet(`View detailed results in the Firebase Console:\n${release.firebaseConsoleUri}`);
|
|
65
77
|
}
|
|
66
78
|
});
|
|
67
|
-
function pluralizeTests(numTests) {
|
|
68
|
-
return `${numTests} test${numTests === 1 ? "" : "s"}`;
|
|
69
|
-
}
|
|
70
79
|
async function invokeTests(client, releaseName, testDefs, devices) {
|
|
71
80
|
try {
|
|
72
81
|
const releaseTests = [];
|
|
@@ -8,12 +8,12 @@ const path = require("path");
|
|
|
8
8
|
const error_1 = require("../error");
|
|
9
9
|
const unzip_1 = require("../unzip");
|
|
10
10
|
const fs = require("fs");
|
|
11
|
-
exports.command = new command_1.Command("studio:export
|
|
11
|
+
exports.command = new command_1.Command("studio:export [path]")
|
|
12
12
|
.description("Bootstrap Firebase Studio apps for migration to Antigravity. Run on the unzipped folder from the Firebase Studio download, or directly on the downloaded zip file.")
|
|
13
13
|
.option("--no-start-antigravity", "skip starting the Antigravity IDE after migration")
|
|
14
14
|
.action(async (exportPath, options) => {
|
|
15
15
|
if (!exportPath) {
|
|
16
|
-
throw new error_1.FirebaseError("Must specify
|
|
16
|
+
throw new error_1.FirebaseError("Must specify the path to the Firebase Studio downloaded zip file or the unzipped folder path.", { exit: 1 });
|
|
17
17
|
}
|
|
18
18
|
let rootPath = path.resolve(exportPath);
|
|
19
19
|
if (fs.existsSync(rootPath) && fs.statSync(rootPath).isFile() && rootPath.endsWith(".zip")) {
|
|
@@ -52,6 +52,21 @@ async function createDatabase(context, options) {
|
|
|
52
52
|
}
|
|
53
53
|
edition = upperEdition;
|
|
54
54
|
}
|
|
55
|
+
let firestoreDataAccessMode;
|
|
56
|
+
let mongodbCompatibleDataAccessMode;
|
|
57
|
+
if (firestoreCfg.dataAccessMode) {
|
|
58
|
+
if (edition !== types.DatabaseEdition.ENTERPRISE) {
|
|
59
|
+
throw new error_1.FirebaseError("dataAccessMode can only be specified for enterprise edition databases.");
|
|
60
|
+
}
|
|
61
|
+
if (firestoreCfg.dataAccessMode === "FIRESTORE_NATIVE") {
|
|
62
|
+
firestoreDataAccessMode = types.DataAccessMode.ENABLED;
|
|
63
|
+
mongodbCompatibleDataAccessMode = types.DataAccessMode.DISABLED;
|
|
64
|
+
}
|
|
65
|
+
else if (firestoreCfg.dataAccessMode === "MONGODB_COMPATIBLE") {
|
|
66
|
+
firestoreDataAccessMode = types.DataAccessMode.DISABLED;
|
|
67
|
+
mongodbCompatibleDataAccessMode = types.DataAccessMode.ENABLED;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
55
70
|
const api = new api_2.FirestoreApi();
|
|
56
71
|
try {
|
|
57
72
|
await api.getDatabase(options.projectId, firestoreCfg.database);
|
|
@@ -67,6 +82,8 @@ async function createDatabase(context, options) {
|
|
|
67
82
|
databaseEdition: edition,
|
|
68
83
|
deleteProtectionState: types.DatabaseDeleteProtectionState.DISABLED,
|
|
69
84
|
pointInTimeRecoveryEnablement: types.PointInTimeRecoveryEnablement.DISABLED,
|
|
85
|
+
firestoreDataAccessMode,
|
|
86
|
+
mongodbCompatibleDataAccessMode,
|
|
70
87
|
};
|
|
71
88
|
await api.createDatabase(createDatabaseReq);
|
|
72
89
|
}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Delegate = exports.DART_ENTRY_POINT = void 0;
|
|
4
|
+
exports.tryCreateDelegate = tryCreateDelegate;
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const util_1 = require("util");
|
|
8
|
+
const spawn = require("cross-spawn");
|
|
9
|
+
const discovery = require("../discovery");
|
|
10
|
+
const supported = require("../supported");
|
|
11
|
+
const logger_1 = require("../../../../logger");
|
|
12
|
+
const error_1 = require("../../../../error");
|
|
13
|
+
const utils_1 = require("../../../../utils");
|
|
14
|
+
const registry_1 = require("../../../../emulator/registry");
|
|
15
|
+
const types_1 = require("../../../../emulator/types");
|
|
16
|
+
async function tryCreateDelegate(context) {
|
|
17
|
+
const pubspecYamlPath = path.join(context.sourceDir, "pubspec.yaml");
|
|
18
|
+
if (!(await (0, util_1.promisify)(fs.exists)(pubspecYamlPath))) {
|
|
19
|
+
logger_1.logger.debug("Customer code is not Dart code.");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const runtime = context.runtime ?? supported.latest("dart");
|
|
23
|
+
if (!supported.isRuntime(runtime)) {
|
|
24
|
+
throw new error_1.FirebaseError(`Runtime ${runtime} is not a valid Dart runtime`);
|
|
25
|
+
}
|
|
26
|
+
if (!supported.runtimeIsLanguage(runtime, "dart")) {
|
|
27
|
+
throw new error_1.FirebaseError(`Internal error. Trying to construct a dart runtime delegate for runtime ${runtime}`, { exit: 1 });
|
|
28
|
+
}
|
|
29
|
+
return Promise.resolve(new Delegate(context.projectId, context.sourceDir, runtime));
|
|
30
|
+
}
|
|
31
|
+
const MIN_DART_SDK_VERSION = "3.8.0";
|
|
32
|
+
exports.DART_ENTRY_POINT = "bin/server.dart";
|
|
33
|
+
class Delegate {
|
|
34
|
+
constructor(projectId, sourceDir, runtime) {
|
|
35
|
+
this.projectId = projectId;
|
|
36
|
+
this.sourceDir = sourceDir;
|
|
37
|
+
this.runtime = runtime;
|
|
38
|
+
this.language = "dart";
|
|
39
|
+
this.bin = "dart";
|
|
40
|
+
this.entryPoint = exports.DART_ENTRY_POINT;
|
|
41
|
+
this.buildRunnerProcess = null;
|
|
42
|
+
}
|
|
43
|
+
async validate() {
|
|
44
|
+
const result = spawn.sync(this.bin, ["--version"], {
|
|
45
|
+
encoding: "utf8",
|
|
46
|
+
timeout: 10000,
|
|
47
|
+
});
|
|
48
|
+
if (result.error) {
|
|
49
|
+
throw new error_1.FirebaseError(`Could not find a Dart SDK. Make sure the '${this.bin}' command is available on your PATH.`);
|
|
50
|
+
}
|
|
51
|
+
const versionOutput = (result.stdout || result.stderr || "").toString();
|
|
52
|
+
const match = /Dart SDK version:\s*(\d+\.\d+\.\d+)/.exec(versionOutput);
|
|
53
|
+
if (match) {
|
|
54
|
+
const installedVersion = match[1];
|
|
55
|
+
if (installedVersion.localeCompare(MIN_DART_SDK_VERSION, undefined, { numeric: true }) < 0) {
|
|
56
|
+
throw new error_1.FirebaseError(`Dart SDK version ${installedVersion} is not supported. ` +
|
|
57
|
+
`Firebase Functions for Dart requires Dart ${MIN_DART_SDK_VERSION} or later. ` +
|
|
58
|
+
`Please upgrade your Dart SDK.`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
logger_1.logger.debug(`Could not parse Dart SDK version from: ${versionOutput}`);
|
|
63
|
+
}
|
|
64
|
+
const pubspecYamlPath = path.join(this.sourceDir, "pubspec.yaml");
|
|
65
|
+
try {
|
|
66
|
+
await fs.promises.access(pubspecYamlPath, fs.constants.R_OK);
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
throw new error_1.FirebaseError(`Failed to read pubspec.yaml at ${pubspecYamlPath}: ${err.message}`);
|
|
70
|
+
}
|
|
71
|
+
const entryPointPath = path.join(this.sourceDir, this.entryPoint);
|
|
72
|
+
try {
|
|
73
|
+
await fs.promises.access(entryPointPath, fs.constants.R_OK);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
throw new error_1.FirebaseError(`Could not find entry point at ${entryPointPath}. ` +
|
|
77
|
+
`Firebase Functions for Dart expects your main function in ${this.entryPoint}.`);
|
|
78
|
+
}
|
|
79
|
+
const packageConfigPath = path.join(this.sourceDir, ".dart_tool", "package_config.json");
|
|
80
|
+
try {
|
|
81
|
+
await fs.promises.access(packageConfigPath, fs.constants.R_OK);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
(0, utils_1.logLabeledBullet)("functions", "running dart pub get...");
|
|
85
|
+
const pubGetProcess = spawn(this.bin, ["pub", "get"], {
|
|
86
|
+
cwd: this.sourceDir,
|
|
87
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
88
|
+
});
|
|
89
|
+
pubGetProcess.stdout?.on("data", (chunk) => {
|
|
90
|
+
logger_1.logger.debug(`[dart pub get] ${chunk.toString("utf8").trim()}`);
|
|
91
|
+
});
|
|
92
|
+
pubGetProcess.stderr?.on("data", (chunk) => {
|
|
93
|
+
logger_1.logger.debug(`[dart pub get] ${chunk.toString("utf8").trim()}`);
|
|
94
|
+
});
|
|
95
|
+
await new Promise((resolve, reject) => {
|
|
96
|
+
pubGetProcess.on("exit", (code) => {
|
|
97
|
+
if (code === 0 || code === null) {
|
|
98
|
+
resolve();
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
reject(new error_1.FirebaseError(`dart pub get failed with exit code ${code}. ` +
|
|
102
|
+
`Make sure your pubspec.yaml is valid and dependencies are available.`));
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
pubGetProcess.on("error", reject);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async build() {
|
|
110
|
+
if (Delegate.watchModeActive) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
(0, utils_1.logLabeledBullet)("functions", "running build_runner...");
|
|
114
|
+
const buildRunnerProcess = spawn(this.bin, ["run", "build_runner", "build", "--delete-conflicting-outputs"], {
|
|
115
|
+
cwd: this.sourceDir,
|
|
116
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
117
|
+
});
|
|
118
|
+
buildRunnerProcess.stdout?.on("data", (chunk) => {
|
|
119
|
+
logger_1.logger.debug(`[build_runner] ${chunk.toString("utf8").trim()}`);
|
|
120
|
+
});
|
|
121
|
+
buildRunnerProcess.stderr?.on("data", (chunk) => {
|
|
122
|
+
logger_1.logger.debug(`[build_runner] ${chunk.toString("utf8").trim()}`);
|
|
123
|
+
});
|
|
124
|
+
await new Promise((resolve, reject) => {
|
|
125
|
+
buildRunnerProcess.on("exit", (code) => {
|
|
126
|
+
if (code === 0 || code === null) {
|
|
127
|
+
resolve();
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
reject(new error_1.FirebaseError(`build_runner failed with exit code ${code}. ` +
|
|
131
|
+
`Make sure your Dart project is properly configured.`));
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
buildRunnerProcess.on("error", reject);
|
|
135
|
+
});
|
|
136
|
+
if (registry_1.EmulatorRegistry.isRunning(types_1.Emulators.FUNCTIONS)) {
|
|
137
|
+
logger_1.logger.debug("Skipping Dart compilation in emulator mode.");
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const binDir = path.join(this.sourceDir, "bin");
|
|
141
|
+
await fs.promises.mkdir(binDir, { recursive: true });
|
|
142
|
+
(0, utils_1.logLabeledBullet)("functions", "compiling Dart to linux-x64 executable...");
|
|
143
|
+
const compileProcess = spawn(this.bin, [
|
|
144
|
+
"compile",
|
|
145
|
+
"exe",
|
|
146
|
+
this.entryPoint,
|
|
147
|
+
"-o",
|
|
148
|
+
"bin/server",
|
|
149
|
+
"--target-os=linux",
|
|
150
|
+
"--target-arch=x64",
|
|
151
|
+
], {
|
|
152
|
+
cwd: this.sourceDir,
|
|
153
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
154
|
+
});
|
|
155
|
+
compileProcess.stdout?.on("data", (chunk) => {
|
|
156
|
+
logger_1.logger.debug(`[dart compile] ${chunk.toString("utf8").trim()}`);
|
|
157
|
+
});
|
|
158
|
+
compileProcess.stderr?.on("data", (chunk) => {
|
|
159
|
+
logger_1.logger.debug(`[dart compile] ${chunk.toString("utf8").trim()}`);
|
|
160
|
+
});
|
|
161
|
+
await new Promise((resolve, reject) => {
|
|
162
|
+
compileProcess.on("exit", (code) => {
|
|
163
|
+
if (code === 0 || code === null) {
|
|
164
|
+
resolve();
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
reject(new error_1.FirebaseError(`Dart compilation failed with exit code ${code}. ` +
|
|
168
|
+
`Make sure your Dart project compiles successfully with: ` +
|
|
169
|
+
`dart compile exe ${this.entryPoint} --target-os=linux --target-arch=x64`));
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
compileProcess.on("error", reject);
|
|
173
|
+
});
|
|
174
|
+
(0, utils_1.logLabeledBullet)("functions", "Dart compilation complete.");
|
|
175
|
+
}
|
|
176
|
+
async watch(onRebuild) {
|
|
177
|
+
Delegate.watchModeActive = true;
|
|
178
|
+
logger_1.logger.debug("Starting build_runner watch for Dart functions...");
|
|
179
|
+
const buildRunnerProcess = spawn(this.bin, ["run", "build_runner", "watch", "--delete-conflicting-outputs"], {
|
|
180
|
+
cwd: this.sourceDir,
|
|
181
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
182
|
+
});
|
|
183
|
+
this.buildRunnerProcess = buildRunnerProcess;
|
|
184
|
+
let initialBuildComplete = false;
|
|
185
|
+
let resolveInitialBuild;
|
|
186
|
+
let rejectInitialBuild;
|
|
187
|
+
const initialBuildPromise = new Promise((resolve, reject) => {
|
|
188
|
+
resolveInitialBuild = resolve;
|
|
189
|
+
rejectInitialBuild = reject;
|
|
190
|
+
});
|
|
191
|
+
const buildCompletePattern = /Succeeded after|Built with build_runner/;
|
|
192
|
+
buildRunnerProcess.stdout?.on("data", (chunk) => {
|
|
193
|
+
const output = chunk.toString("utf8").trim();
|
|
194
|
+
if (output) {
|
|
195
|
+
logger_1.logger.debug(`[build_runner] ${output}`);
|
|
196
|
+
if (buildCompletePattern.test(output)) {
|
|
197
|
+
if (!initialBuildComplete) {
|
|
198
|
+
initialBuildComplete = true;
|
|
199
|
+
logger_1.logger.debug("build_runner initial build completed");
|
|
200
|
+
resolveInitialBuild();
|
|
201
|
+
}
|
|
202
|
+
else if (onRebuild) {
|
|
203
|
+
onRebuild();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
buildRunnerProcess.stderr?.on("data", (chunk) => {
|
|
209
|
+
const output = chunk.toString("utf8").trim();
|
|
210
|
+
if (output) {
|
|
211
|
+
logger_1.logger.debug(`[build_runner] ${output}`);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
buildRunnerProcess.on("exit", (code) => {
|
|
215
|
+
if (code !== 0 && code !== null) {
|
|
216
|
+
logger_1.logger.debug(`build_runner exited with code ${code}. Initial build failed.`);
|
|
217
|
+
if (!initialBuildComplete) {
|
|
218
|
+
rejectInitialBuild(new error_1.FirebaseError(`build_runner exited with code ${code}. Your Dart functions may not be deployed or emulated correctly.`));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
this.buildRunnerProcess = null;
|
|
222
|
+
});
|
|
223
|
+
buildRunnerProcess.on("error", (err) => {
|
|
224
|
+
logger_1.logger.debug(`Failed to start build_runner: ${err.message}. Your Dart functions may not be deployed or emulated correctly.`);
|
|
225
|
+
if (!initialBuildComplete) {
|
|
226
|
+
rejectInitialBuild(err);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
await initialBuildPromise;
|
|
230
|
+
return async () => {
|
|
231
|
+
if (this.buildRunnerProcess && !this.buildRunnerProcess.killed) {
|
|
232
|
+
this.buildRunnerProcess.kill("SIGTERM");
|
|
233
|
+
this.buildRunnerProcess = null;
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
async discoverBuild(_configValues, envs) {
|
|
238
|
+
const yamlDir = this.sourceDir;
|
|
239
|
+
const yamlPath = path.join(yamlDir, "functions.yaml");
|
|
240
|
+
let discovered = await discovery.detectFromYaml(yamlDir, this.projectId, this.runtime);
|
|
241
|
+
if (!discovered) {
|
|
242
|
+
logger_1.logger.debug("functions.yaml not found, running build_runner to generate it...");
|
|
243
|
+
const buildRunnerProcess = spawn(this.bin, ["run", "build_runner", "build"], {
|
|
244
|
+
cwd: this.sourceDir,
|
|
245
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
246
|
+
});
|
|
247
|
+
buildRunnerProcess.stdout?.on("data", (chunk) => {
|
|
248
|
+
logger_1.logger.debug(`[build_runner] ${chunk.toString("utf8")}`);
|
|
249
|
+
});
|
|
250
|
+
buildRunnerProcess.stderr?.on("data", (chunk) => {
|
|
251
|
+
logger_1.logger.debug(`[build_runner] ${chunk.toString("utf8")}`);
|
|
252
|
+
});
|
|
253
|
+
await new Promise((resolve, reject) => {
|
|
254
|
+
buildRunnerProcess.on("exit", (code) => {
|
|
255
|
+
if (code === 0 || code === null) {
|
|
256
|
+
resolve();
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
reject(new error_1.FirebaseError(`build_runner failed with exit code ${code}. Make sure your Dart project is properly configured.`));
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
buildRunnerProcess.on("error", reject);
|
|
263
|
+
});
|
|
264
|
+
discovered = await discovery.detectFromYaml(yamlDir, this.projectId, this.runtime);
|
|
265
|
+
if (!discovered) {
|
|
266
|
+
throw new error_1.FirebaseError(`Could not find functions.yaml at ${yamlPath} after running build_runner. ` +
|
|
267
|
+
`Make sure your Dart project is properly configured with firebase_functions.`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const isEmulator = envs.FUNCTIONS_EMULATOR === "true";
|
|
271
|
+
if (!isEmulator) {
|
|
272
|
+
for (const ep of Object.values(discovered.endpoints)) {
|
|
273
|
+
if (ep.platform === "gcfv2") {
|
|
274
|
+
ep.platform = "run";
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return discovered;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
exports.Delegate = Delegate;
|
|
282
|
+
Delegate.watchModeActive = false;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getRuntimeDelegate = getRuntimeDelegate;
|
|
4
|
+
const dart = require("./dart");
|
|
4
5
|
const node = require("./node");
|
|
5
6
|
const python = require("./python");
|
|
6
7
|
const validate = require("../validate");
|
|
7
8
|
const error_1 = require("../../../error");
|
|
8
9
|
const supported = require("./supported");
|
|
9
|
-
const dart = require("./dart");
|
|
10
10
|
const experiments = require("../../../experiments");
|
|
11
11
|
const factories = [
|
|
12
12
|
node.tryCreateDelegate,
|
|
@@ -16,6 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
exports.isRuntime = isRuntime;
|
|
18
18
|
exports.runtimeIsLanguage = runtimeIsLanguage;
|
|
19
|
+
exports.isLanguageRuntime = isLanguageRuntime;
|
|
19
20
|
exports.latest = latest;
|
|
20
21
|
exports.isDecommissioned = isDecommissioned;
|
|
21
22
|
exports.guardVersionSupport = guardVersionSupport;
|
|
@@ -29,6 +30,9 @@ function isRuntime(maybe) {
|
|
|
29
30
|
function runtimeIsLanguage(runtime, language) {
|
|
30
31
|
return runtime.startsWith(language);
|
|
31
32
|
}
|
|
33
|
+
function isLanguageRuntime(runtime, language) {
|
|
34
|
+
return !!runtime && runtime.startsWith(language);
|
|
35
|
+
}
|
|
32
36
|
function latest(language, runtimes = Object.keys(types_1.RUNTIMES)) {
|
|
33
37
|
const sorted = runtimes
|
|
34
38
|
.filter((s) => runtimeIsLanguage(s, language))
|
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
"downloadPathRelativeToCacheDir": "firebase-database-emulator-v4.11.2.jar"
|
|
9
9
|
},
|
|
10
10
|
"firestore": {
|
|
11
|
-
"version": "1.20.
|
|
12
|
-
"expectedSize":
|
|
13
|
-
"expectedChecksum": "
|
|
14
|
-
"expectedChecksumSHA256": "
|
|
15
|
-
"remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v1.20.
|
|
16
|
-
"downloadPathRelativeToCacheDir": "cloud-firestore-emulator-v1.20.
|
|
11
|
+
"version": "1.20.4",
|
|
12
|
+
"expectedSize": 136551710,
|
|
13
|
+
"expectedChecksum": "8397dac992e02454914dec28d23b6ed8",
|
|
14
|
+
"expectedChecksumSHA256": "49ee7814741b481ae5becfdaa7b55693bdf7a03363cd184e35c36c1595a9643",
|
|
15
|
+
"remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v1.20.4.jar",
|
|
16
|
+
"downloadPathRelativeToCacheDir": "cloud-firestore-emulator-v1.20.4.jar"
|
|
17
17
|
},
|
|
18
18
|
"storage": {
|
|
19
19
|
"version": "1.1.3",
|
|
@@ -54,36 +54,36 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dataconnect": {
|
|
56
56
|
"darwin": {
|
|
57
|
-
"version": "3.
|
|
58
|
-
"expectedSize":
|
|
59
|
-
"expectedChecksum": "
|
|
60
|
-
"expectedChecksumSHA256": "
|
|
61
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.
|
|
62
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.
|
|
57
|
+
"version": "3.3.0",
|
|
58
|
+
"expectedSize": 32051312,
|
|
59
|
+
"expectedChecksum": "9c9d9fb375826d90f9a57ce7a1770afc",
|
|
60
|
+
"expectedChecksumSHA256": "ae2fe4f4b9a79b6bfd52ef227dcdb5e58c5b0e3a662a3874abc07c73d2e8f8e4",
|
|
61
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.3.0",
|
|
62
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.0"
|
|
63
63
|
},
|
|
64
64
|
"darwin_arm64": {
|
|
65
|
-
"version": "3.
|
|
66
|
-
"expectedSize":
|
|
67
|
-
"expectedChecksum": "
|
|
68
|
-
"expectedChecksumSHA256": "
|
|
69
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.
|
|
70
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.
|
|
65
|
+
"version": "3.3.0",
|
|
66
|
+
"expectedSize": 30237490,
|
|
67
|
+
"expectedChecksum": "cd6e0b50e7614edd420a44cde1a6de54",
|
|
68
|
+
"expectedChecksumSHA256": "1524161c428f6c97ae1e4eb6371028bace2280f1090ebe89e73313afb30b7bdc",
|
|
69
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.3.0",
|
|
70
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.0"
|
|
71
71
|
},
|
|
72
72
|
"win32": {
|
|
73
|
-
"version": "3.
|
|
74
|
-
"expectedSize":
|
|
75
|
-
"expectedChecksum": "
|
|
76
|
-
"expectedChecksumSHA256": "
|
|
77
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.
|
|
78
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.
|
|
73
|
+
"version": "3.3.0",
|
|
74
|
+
"expectedSize": 32093696,
|
|
75
|
+
"expectedChecksum": "e2b6a90994609e613aae189fb6bb732b",
|
|
76
|
+
"expectedChecksumSHA256": "ddf44b362658a43ffb97c23e605e26d257e786adb9a71466ebd756b36c4dd212",
|
|
77
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.3.0",
|
|
78
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.0.exe"
|
|
79
79
|
},
|
|
80
80
|
"linux": {
|
|
81
|
-
"version": "3.
|
|
82
|
-
"expectedSize":
|
|
83
|
-
"expectedChecksum": "
|
|
84
|
-
"expectedChecksumSHA256": "
|
|
85
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.
|
|
86
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.
|
|
81
|
+
"version": "3.3.0",
|
|
82
|
+
"expectedSize": 31211704,
|
|
83
|
+
"expectedChecksum": "48b40f31c1897afd39e3b256c356730a",
|
|
84
|
+
"expectedChecksumSHA256": "e6774287f3a3ea7ee240f524644e0b0441720dd5d55d6a970a6c4db9923b517f",
|
|
85
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.3.0",
|
|
86
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.0"
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
}
|