Skip to content

Commit 2ff89d6

Browse files
ThadCastl3bkendall
andauthored
Adding '--force' argument (#424)
* added in a flag for '--force' when deploying changes that may increase your bill, such as minimum instances required. * updated force flag to accept a boolean value. : * updated tests for using force flag * updated formatting to match contributor guidelines * Update action.yml Co-authored-by: Bryan Kendall <[email protected]> * accepted update to action.yml for 'force' flag description, added support for channel site deployment with force flag. Updated tests to and types to account for force flags. Force flag is now optional on ProductionDeployConfig and ChannelDeployConfig. Simplified deploy calls using force flag to automatically append force based on the boolean value nad removed the force-specific branch in index.ts * updated test to use the correct ChannelDeployConfig * merged remote main branch updates and formatted, tests are passing now * removed unused types for tests --------- Co-authored-by: Bryan Kendall <[email protected]>
1 parent d0d1dde commit 2ff89d6

File tree

5 files changed

+121
-27
lines changed

5 files changed

+121
-27
lines changed

action.yml

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,25 +33,16 @@ inputs:
3333
default: "7d"
3434
required: false
3535
projectId:
36-
description:
37-
"The project to deploy to. If you leave this blank, make sure you check in
38-
a .firebaserc file"
36+
description: "The project to deploy to. If you leave this blank, make sure you check in a .firebaserc file"
3937
required: false
4038
channelId:
41-
description: "The ID of the channel to deploy to. If you leave this blank,
42-
a preview channel and its ID will be auto-generated per branch or PR."
39+
description: "The ID of the channel to deploy to. If you leave this blank, a preview channel and its ID will be auto-generated per branch or PR."
4340
required: false
4441
target:
45-
description:
46-
"The target name of the Hosting site to deploy to. If you leave this blank,
47-
the default target or all targets defined in the .firebaserc will be deployed to.
48-
Refer to the Hosting docs about [multiple sites](https://firebase.google.com/docs/hosting/multisites)
49-
for more information about deploy targets."
42+
description: "The target name of the Hosting site to deploy to. If you leave this blank, the default target or all targets defined in the .firebaserc will be deployed to. Refer to the Hosting docs about [multiple sites](https://firebase.google.com/docs/hosting/multisites) for more information about deploy targets."
5043
required: false
5144
entryPoint:
52-
description:
53-
"The location of your firebase.json file, relative to the root of your
54-
directory"
45+
description: "The location of your firebase.json file, relative to the root of your directory"
5546
default: "."
5647
required: false
5748
firebaseToolsVersion:
@@ -64,6 +55,10 @@ inputs:
6455
Disable auto-commenting with the preview channel URL to the pull request
6556
default: "false"
6657
required: false
58+
force:
59+
description: "Pass 'true' to use the --force flag with firebase deploy. This will automatically accept any warning or prompts during deploy. Use with caution."
60+
default: "false"
61+
required: false
6762
outputs:
6863
urls:
6964
description: The url(s) deployed to

bin/action.min.js

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92946,8 +92946,9 @@ async function execWithCredentials(args, projectId, gacFilename, opts) {
9294692946
let deployOutputBuf = [];
9294792947
const debug = opts.debug || false;
9294892948
const firebaseToolsVersion = opts.firebaseToolsVersion || "latest";
92949+
const force = opts.force;
9294992950
try {
92950-
await exec_1.exec(`npx firebase-tools@${firebaseToolsVersion}`, [...args, ...(projectId ? ["--project", projectId] : []), debug ? "--debug" // gives a more thorough error message
92951+
await exec_1.exec(`npx firebase-tools@${firebaseToolsVersion}`, [...args, ...(projectId ? ["--project", projectId] : []), ...(force ? ["--force"] : []), debug ? "--debug" // gives a more thorough error message
9295192952
: "--json" // allows us to easily parse the output
9295292953
], {
9295392954
listeners: {
@@ -92968,7 +92969,8 @@ async function execWithCredentials(args, projectId, gacFilename, opts) {
9296892969
console.log("Retrying deploy with the --debug flag for better error output");
9296992970
await execWithCredentials(args, projectId, gacFilename, {
9297092971
debug: true,
92971-
firebaseToolsVersion
92972+
firebaseToolsVersion,
92973+
force
9297292974
});
9297392975
} else {
9297492976
throw e;
@@ -92982,10 +92984,12 @@ async function deployPreview(gacFilename, deployConfig) {
9298292984
channelId,
9298392985
target,
9298492986
expires,
92985-
firebaseToolsVersion
92987+
firebaseToolsVersion,
92988+
force
9298692989
} = deployConfig;
9298792990
const deploymentText = await execWithCredentials(["hosting:channel:deploy", channelId, ...(target ? ["--only", target] : []), ...(expires ? ["--expires", expires] : [])], projectId, gacFilename, {
92988-
firebaseToolsVersion
92991+
firebaseToolsVersion,
92992+
force
9298992993
});
9299092994
const deploymentResult = JSON.parse(deploymentText.trim());
9299192995
return deploymentResult;
@@ -92994,10 +92998,12 @@ async function deployProductionSite(gacFilename, productionDeployConfig) {
9299492998
const {
9299592999
projectId,
9299693000
target,
92997-
firebaseToolsVersion
93001+
firebaseToolsVersion,
93002+
force
9299893003
} = productionDeployConfig;
9299993004
const deploymentText = await execWithCredentials(["deploy", "--only", `hosting${target ? ":" + target : ""}`], projectId, gacFilename, {
93000-
firebaseToolsVersion
93005+
firebaseToolsVersion,
93006+
force
9300193007
});
9300293008
const deploymentResult = JSON.parse(deploymentText);
9300393009
return deploymentResult;
@@ -93181,6 +93187,7 @@ const entryPoint = core.getInput("entryPoint");
9318193187
const target = core.getInput("target");
9318293188
const firebaseToolsVersion = core.getInput("firebaseToolsVersion");
9318393189
const disableComment = core.getInput("disableComment");
93190+
const force = core.getInput("force") === "true";
9318493191
async function run() {
9318593192
const isPullRequest = !!github.context.payload.pull_request;
9318693193
let finish = details => console.log(details);
@@ -93212,7 +93219,8 @@ async function run() {
9321293219
const deployment = await deployProductionSite(gacFilename, {
9321393220
projectId,
9321493221
target,
93215-
firebaseToolsVersion
93222+
firebaseToolsVersion,
93223+
force
9321693224
});
9321793225
if (deployment.status === "error") {
9321893226
throw Error(deployment.error);
@@ -93237,7 +93245,8 @@ async function run() {
9323793245
expires,
9323893246
channelId,
9323993247
target,
93240-
firebaseToolsVersion
93248+
firebaseToolsVersion,
93249+
force
9324193250
});
9324293251
if (deployment.status === "error") {
9324393252
throw Error(deployment.error);

src/deploy.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,18 @@ type DeployConfig = {
4545
target?: string;
4646
// Optional version specification for firebase-tools. Defaults to `latest`.
4747
firebaseToolsVersion?: string;
48+
force?: boolean;
4849
};
4950

5051
export type ChannelDeployConfig = DeployConfig & {
5152
expires: string;
5253
channelId: string;
54+
force?: boolean;
5355
};
5456

55-
export type ProductionDeployConfig = DeployConfig & {};
57+
export type ProductionDeployConfig = DeployConfig & {
58+
force?: boolean;
59+
};
5660

5761
export function interpretChannelDeployResult(
5862
deployResult: ChannelSuccessResult
@@ -74,18 +78,20 @@ async function execWithCredentials(
7478
args: string[],
7579
projectId,
7680
gacFilename,
77-
opts: { debug?: boolean; firebaseToolsVersion?: string }
81+
opts: { debug?: boolean; firebaseToolsVersion?: string; force?: boolean }
7882
) {
7983
let deployOutputBuf: Buffer[] = [];
8084
const debug = opts.debug || false;
8185
const firebaseToolsVersion = opts.firebaseToolsVersion || "latest";
86+
const force = opts.force;
8287

8388
try {
8489
await exec(
8590
`npx firebase-tools@${firebaseToolsVersion}`,
8691
[
8792
...args,
8893
...(projectId ? ["--project", projectId] : []),
94+
...(force ? ["--force"] : []),
8995
debug
9096
? "--debug" // gives a more thorough error message
9197
: "--json", // allows us to easily parse the output
@@ -114,6 +120,7 @@ async function execWithCredentials(
114120
await execWithCredentials(args, projectId, gacFilename, {
115121
debug: true,
116122
firebaseToolsVersion,
123+
force,
117124
});
118125
} else {
119126
throw e;
@@ -129,7 +136,7 @@ export async function deployPreview(
129136
gacFilename: string,
130137
deployConfig: ChannelDeployConfig
131138
) {
132-
const { projectId, channelId, target, expires, firebaseToolsVersion } =
139+
const { projectId, channelId, target, expires, firebaseToolsVersion, force } =
133140
deployConfig;
134141

135142
const deploymentText = await execWithCredentials(
@@ -141,7 +148,7 @@ export async function deployPreview(
141148
],
142149
projectId,
143150
gacFilename,
144-
{ firebaseToolsVersion }
151+
{ firebaseToolsVersion, force }
145152
);
146153

147154
const deploymentResult = JSON.parse(deploymentText.trim()) as
@@ -155,13 +162,14 @@ export async function deployProductionSite(
155162
gacFilename,
156163
productionDeployConfig: ProductionDeployConfig
157164
) {
158-
const { projectId, target, firebaseToolsVersion } = productionDeployConfig;
165+
const { projectId, target, firebaseToolsVersion, force } =
166+
productionDeployConfig;
159167

160168
const deploymentText = await execWithCredentials(
161169
["deploy", "--only", `hosting${target ? ":" + target : ""}`],
162170
projectId,
163171
gacFilename,
164-
{ firebaseToolsVersion }
172+
{ firebaseToolsVersion, force }
165173
);
166174

167175
const deploymentResult = JSON.parse(deploymentText) as

src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ const entryPoint = getInput("entryPoint");
5151
const target = getInput("target");
5252
const firebaseToolsVersion = getInput("firebaseToolsVersion");
5353
const disableComment = getInput("disableComment");
54+
const force = getInput("force") === "true";
5455

5556
async function run() {
5657
const isPullRequest = !!context.payload.pull_request;
@@ -94,6 +95,7 @@ async function run() {
9495
projectId,
9596
target,
9697
firebaseToolsVersion,
98+
force,
9799
});
98100
if (deployment.status === "error") {
99101
throw Error((deployment as ErrorResult).error);
@@ -122,6 +124,7 @@ async function run() {
122124
channelId,
123125
target,
124126
firebaseToolsVersion,
127+
force,
125128
});
126129

127130
if (deployment.status === "error") {

test/deploy.test.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@ const baseLiveDeployConfig: ProductionDeployConfig = {
2525
projectId: "my-project",
2626
};
2727

28+
const forceProductionDeployConfig: ProductionDeployConfig = {
29+
projectId: "my-project",
30+
force: true,
31+
};
32+
33+
const forcePreviewDeployConfig: ChannelDeployConfig = {
34+
projectId: "my-project",
35+
channelId: "my-channel",
36+
expires: undefined,
37+
force: true,
38+
};
39+
2840
async function fakeExecFail(
2941
mainCommand: string,
3042
args: string[],
@@ -128,6 +140,48 @@ describe("deploy", () => {
128140
});
129141
});
130142

143+
describe("deploy to preview channel with force flag", () => {
144+
it("calls exec and interprets the output, including the --force flag when force is true", async () => {
145+
// @ts-ignore read-only property
146+
exec.exec = jest.fn(fakeExec);
147+
148+
const deployOutput: ChannelSuccessResult = (await deployPreview(
149+
"my-file",
150+
forcePreviewDeployConfig
151+
)) as ChannelSuccessResult;
152+
153+
expect(exec.exec).toBeCalled();
154+
expect(deployOutput).toEqual(channelSingleSiteSuccess);
155+
156+
// Check the arguments that exec was called with
157+
// @ts-ignore Jest adds a magic "mock" property
158+
const args = exec.exec.mock.calls;
159+
const deployFlags = args[0][1];
160+
expect(deployFlags).toContain("hosting:channel:deploy");
161+
expect(deployFlags).toContain("--force");
162+
});
163+
164+
it("specifies a target when one is provided", async () => {
165+
// @ts-ignore read-only property
166+
exec.exec = jest.fn(fakeExec);
167+
168+
const config: ChannelDeployConfig = {
169+
...forcePreviewDeployConfig,
170+
target: "my-second-site",
171+
};
172+
173+
await deployPreview("my-file", config);
174+
175+
// Check the arguments that exec was called with
176+
// @ts-ignore Jest adds a magic "mock" property
177+
const args = exec.exec.mock.calls;
178+
const deployFlags = args[0][1];
179+
expect(deployFlags).toContain("--only");
180+
expect(deployFlags).toContain("my-second-site");
181+
expect(deployFlags).toContain("--force");
182+
});
183+
});
184+
131185
describe("deploy to live channel", () => {
132186
it("calls exec and interprets the output", async () => {
133187
// @ts-ignore read-only property
@@ -150,4 +204,29 @@ describe("deploy", () => {
150204
expect(deployFlags).toContain("hosting");
151205
});
152206
});
207+
208+
describe("deploy to live channel with force flag", () => {
209+
it("includes --force flag when force is true for deploy", async () => {
210+
// @ts-ignore read-only property
211+
exec.exec = jest.fn(fakeExec);
212+
213+
const forceDeployOutput: ProductionSuccessResult =
214+
(await deployProductionSite(
215+
"my-file",
216+
forceProductionDeployConfig
217+
)) as ProductionSuccessResult;
218+
219+
expect(exec.exec).toBeCalled();
220+
expect(forceDeployOutput).toEqual(liveDeploySingleSiteSuccess);
221+
222+
// Check the arguments that exec was called with
223+
// @ts-ignore Jest adds a magic "mock" property
224+
const args = exec.exec.mock.calls;
225+
const deployFlags = args[0][1];
226+
expect(deployFlags).toContain("deploy");
227+
expect(deployFlags).toContain("--only");
228+
expect(deployFlags).toContain("hosting");
229+
expect(deployFlags).toContain("--force");
230+
});
231+
});
153232
});

0 commit comments

Comments
 (0)