Skip to content
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Fix webframeworks deployments when using `site` in `firebase.json`. (#8295)
122 changes: 0 additions & 122 deletions src/deploy/hosting/deploy.spec.ts

This file was deleted.

176 changes: 176 additions & 0 deletions src/deploy/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import { expect } from "chai";
import * as sinon from "sinon";

import { isDeployingWebFramework } from "./index";

describe("Deploy", () => {
describe("isDeployingWebFramework", () => {
let options: any;

Check warning on line 8 in src/deploy/index.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected any. Specify a different type

beforeEach(() => {
options = {
config: {
get: sinon.stub(),
},
only: undefined,
};
});

describe("with site in config", () => {
describe("single web framework", () => {
beforeEach(() => {
options.config.get.withArgs("hosting").returns([{ source: "src", site: "webframework" }]);

Check warning on line 22 in src/deploy/index.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .returns on an `any` value

Check warning on line 22 in src/deploy/index.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe call of an `any` typed value

Check warning on line 22 in src/deploy/index.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .config on an `any` value

Check warning on line 22 in src/deploy/index.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe call of an `any` typed value
});

describe("without 'only' option", () => {
it("should return true if a web framework is in config", () => {
expect(isDeployingWebFramework(options)).to.be.true;

Check warning on line 27 in src/deploy/index.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe argument of type `any` assigned to a parameter of type `DeployOptions`
});
});

describe("with 'only' option", () => {
it("should return false if 'only' option does not match the site", () => {
options.only = "hosting:othersite";

Check warning on line 33 in src/deploy/index.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .only on an `any` value
expect(isDeployingWebFramework(options)).to.be.false;

Check warning on line 34 in src/deploy/index.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe argument of type `any` assigned to a parameter of type `DeployOptions`
});

it("should return true if 'only' option matches the site", () => {
options.only = "hosting:webframework";

Check warning on line 38 in src/deploy/index.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .only on an `any` value
expect(isDeployingWebFramework(options)).to.be.true;

Check warning on line 39 in src/deploy/index.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe argument of type `any` assigned to a parameter of type `DeployOptions`
});

it("should return false if 'only' option matches a function, not a web framework", () => {
options.only = "functions:webframework";
expect(isDeployingWebFramework(options)).to.be.false;
});
});
});

describe("with both a web framework and a non-web framework", () => {
beforeEach(() => {
options.config.get.withArgs("hosting").returns([
{ source: "src", site: "webframework" },
{ site: "public", public: "public" },
]);
});

describe("without 'only' option", () => {
it("should return true if a web framework is in config", () => {
expect(isDeployingWebFramework(options)).to.be.true;
});
});

describe("with 'only' option", () => {
it("should return false if 'only' option does not match the web framework site", () => {
options.only = "hosting:othersite";
expect(isDeployingWebFramework(options)).to.be.false;
});

it("should return true if 'only' option matches the web framework site", () => {
options.only = "hosting:webframework";
expect(isDeployingWebFramework(options)).to.be.true;
});

it("should return false if 'only' option matches a non-web framework site", () => {
options.only = "hosting:public";
expect(isDeployingWebFramework(options)).to.be.false;
});

it("should return false if 'only' option matches a function, not a web framework", () => {
options.only = "functions:webframework";
expect(isDeployingWebFramework(options)).to.be.false;
});
});
});
});

describe("with target in config", () => {
describe("single web framework", () => {
beforeEach(() => {
options.config.get
.withArgs("hosting")
.returns([{ source: "src", target: "webframework" }]);
});

describe("without 'only' option", () => {
it("should return true if a web framework is in config", () => {
expect(isDeployingWebFramework(options)).to.be.true;
});
});

describe("with 'only' option", () => {
it("should return false if 'only' option does not match the target", () => {
options.only = "hosting:othertarget";
expect(isDeployingWebFramework(options)).to.be.false;
});

it("should return true if 'only' option matches the target", () => {
options.only = "hosting:webframework";
expect(isDeployingWebFramework(options)).to.be.true;
});

it("should return false if 'only' option matches a function, not a web framework", () => {
options.only = "functions:webframework";
expect(isDeployingWebFramework(options)).to.be.false;
});
});
});

describe("with both a web framework and a non-web framework", () => {
beforeEach(() => {
options.config.get.withArgs("hosting").returns([
{ source: "src", target: "webframework" },
{ target: "public", public: "public" },
]);
});

describe("without 'only' option", () => {
it("should return true if a web framework is in config", () => {
expect(isDeployingWebFramework(options)).to.be.true;
});
});

describe("with 'only' option", () => {
it("should return false if 'only' option does not match the web framework target", () => {
options.only = "hosting:othertarget";
expect(isDeployingWebFramework(options)).to.be.false;
});

it("should return true if 'only' option matches the web framework target", () => {
options.only = "hosting:webframework";
expect(isDeployingWebFramework(options)).to.be.true;
});

it("should return false if 'only' option matches a non-web framework target", () => {
options.only = "hosting:public";
expect(isDeployingWebFramework(options)).to.be.false;
});

it("should return false if 'only' option matches a function, not a web framework", () => {
options.only = "functions:webframework";
expect(isDeployingWebFramework(options)).to.be.false;
});
});
});
});

describe("with no web framework in config", () => {
beforeEach(() => {
options.config.get.withArgs("hosting").returns([{ site: "public", public: "public" }]);
});

describe("without 'only' option", () => {
it("should return false if no web framework is in config", () => {
expect(isDeployingWebFramework(options)).to.be.false;
});
});

describe("with 'only' option", () => {
it("should return false regardless of 'only' option", () => {
options.only = "hosting:webframework";
expect(isDeployingWebFramework(options)).to.be.false;
});
});
});
});
});
45 changes: 20 additions & 25 deletions src/deploy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,13 @@ import * as StorageTarget from "./storage";
import * as RemoteConfigTarget from "./remoteconfig";
import * as ExtensionsTarget from "./extensions";
import * as DataConnectTarget from "./dataconnect";
import { HostingConfig } from "../firebaseConfig";
import { prepareFrameworks } from "../frameworks";
import { FrameworkContext } from "../frameworks/interfaces";
import { Context } from "./hosting/context";
import { addPinnedFunctionsToOnlyString, hasPinnedFunctions } from "./hosting/prepare";
import { isRunningInGithubAction } from "../init/features/hosting/github";
import { TARGET_PERMISSIONS } from "../commands/deploy";
import { requirePermissions } from "../requirePermissions";
import { Options } from "../options";
import { Context } from "./hosting/context";

const TARGETS = {
hosting: HostingTarget,
Expand All @@ -48,27 +46,23 @@ const chain = async function (fns: Chain, context: any, options: any, payload: a
}
};

export const matchesHostingTarget = (only: string | undefined, target?: string): boolean => {
if (!only) return true;
if (!only.includes("hosting:")) return true;
const targetStr = `hosting:${target ?? ""}`;
return only.split(",").some((t) => t === targetStr);
};
export const isDeployingWebFramework = (options: DeployOptions): boolean => {
const config = options.config.get("hosting");

export const prepareFrameworksIfNeeded = async function (
targetNames: (keyof typeof TARGETS)[],
options: DeployOptions,
context: FrameworkContext,
): Promise<void> {
const config = options.config.get("hosting") as HostingConfig;
if (
Array.isArray(config)
? config.some((it) => it.source && matchesHostingTarget(options.only, it.target))
: config.source
) {
experiments.assertEnabled("webframeworks", "deploy a web framework from source");
await prepareFrameworks("deploy", targetNames, context, options);
}
const webFrameworkInConfig = (Array.isArray(config) ? config : [config]).find((it) => it.source);

if (!webFrameworkInConfig) return false;

if (!options.only) return true;

return options.only.split(",").some((it) => {
const [target, site] = it.split(":");

return (
target === "hosting" &&
[webFrameworkInConfig.site, webFrameworkInConfig.target].includes(site)
);
});
};

/**
Expand All @@ -92,8 +86,9 @@ export const deploy = async function (
const postdeploys: Chain = [];
const startTime = Date.now();

if (targetNames.includes("hosting")) {
await prepareFrameworksIfNeeded(targetNames, options, context);
if (targetNames.includes("hosting") && isDeployingWebFramework(options)) {
experiments.assertEnabled("webframeworks", "deploy a web framework from source");
await prepareFrameworks("deploy", targetNames, context, options);
}

if (targetNames.includes("hosting") && hasPinnedFunctions(options)) {
Expand Down
Loading