Skip to content

Commit 6d2b00b

Browse files
authored
Merge branch 'main' into leoortizz_nextjsintegrationtests
2 parents 4a581df + 1738d20 commit 6d2b00b

File tree

6 files changed

+52
-137
lines changed

6 files changed

+52
-137
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
- Updated TypeScript templates for `ext:dev:init` to fix build failures (#9524)
2+
- Fixed a bug when `firebase emulators:start` incorrectly deletes discovery file of another emulator process (#9672)

firebase-vscode/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## NEXT
22

3+
- Fix the data connect emulator discovery bugs.
4+
35
## 2.0.0
46

57
- Update internal `firebase-tools` dependency to 15.0.0

firebase-vscode/src/core/emulators.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@ export class EmulatorsController implements Disposable {
2121

2222
// called by emulator UI
2323
this.subscriptions.push(
24-
broker.on("runStartEmulators", () => {
25-
this.setEmulatorsStarting();
24+
broker.on("runStartEmulators", async () => {
25+
if (await this.areEmulatorsRunning()) {
26+
return;
27+
}
28+
this.startEmulators();
2629
}),
2730
);
2831

@@ -143,6 +146,12 @@ export class EmulatorsController implements Disposable {
143146
this.notifyEmulatorStateChanged();
144147
}
145148

149+
public async areEmulatorsRunning(): Promise<boolean> {
150+
// Check if any emulators are running
151+
// It may have been terminated without VS Code knowing.
152+
return (await this.findRunningCliEmulators())?.status === "running";
153+
}
154+
146155
async findRunningCliEmulators(): Promise<
147156
{ status: EmulatorsStatus; infos?: RunningEmulatorInfo }
148157
> {
@@ -188,13 +197,6 @@ export class EmulatorsController implements Disposable {
188197
}
189198
}
190199

191-
public async areEmulatorsRunning(): Promise<boolean> {
192-
if (this.emulators.status === "running") {
193-
return true;
194-
}
195-
return (await this.findRunningCliEmulators())?.status === "running";
196-
}
197-
198200
/** FDC specific functions */
199201
readonly isPostgresEnabled = signal(false);
200202
private connectToEmulatorStream() {

firebase-vscode/src/data-connect/terminal.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { checkLogin } from "../core/user";
44
import { DATA_CONNECT_EVENT_NAME, AnalyticsLogger } from "../analytics";
55
import { getSettings } from "../utils/settings";
66
import { currentProjectId } from "../core/project";
7+
import { EmulatorHub } from "../../../src/emulator/hub";
78

89
let environmentVariables: Record<string, string> = {};
910

@@ -93,9 +94,7 @@ export function registerTerminalTasks(
9394
analyticsLogger.logger.logUsage(DATA_CONNECT_EVENT_NAME.START_EMULATORS);
9495

9596
let cmd = `${settings.firebasePath} emulators:start`;
96-
if (currentProjectId.value) {
97-
cmd += ` --project ${currentProjectId.value}`;
98-
}
97+
cmd += ` --project ${currentProjectId.value || EmulatorHub.MISSING_PROJECT_PLACEHOLDER}`;
9998
if (settings.importPath) {
10099
cmd += ` --import ${settings.importPath}`;
101100
}
@@ -108,17 +107,13 @@ export function registerTerminalTasks(
108107
{ focus: true },
109108
);
110109
};
111-
const startEmulatorsTaskBroker = broker.on("runStartEmulators", () => {
112-
startEmulatorsTask();
113-
});
114110
const startEmulatorsCommand = vscode.commands.registerCommand(
115111
"firebase.emulators.start",
116112
startEmulatorsTask,
117113
);
118114

119115
return Disposable.from(
120116
{ dispose: loginTaskBroker },
121-
{ dispose: startEmulatorsTaskBroker },
122117
startEmulatorsCommand,
123118
vscode.commands.registerCommand(
124119
"firebase.dataConnect.runTerminalTask",

firebase-vscode/webviews/data-connect/data-connect.entry.tsx

Lines changed: 0 additions & 85 deletions
This file was deleted.

src/emulator/hub.ts

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export interface Locator {
2020
version: string;
2121
// Ways of reaching the hub as URL prefix, such as http://127.0.0.1:4000
2222
origins: string[];
23+
pid: number;
2324
}
2425

2526
export interface EmulatorHubArgs {
@@ -52,13 +53,7 @@ export class EmulatorHub extends ExpressBasedEmulator {
5253

5354
const data = fs.readFileSync(locatorPath, "utf8").toString();
5455
const locator = JSON.parse(data) as Locator;
55-
56-
if (locator.version !== this.CLI_VERSION) {
57-
logger.debug(
58-
`Found emulator locator with different version: ${JSON.stringify(locator)}, CLI_VERSION: ${this.CLI_VERSION}`,
59-
);
60-
}
61-
56+
logger.debug(`Found emulator hub locator: ${JSON.stringify(locator)}`);
6257
return locator;
6358
}
6459

@@ -69,7 +64,6 @@ export class EmulatorHub extends ExpressBasedEmulator {
6964
}
7065
const filename = `hub-${projectId}.json`;
7166
const locatorPath = path.join(dir, filename);
72-
logger.debug(`Emulator locator file path: ${locatorPath}`);
7367
return locatorPath;
7468
}
7569

@@ -99,7 +93,7 @@ export class EmulatorHub extends ExpressBasedEmulator {
9993
const app = await super.createExpressApp();
10094
app.get("/", (req, res) => {
10195
res.json({
102-
...this.getLocator(),
96+
...this.buildLocator(),
10397
// For backward compatibility:
10498
host: utils.connectableHostname(this.args.listen[0].address),
10599
port: this.args.listen[0].port,
@@ -194,14 +188,13 @@ export class EmulatorHub extends ExpressBasedEmulator {
194188

195189
async stop(): Promise<void> {
196190
await super.stop();
197-
await this.deleteLocatorFile();
198191
}
199192

200193
getName(): Emulators {
201194
return Emulators.HUB;
202195
}
203196

204-
private getLocator(): Locator {
197+
private buildLocator(): Locator {
205198
const version = pkg.version;
206199
const origins: string[] = [];
207200
for (const spec of this.args.listen) {
@@ -214,44 +207,51 @@ export class EmulatorHub extends ExpressBasedEmulator {
214207
return {
215208
version,
216209
origins,
210+
pid: process.pid,
217211
};
218212
}
219213

220214
private async writeLocatorFile(): Promise<void> {
221215
const projectId = this.args.projectId;
222-
const locatorPath = EmulatorHub.getLocatorFilePath(projectId);
223-
const locator = this.getLocator();
224-
225-
if (fs.existsSync(locatorPath)) {
216+
const prevLocator = EmulatorHub.readLocatorFile(projectId);
217+
if (prevLocator && prevLocator.pid && isProcessLive(prevLocator.pid)) {
226218
utils.logLabeledWarning(
227219
"emulators",
228220
`It seems that you are running multiple instances of the emulator suite for project ${projectId}. This may result in unexpected behavior.`,
229221
);
222+
return;
230223
}
231224

232-
logger.debug(`[hub] writing locator at ${locatorPath}`);
233-
return new Promise((resolve, reject) => {
234-
fs.writeFile(locatorPath, JSON.stringify(locator), (e) => {
235-
if (e) {
236-
reject(e);
237-
} else {
238-
resolve();
225+
const locatorPath = EmulatorHub.getLocatorFilePath(projectId);
226+
logger.debug(`Write emulator hub locator at ${locatorPath}`);
227+
fs.writeFileSync(locatorPath, JSON.stringify(this.buildLocator()));
228+
229+
// Delete the emulator hub locator file on exit
230+
const cleanup = () => {
231+
try {
232+
const curLocator = EmulatorHub.readLocatorFile(projectId);
233+
if (curLocator && curLocator.pid === process.pid) {
234+
fs.unlinkSync(locatorPath);
235+
logger.debug(`Delete emulator hub locator file: ${locatorPath}`);
239236
}
240-
});
241-
});
237+
} catch (e: any) {
238+
logger.debug(`Cannot delete emulator hub locator file`, e);
239+
}
240+
};
241+
process.on("SIGINT", cleanup);
242+
process.on("SIGTERM", cleanup);
243+
process.on("exit", cleanup);
242244
}
245+
}
243246

244-
private async deleteLocatorFile(): Promise<void> {
245-
const locatorPath = EmulatorHub.getLocatorFilePath(this.args.projectId);
246-
return new Promise((resolve, reject) => {
247-
fs.unlink(locatorPath, (e) => {
248-
// If the file is already deleted, no need to throw.
249-
if (e && e.code !== "ENOENT") {
250-
reject(e);
251-
} else {
252-
resolve();
253-
}
254-
});
255-
});
247+
function isProcessLive(pid: number): boolean {
248+
try {
249+
// Send signal 0 to check if process is alive.
250+
process.kill(pid, 0);
251+
return true;
252+
} catch (error: any) {
253+
// ESRCH: The process does not exist (it's dead)
254+
// EPERM: The process exists, but you don't have permission to signal it (it's live)
255+
return error.code === "EPERM";
256256
}
257257
}

0 commit comments

Comments
 (0)