From b7bece462dad6a80db12a1b3ee9f080f29279709 Mon Sep 17 00:00:00 2001 From: Mathusan Selvarajah Date: Mon, 29 Jul 2024 20:41:32 +0000 Subject: [PATCH 01/12] set up App Hosting emulator boilerplate --- src/emulator/apphosting/index.ts | 37 ++++++++++++++++++++++++++++++++ src/emulator/constants.ts | 3 +++ src/emulator/controller.ts | 14 ++++++++++++ src/emulator/portUtils.ts | 2 ++ src/emulator/registry.ts | 2 ++ src/emulator/types.ts | 2 ++ src/firebaseConfig.ts | 4 ++++ 7 files changed, 64 insertions(+) create mode 100644 src/emulator/apphosting/index.ts diff --git a/src/emulator/apphosting/index.ts b/src/emulator/apphosting/index.ts new file mode 100644 index 00000000000..c327b92bc8b --- /dev/null +++ b/src/emulator/apphosting/index.ts @@ -0,0 +1,37 @@ +import { EmulatorInfo, EmulatorInstance, Emulators } from "../types"; + +interface AppHostingEmulatorArgs { + options?: any; + port?: number; + host?: string; +} + +export class AppHostingEmulator implements EmulatorInstance { + constructor(private args: AppHostingEmulatorArgs) {} + + async start(): Promise { + this.args.options.host = this.args.host; + this.args.options.port = this.args.port; + + // const { ports } = await serveHosting.start(this.args.options); + // this.args.port = ports[0]; + // if (ports.length > 1) { + // this.reservedPorts = ports.slice(1); + // } + } + connect(): Promise { + throw new Error("Method not implemented."); + } + + stop(): Promise { + throw new Error("Method not implemented."); + } + + getInfo(): EmulatorInfo { + throw new Error("Method not implemented."); + } + + getName(): Emulators { + throw new Error("Method not implemented."); + } +} diff --git a/src/emulator/constants.ts b/src/emulator/constants.ts index ffe02cc3664..20932d907bb 100644 --- a/src/emulator/constants.ts +++ b/src/emulator/constants.ts @@ -7,6 +7,7 @@ export const DEFAULT_PORTS: { [s in Emulators]: number } = { hosting: 5000, functions: 5001, extensions: 5001, // The Extensions Emulator runs on the same port as the Functions Emulator + apphosting: 5002, firestore: 8080, pubsub: 8085, database: 9000, @@ -22,6 +23,7 @@ export const FIND_AVAILBLE_PORT_BY_DEFAULT: Record = { hub: true, logging: true, hosting: true, + apphosting: true, functions: false, firestore: false, database: false, @@ -39,6 +41,7 @@ export const EMULATOR_DESCRIPTION: Record = { hub: "emulator hub", logging: "Logging Emulator", hosting: "Hosting Emulator", + apphosting: "App Hosting Emulator", functions: "Functions Emulator", firestore: "Firestore Emulator", database: "Database Emulator", diff --git a/src/emulator/controller.ts b/src/emulator/controller.ts index 7fbe76888f9..e7b5a74a100 100755 --- a/src/emulator/controller.ts +++ b/src/emulator/controller.ts @@ -58,6 +58,7 @@ import { PubsubEmulator } from "./pubsubEmulator"; import { StorageEmulator } from "./storage"; import { readFirebaseJson } from "../dataconnect/fileUtils"; import { TasksEmulator } from "./tasksEmulator"; +import { AppHostingEmulator } from "./apphosting"; const START_LOGGING_EMULATOR = utils.envOverride( "START_LOGGING_EMULATOR", @@ -886,6 +887,19 @@ export async function startAll( await startEmulator(hostingEmulator); } + // similar to the hosting emulator, the App Hosting emulator should also + // start after the other emulators + if (listenForEmulator.apphosting) { + const apphostingAddr = legacyGetFirstAddr(Emulators.APPHOSTING); + const apphostingEmulator = new AppHostingEmulator({ + host: apphostingAddr.host, + port: apphostingAddr.port, + options, + }); + + await startEmulator(apphostingEmulator); + } + if (listenForEmulator.logging) { const loggingAddr = legacyGetFirstAddr(Emulators.LOGGING); const loggingEmulator = new LoggingEmulator({ diff --git a/src/emulator/portUtils.ts b/src/emulator/portUtils.ts index e3db91120ba..8d3d9233745 100644 --- a/src/emulator/portUtils.ts +++ b/src/emulator/portUtils.ts @@ -209,6 +209,8 @@ const EMULATOR_CAN_LISTEN_ON_PRIMARY_ONLY: Record = { // Only one hostname possible in .server mode, can switch to middleware later. hosting: true, + + apphosting: true, }; export interface EmulatorListenConfig { diff --git a/src/emulator/registry.ts b/src/emulator/registry.ts index aa955d72624..eb0b42f8e23 100644 --- a/src/emulator/registry.ts +++ b/src/emulator/registry.ts @@ -79,6 +79,8 @@ export class EmulatorRegistry { // Hosting is next because it can trigger functions. hosting: 2, + apphosting: 2.1, + // All background trigger emulators are equal here, so we choose // an order for consistency. database: 3.0, diff --git a/src/emulator/types.ts b/src/emulator/types.ts index b1566f5a747..a969e70a257 100644 --- a/src/emulator/types.ts +++ b/src/emulator/types.ts @@ -8,6 +8,7 @@ export enum Emulators { FIRESTORE = "firestore", DATABASE = "database", HOSTING = "hosting", + APPHOSTING = "apphosting", PUBSUB = "pubsub", UI = "ui", LOGGING = "logging", @@ -48,6 +49,7 @@ export const ALL_SERVICE_EMULATORS = [ Emulators.FIRESTORE, Emulators.DATABASE, Emulators.HOSTING, + Emulators.APPHOSTING, Emulators.PUBSUB, Emulators.STORAGE, Emulators.EVENTARC, diff --git a/src/firebaseConfig.ts b/src/firebaseConfig.ts index 1ddd9e478bb..0f2260ed701 100644 --- a/src/firebaseConfig.ts +++ b/src/firebaseConfig.ts @@ -203,6 +203,10 @@ export type EmulatorsConfig = { host?: string; port?: number; }; + apphosting?: { + host?: string; + port?: number; + }; pubsub?: { host?: string; port?: number; From 608e3cbed867da186c70e443c685041541bda735 Mon Sep 17 00:00:00 2001 From: Mathusan Selvarajah Date: Mon, 5 Aug 2024 17:36:20 +0000 Subject: [PATCH 02/12] mock out apphosting emulator functions --- src/emulator/apphosting/index.ts | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/emulator/apphosting/index.ts b/src/emulator/apphosting/index.ts index c327b92bc8b..0957b40c4b9 100644 --- a/src/emulator/apphosting/index.ts +++ b/src/emulator/apphosting/index.ts @@ -18,20 +18,31 @@ export class AppHostingEmulator implements EmulatorInstance { // if (ports.length > 1) { // this.reservedPorts = ports.slice(1); // } + + console.log(`starting apphosting emulatorr!!`); } connect(): Promise { - throw new Error("Method not implemented."); + console.log(`connecting apphosting emulatorr!!`); + // throw new Error("Method not implemented."); + return Promise.resolve(); } stop(): Promise { - throw new Error("Method not implemented."); + // throw new Error("Method not implemented."); + console.log("stopping apphosting emulator"); + return Promise.resolve(); } getInfo(): EmulatorInfo { - throw new Error("Method not implemented."); + // throw new Error("Method not implemented."); + return { + name: Emulators.APPHOSTING, + host: "127.0.0.1", + port: 5001, + }; } getName(): Emulators { - throw new Error("Method not implemented."); + return Emulators.APPHOSTING; } } From 12bb8d66af0403a0cdedf6898de73fa415d1dfc2 Mon Sep 17 00:00:00 2001 From: Mathusan Selvarajah Date: Tue, 6 Aug 2024 17:55:34 +0000 Subject: [PATCH 03/12] working nextjs setupwq --- src/emulator/apphosting/index.ts | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/emulator/apphosting/index.ts b/src/emulator/apphosting/index.ts index 0957b40c4b9..8866b162054 100644 --- a/src/emulator/apphosting/index.ts +++ b/src/emulator/apphosting/index.ts @@ -1,5 +1,5 @@ import { EmulatorInfo, EmulatorInstance, Emulators } from "../types"; - +import { spawn } from "cross-spawn"; interface AppHostingEmulatorArgs { options?: any; port?: number; @@ -20,6 +20,12 @@ export class AppHostingEmulator implements EmulatorInstance { // } console.log(`starting apphosting emulatorr!!`); + const host = await serve( + this.args.options.host, + this.args.options.port, + "/usr/local/google/home/mathusan/github.com/mathu97/domain-name-generator", + ); + console.log(`serving on ${host}`); } connect(): Promise { console.log(`connecting apphosting emulatorr!!`); @@ -46,3 +52,26 @@ export class AppHostingEmulator implements EmulatorInstance { return Emulators.APPHOSTING; } } + +export async function serve(hostaddr: string, port: string, cwd: string) { + console.log(`cwd: ${process.cwd()}`); + const host = new Promise((resolve, reject) => { + const serve = spawn("npm", ["run", "dev", "--", `-H`, hostaddr, `-p`, port], { + cwd: process.cwd(), + }); + serve.on("error", function (err) { + console.log("Oh noez, teh errurz: " + JSON.stringify(err)); + }); + serve.stdout.on("data", (data: any) => { + process.stdout.write(data); + const match = data.toString().match(/(http:\/\/localhost:\d+)/); + if (match) resolve(match[1]); + }); + serve.stderr.on("data", (data: any) => { + process.stderr.write(data); + }); + serve.on("exit", reject); + }); + + return host; +} From 111b328d50738a80d1fccb8c07547b311aaa8910 Mon Sep 17 00:00:00 2001 From: Mathusan Selvarajah Date: Mon, 26 Aug 2024 17:21:32 +0000 Subject: [PATCH 04/12] move apphosting emulator behind a flag and refactor --- src/emulator/apphosting/index.ts | 40 +++------------------- src/emulator/apphosting/serve.ts | 59 ++++++++++++++++++++++++++++++++ src/emulator/controller.ts | 18 +++++----- src/emulator/types.ts | 3 +- src/experiments.ts | 4 +++ 5 files changed, 80 insertions(+), 44 deletions(-) create mode 100644 src/emulator/apphosting/serve.ts diff --git a/src/emulator/apphosting/index.ts b/src/emulator/apphosting/index.ts index 8866b162054..fb174cee0e7 100644 --- a/src/emulator/apphosting/index.ts +++ b/src/emulator/apphosting/index.ts @@ -1,5 +1,5 @@ import { EmulatorInfo, EmulatorInstance, Emulators } from "../types"; -import { spawn } from "cross-spawn"; +import { start as apphostingStart } from "./serve"; interface AppHostingEmulatorArgs { options?: any; port?: number; @@ -19,28 +19,21 @@ export class AppHostingEmulator implements EmulatorInstance { // this.reservedPorts = ports.slice(1); // } - console.log(`starting apphosting emulatorr!!`); - const host = await serve( - this.args.options.host, - this.args.options.port, - "/usr/local/google/home/mathusan/github.com/mathu97/domain-name-generator", - ); - console.log(`serving on ${host}`); + console.log("starting apphosting emulator"); + const { port } = await apphostingStart(this.args.options); + console.log(`serving on port ${port}`); } connect(): Promise { - console.log(`connecting apphosting emulatorr!!`); - // throw new Error("Method not implemented."); + console.log(`connecting apphosting emulator`); return Promise.resolve(); } stop(): Promise { - // throw new Error("Method not implemented."); console.log("stopping apphosting emulator"); return Promise.resolve(); } getInfo(): EmulatorInfo { - // throw new Error("Method not implemented."); return { name: Emulators.APPHOSTING, host: "127.0.0.1", @@ -52,26 +45,3 @@ export class AppHostingEmulator implements EmulatorInstance { return Emulators.APPHOSTING; } } - -export async function serve(hostaddr: string, port: string, cwd: string) { - console.log(`cwd: ${process.cwd()}`); - const host = new Promise((resolve, reject) => { - const serve = spawn("npm", ["run", "dev", "--", `-H`, hostaddr, `-p`, port], { - cwd: process.cwd(), - }); - serve.on("error", function (err) { - console.log("Oh noez, teh errurz: " + JSON.stringify(err)); - }); - serve.stdout.on("data", (data: any) => { - process.stdout.write(data); - const match = data.toString().match(/(http:\/\/localhost:\d+)/); - if (match) resolve(match[1]); - }); - serve.stderr.on("data", (data: any) => { - process.stderr.write(data); - }); - serve.on("exit", reject); - }); - - return host; -} diff --git a/src/emulator/apphosting/serve.ts b/src/emulator/apphosting/serve.ts new file mode 100644 index 00000000000..4b5ab45c588 --- /dev/null +++ b/src/emulator/apphosting/serve.ts @@ -0,0 +1,59 @@ +/** + * Start the App Hosting server. + * @param options the Firebase CLI options. + */ +import { spawn } from "cross-spawn"; +import { isIPv4 } from "net"; +import { checkListenable } from "../portUtils"; +import { EmulatorLogger } from "../../emulator/emulatorLogger"; +import { Emulators } from "../types"; +import { wrapSpawn } from "../../init/spawn"; + +const logger = EmulatorLogger.forEmulator(Emulators.APPHOSTING); + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export async function start(options: any): Promise<{ port: number }> { + // const config = read in env variables / secrets etc and other config stuff from firebase.json + + let port = options.port; + while (!(await availablePort(options.host, port))) { + port += 1; + } + + serve(options, port); + + return { port }; +} + +function availablePort(host: string, port: number): Promise { + return checkListenable({ + address: host, + port, + family: isIPv4(host) ? "IPv4" : "IPv6", + }); +} + +export async function serve(options: any, port: string) { + console.log(`current working directory: ${process.cwd()}`); + const host = new Promise(async (resolve, reject) => { + const serve = await wrapSpawn( + "npm", + ["run", "dev", "--", `-H`, options.host, `-p`, port], + process.cwd(), + ); + // serve.on("error", function (err) { + // console.log("error: " + JSON.stringify(err)); + // }); + // serve.stdout.on("data", (data: any) => { + // process.stdout.write(data); + // const match = data.toString().match(/(http:\/\/localhost:\d+)/); + // if (match) resolve(match[1]); + // }); + // serve.stderr.on("data", (data: any) => { + // process.stderr.write(data); + // }); + // serve.on("exit", reject); + }); + + return host; +} diff --git a/src/emulator/controller.ts b/src/emulator/controller.ts index e7b5a74a100..12b17c9655f 100755 --- a/src/emulator/controller.ts +++ b/src/emulator/controller.ts @@ -889,15 +889,17 @@ export async function startAll( // similar to the hosting emulator, the App Hosting emulator should also // start after the other emulators - if (listenForEmulator.apphosting) { - const apphostingAddr = legacyGetFirstAddr(Emulators.APPHOSTING); - const apphostingEmulator = new AppHostingEmulator({ - host: apphostingAddr.host, - port: apphostingAddr.port, - options, - }); + if (experiments.isEnabled("emulatorapphosting")) { + if (listenForEmulator.apphosting) { + const apphostingAddr = legacyGetFirstAddr(Emulators.APPHOSTING); + const apphostingEmulator = new AppHostingEmulator({ + host: apphostingAddr.host, + port: apphostingAddr.port, + options, + }); - await startEmulator(apphostingEmulator); + await startEmulator(apphostingEmulator); + } } if (listenForEmulator.logging) { diff --git a/src/emulator/types.ts b/src/emulator/types.ts index a969e70a257..e3d48dc9693 100644 --- a/src/emulator/types.ts +++ b/src/emulator/types.ts @@ -1,5 +1,6 @@ import { ChildProcess } from "child_process"; import { EventEmitter } from "events"; +import * as experiments from "../experiments"; export enum Emulators { AUTH = "auth", @@ -49,7 +50,7 @@ export const ALL_SERVICE_EMULATORS = [ Emulators.FIRESTORE, Emulators.DATABASE, Emulators.HOSTING, - Emulators.APPHOSTING, + ...(experiments.isEnabled("emulatorapphosting") ? [Emulators.APPHOSTING] : []), Emulators.PUBSUB, Emulators.STORAGE, Emulators.EVENTARC, diff --git a/src/experiments.ts b/src/experiments.ts index 08eb211761d..b0aa324ebd7 100644 --- a/src/experiments.ts +++ b/src/experiments.ts @@ -78,6 +78,10 @@ export const ALL_EXPERIMENTS = experiments({ emulatoruisnapshot: { shortDescription: "Load pre-release versions of the emulator UI", }, + emulatorapphosting: { + shortDescription: "App Hosting emululator", + public: false, + }, // Hosting experiments webframeworks: { From a6a2a6fead22f0e0c09edda01fcbdb655dc6ce79 Mon Sep 17 00:00:00 2001 From: Mathusan Selvarajah Date: Mon, 26 Aug 2024 18:50:28 +0000 Subject: [PATCH 05/12] remove comments --- src/emulator/apphosting/index.ts | 6 ------ src/emulator/apphosting/serve.ts | 14 -------------- 2 files changed, 20 deletions(-) diff --git a/src/emulator/apphosting/index.ts b/src/emulator/apphosting/index.ts index fb174cee0e7..11482f7c70b 100644 --- a/src/emulator/apphosting/index.ts +++ b/src/emulator/apphosting/index.ts @@ -13,12 +13,6 @@ export class AppHostingEmulator implements EmulatorInstance { this.args.options.host = this.args.host; this.args.options.port = this.args.port; - // const { ports } = await serveHosting.start(this.args.options); - // this.args.port = ports[0]; - // if (ports.length > 1) { - // this.reservedPorts = ports.slice(1); - // } - console.log("starting apphosting emulator"); const { port } = await apphostingStart(this.args.options); console.log(`serving on port ${port}`); diff --git a/src/emulator/apphosting/serve.ts b/src/emulator/apphosting/serve.ts index 4b5ab45c588..5a3ce8ebf77 100644 --- a/src/emulator/apphosting/serve.ts +++ b/src/emulator/apphosting/serve.ts @@ -2,7 +2,6 @@ * Start the App Hosting server. * @param options the Firebase CLI options. */ -import { spawn } from "cross-spawn"; import { isIPv4 } from "net"; import { checkListenable } from "../portUtils"; import { EmulatorLogger } from "../../emulator/emulatorLogger"; @@ -34,25 +33,12 @@ function availablePort(host: string, port: number): Promise { } export async function serve(options: any, port: string) { - console.log(`current working directory: ${process.cwd()}`); const host = new Promise(async (resolve, reject) => { const serve = await wrapSpawn( "npm", ["run", "dev", "--", `-H`, options.host, `-p`, port], process.cwd(), ); - // serve.on("error", function (err) { - // console.log("error: " + JSON.stringify(err)); - // }); - // serve.stdout.on("data", (data: any) => { - // process.stdout.write(data); - // const match = data.toString().match(/(http:\/\/localhost:\d+)/); - // if (match) resolve(match[1]); - // }); - // serve.stderr.on("data", (data: any) => { - // process.stderr.write(data); - // }); - // serve.on("exit", reject); }); return host; From 9ff7a3dea3bc23d0f940e16a839863e367092c2d Mon Sep 17 00:00:00 2001 From: Mathusan Selvarajah Date: Fri, 30 Aug 2024 20:30:00 +0000 Subject: [PATCH 06/12] clean pup --- src/emulator/apphosting/index.ts | 5 +++-- src/emulator/apphosting/serve.ts | 13 ++----------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/emulator/apphosting/index.ts b/src/emulator/apphosting/index.ts index 11482f7c70b..35cc89e05b6 100644 --- a/src/emulator/apphosting/index.ts +++ b/src/emulator/apphosting/index.ts @@ -17,6 +17,7 @@ export class AppHostingEmulator implements EmulatorInstance { const { port } = await apphostingStart(this.args.options); console.log(`serving on port ${port}`); } + connect(): Promise { console.log(`connecting apphosting emulator`); return Promise.resolve(); @@ -30,8 +31,8 @@ export class AppHostingEmulator implements EmulatorInstance { getInfo(): EmulatorInfo { return { name: Emulators.APPHOSTING, - host: "127.0.0.1", - port: 5001, + host: this.args.host!, + port: this.args.port!, }; } diff --git a/src/emulator/apphosting/serve.ts b/src/emulator/apphosting/serve.ts index 5a3ce8ebf77..a8e1e629c00 100644 --- a/src/emulator/apphosting/serve.ts +++ b/src/emulator/apphosting/serve.ts @@ -12,8 +12,6 @@ const logger = EmulatorLogger.forEmulator(Emulators.APPHOSTING); // eslint-disable-next-line @typescript-eslint/no-explicit-any export async function start(options: any): Promise<{ port: number }> { - // const config = read in env variables / secrets etc and other config stuff from firebase.json - let port = options.port; while (!(await availablePort(options.host, port))) { port += 1; @@ -33,13 +31,6 @@ function availablePort(host: string, port: number): Promise { } export async function serve(options: any, port: string) { - const host = new Promise(async (resolve, reject) => { - const serve = await wrapSpawn( - "npm", - ["run", "dev", "--", `-H`, options.host, `-p`, port], - process.cwd(), - ); - }); - - return host; + // TODO: update to support other package managers and frameworks other than NextJS + await wrapSpawn("npm", ["run", "dev", "--", `-H`, options.host, `-p`, port], process.cwd()); } From e9a353aeffe0b6680d51b4badd87e68bb12c6f2d Mon Sep 17 00:00:00 2001 From: Mathusan Selvarajah Date: Fri, 30 Aug 2024 20:30:42 +0000 Subject: [PATCH 07/12] remove unused logger --- src/emulator/apphosting/serve.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/emulator/apphosting/serve.ts b/src/emulator/apphosting/serve.ts index a8e1e629c00..956d85d347d 100644 --- a/src/emulator/apphosting/serve.ts +++ b/src/emulator/apphosting/serve.ts @@ -4,12 +4,8 @@ */ import { isIPv4 } from "net"; import { checkListenable } from "../portUtils"; -import { EmulatorLogger } from "../../emulator/emulatorLogger"; -import { Emulators } from "../types"; import { wrapSpawn } from "../../init/spawn"; -const logger = EmulatorLogger.forEmulator(Emulators.APPHOSTING); - // eslint-disable-next-line @typescript-eslint/no-explicit-any export async function start(options: any): Promise<{ port: number }> { let port = options.port; From 15f97565265d04ce132bc4b6ecfe6e0e3733d1fa Mon Sep 17 00:00:00 2001 From: Mathusan Selvarajah Date: Tue, 3 Sep 2024 17:36:10 +0000 Subject: [PATCH 08/12] add test for start func --- src/emulator/apphosting/serve.spec.ts | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/emulator/apphosting/serve.spec.ts diff --git a/src/emulator/apphosting/serve.spec.ts b/src/emulator/apphosting/serve.spec.ts new file mode 100644 index 00000000000..090365254ca --- /dev/null +++ b/src/emulator/apphosting/serve.spec.ts @@ -0,0 +1,34 @@ +import * as portUtils from "../portUtils"; +import * as sinon from "sinon"; +import * as spawn from "../../init/spawn"; +import { expect } from "chai"; +import * as serve from "./serve"; + +describe("serve", () => { + let checkListenableStub: sinon.SinonStub; + let wrapSpawnStub: sinon.SinonStub; + + beforeEach(() => { + checkListenableStub = sinon.stub(portUtils, "checkListenable"); + wrapSpawnStub = sinon.stub(spawn, "wrapSpawn"); + }); + + afterEach(() => { + checkListenableStub.restore(); + wrapSpawnStub.restore(); + }); + + describe("start", () => { + it("should only select an available port to serve", async () => { + // const reservedPorts = [5000, 5001, 5002]; + checkListenableStub.onFirstCall().returns(false); + checkListenableStub.onSecondCall().returns(false); + checkListenableStub.onThirdCall().returns(true); + + wrapSpawnStub.returns(Promise.resolve()); + + const res = await serve.start({ host: "127.0.0.1", port: 5000 }); + expect(res.port).to.equal(5002); + }); + }); +}); From 1129f27bdbf82555c644c2f57c832324e132eef8 Mon Sep 17 00:00:00 2001 From: Mathusan Selvarajah Date: Tue, 3 Sep 2024 17:39:04 +0000 Subject: [PATCH 09/12] fix --- src/emulator/apphosting/serve.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/emulator/apphosting/serve.spec.ts b/src/emulator/apphosting/serve.spec.ts index 090365254ca..750df4e41a6 100644 --- a/src/emulator/apphosting/serve.spec.ts +++ b/src/emulator/apphosting/serve.spec.ts @@ -20,7 +20,6 @@ describe("serve", () => { describe("start", () => { it("should only select an available port to serve", async () => { - // const reservedPorts = [5000, 5001, 5002]; checkListenableStub.onFirstCall().returns(false); checkListenableStub.onSecondCall().returns(false); checkListenableStub.onThirdCall().returns(true); From bdcd151c69889f56cc93e2afc9d2cc6e300872b0 Mon Sep 17 00:00:00 2001 From: Mathusan Selvarajah Date: Tue, 3 Sep 2024 17:44:52 +0000 Subject: [PATCH 10/12] update schema --- schema/firebase-config.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/schema/firebase-config.json b/schema/firebase-config.json index e2a2f727298..3c067ed1562 100644 --- a/schema/firebase-config.json +++ b/schema/firebase-config.json @@ -357,6 +357,18 @@ "emulators": { "additionalProperties": false, "properties": { + "apphosting": { + "additionalProperties": false, + "properties": { + "host": { + "type": "string" + }, + "port": { + "type": "number" + } + }, + "type": "object" + }, "auth": { "additionalProperties": false, "properties": { From 3d6acc4d81c4b40204ad10f0285db3ef0670f373 Mon Sep 17 00:00:00 2001 From: Mathusan Selvarajah Date: Tue, 3 Sep 2024 21:30:14 +0000 Subject: [PATCH 11/12] address comments --- src/emulator/apphosting/index.ts | 14 ++++++++++---- src/emulator/apphosting/serve.ts | 6 ++++++ src/emulator/controller.ts | 8 ++++++-- src/emulator/registry.ts | 4 ++++ src/experiments.ts | 2 +- 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/emulator/apphosting/index.ts b/src/emulator/apphosting/index.ts index 35cc89e05b6..170bd7b88b7 100644 --- a/src/emulator/apphosting/index.ts +++ b/src/emulator/apphosting/index.ts @@ -1,3 +1,4 @@ +import { EmulatorLogger } from "../emulatorLogger"; import { EmulatorInfo, EmulatorInstance, Emulators } from "../types"; import { start as apphostingStart } from "./serve"; interface AppHostingEmulatorArgs { @@ -6,25 +7,30 @@ interface AppHostingEmulatorArgs { host?: string; } +/** + * An emulator instance for Firebase's App Hosting product. This class provides a simulated + * environment for testing App Hosting features locally. + */ export class AppHostingEmulator implements EmulatorInstance { + private logger = EmulatorLogger.forEmulator(Emulators.APPHOSTING); constructor(private args: AppHostingEmulatorArgs) {} async start(): Promise { this.args.options.host = this.args.host; this.args.options.port = this.args.port; - console.log("starting apphosting emulator"); + this.logger.logLabeled("INFO", Emulators.APPHOSTING, "starting apphosting emulator"); const { port } = await apphostingStart(this.args.options); - console.log(`serving on port ${port}`); + this.logger.logLabeled("INFO", Emulators.APPHOSTING, `serving on port ${port}`); } connect(): Promise { - console.log(`connecting apphosting emulator`); + this.logger.logLabeled("INFO", Emulators.APPHOSTING, "connecting apphosting emulator"); return Promise.resolve(); } stop(): Promise { - console.log("stopping apphosting emulator"); + this.logger.logLabeled("INFO", Emulators.APPHOSTING, "stopping apphosting emulator"); return Promise.resolve(); } diff --git a/src/emulator/apphosting/serve.ts b/src/emulator/apphosting/serve.ts index 956d85d347d..6afed818d75 100644 --- a/src/emulator/apphosting/serve.ts +++ b/src/emulator/apphosting/serve.ts @@ -6,6 +6,9 @@ import { isIPv4 } from "net"; import { checkListenable } from "../portUtils"; import { wrapSpawn } from "../../init/spawn"; +/** + * Spins up a project locally by running the project's dev command. + */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export async function start(options: any): Promise<{ port: number }> { let port = options.port; @@ -26,6 +29,9 @@ function availablePort(host: string, port: number): Promise { }); } +/** + * Exported for unit testing + */ export async function serve(options: any, port: string) { // TODO: update to support other package managers and frameworks other than NextJS await wrapSpawn("npm", ["run", "dev", "--", `-H`, options.host, `-p`, port], process.cwd()); diff --git a/src/emulator/controller.ts b/src/emulator/controller.ts index 12b17c9655f..ae002e4aaa8 100755 --- a/src/emulator/controller.ts +++ b/src/emulator/controller.ts @@ -887,8 +887,12 @@ export async function startAll( await startEmulator(hostingEmulator); } - // similar to the hosting emulator, the App Hosting emulator should also - // start after the other emulators + /** + * Similar to the Hosting emulator, the App Hosting emulator should also + * start after the other emulators. This is because the service running on + * app hosting emulator may depend on other emulators (i.e auth, firestore, + * storage, etc). + */ if (experiments.isEnabled("emulatorapphosting")) { if (listenForEmulator.apphosting) { const apphostingAddr = legacyGetFirstAddr(Emulators.APPHOSTING); diff --git a/src/emulator/registry.ts b/src/emulator/registry.ts index eb0b42f8e23..28be17d4c20 100644 --- a/src/emulator/registry.ts +++ b/src/emulator/registry.ts @@ -79,6 +79,10 @@ export class EmulatorRegistry { // Hosting is next because it can trigger functions. hosting: 2, + /** App Hosting should be shut down next. Users should not be interacting + * with their app while its being shut down as the app may using the + * background trigger emulators below. + */ apphosting: 2.1, // All background trigger emulators are equal here, so we choose diff --git a/src/experiments.ts b/src/experiments.ts index b0aa324ebd7..f5cdfe2e3b4 100644 --- a/src/experiments.ts +++ b/src/experiments.ts @@ -79,7 +79,7 @@ export const ALL_EXPERIMENTS = experiments({ shortDescription: "Load pre-release versions of the emulator UI", }, emulatorapphosting: { - shortDescription: "App Hosting emululator", + shortDescription: "App Hosting emulator", public: false, }, From 7db8187bf67c8581bd9d318c8fce0639fe8412c2 Mon Sep 17 00:00:00 2001 From: Mathusan Selvarajah Date: Tue, 3 Sep 2024 17:31:42 -0400 Subject: [PATCH 12/12] Update src/emulator/apphosting/serve.ts Co-authored-by: Daniel Lee --- src/emulator/apphosting/serve.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emulator/apphosting/serve.ts b/src/emulator/apphosting/serve.ts index 6afed818d75..f32b6bb6def 100644 --- a/src/emulator/apphosting/serve.ts +++ b/src/emulator/apphosting/serve.ts @@ -34,5 +34,5 @@ function availablePort(host: string, port: number): Promise { */ export async function serve(options: any, port: string) { // TODO: update to support other package managers and frameworks other than NextJS - await wrapSpawn("npm", ["run", "dev", "--", `-H`, options.host, `-p`, port], process.cwd()); + await wrapSpawn("npm", ["run", "dev", "--", "-H", options.host, "-p", port], process.cwd()); }