Skip to content
Merged
21 changes: 8 additions & 13 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,16 @@ inputs:
default: "7d"
required: false
projectId:
description:
"The project to deploy to. If you leave this blank, make sure you check in
a .firebaserc file"
description: "The project to deploy to. If you leave this blank, make sure you check in a .firebaserc file"
required: false
channelId:
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."
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."
required: false
target:
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."
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."
required: false
entryPoint:
description:
"The location of your firebase.json file, relative to the root of your
directory"
description: "The location of your firebase.json file, relative to the root of your directory"
default: "."
required: false
firebaseToolsVersion:
Expand All @@ -64,6 +55,10 @@ inputs:
Disable auto-commenting with the preview channel URL to the pull request
default: "false"
required: false
force:
description: "Pass 'true' to use the --force flag with firebase deploy, which is useful if you need to make changes that may increase your minimum bill like deploying more than 0 minimum instances of a function."
default: "false"
required: false
outputs:
urls:
description: The url(s) deployed to
Expand Down
64 changes: 55 additions & 9 deletions bin/action.min.js
Original file line number Diff line number Diff line change
Expand Up @@ -92946,8 +92946,9 @@ async function execWithCredentials(args, projectId, gacFilename, opts) {
let deployOutputBuf = [];
const debug = opts.debug || false;
const firebaseToolsVersion = opts.firebaseToolsVersion || "latest";
const force = opts.force;
try {
await exec_1.exec(`npx firebase-tools@${firebaseToolsVersion}`, [...args, ...(projectId ? ["--project", projectId] : []), debug ? "--debug" // gives a more thorough error message
await exec_1.exec(`npx firebase-tools@${firebaseToolsVersion}`, [...args, ...(projectId ? ["--project", projectId] : []), ...(force ? ["--force"] : []), debug ? "--debug" // gives a more thorough error message
: "--json" // allows us to easily parse the output
], {
listeners: {
Expand All @@ -92968,25 +92969,27 @@ async function execWithCredentials(args, projectId, gacFilename, opts) {
console.log("Retrying deploy with the --debug flag for better error output");
await execWithCredentials(args, projectId, gacFilename, {
debug: true,
firebaseToolsVersion
firebaseToolsVersion,
force
});
} else {
throw e;
}
}
return deployOutputBuf.length ? deployOutputBuf[deployOutputBuf.length - 1].toString("utf-8") : ""; // output from the CLI
}

async function deployPreview(gacFilename, deployConfig) {
const {
projectId,
channelId,
target,
expires,
firebaseToolsVersion
firebaseToolsVersion,
force
} = deployConfig;
const deploymentText = await execWithCredentials(["hosting:channel:deploy", channelId, ...(target ? ["--only", target] : []), ...(expires ? ["--expires", expires] : [])], projectId, gacFilename, {
firebaseToolsVersion
firebaseToolsVersion,
force
});
const deploymentResult = JSON.parse(deploymentText.trim());
return deploymentResult;
Expand All @@ -92995,10 +92998,26 @@ async function deployProductionSite(gacFilename, productionDeployConfig) {
const {
projectId,
target,
firebaseToolsVersion
firebaseToolsVersion,
force
} = productionDeployConfig;
const deploymentText = await execWithCredentials(["deploy", "--only", `hosting${target ? ":" + target : ""}`], projectId, gacFilename, {
firebaseToolsVersion
firebaseToolsVersion,
force
});
const deploymentResult = JSON.parse(deploymentText);
return deploymentResult;
}
async function deployWithForce(gacFilename, deployConfig) {
const {
projectId,
target,
firebaseToolsVersion,
force
} = deployConfig;
const deploymentText = await execWithCredentials(["deploy", "--only", `hosting${target ? ":" + target : ""}`, "--force"], projectId, gacFilename, {
firebaseToolsVersion,
force
});
const deploymentResult = JSON.parse(deploymentText);
return deploymentResult;
Expand Down Expand Up @@ -93182,6 +93201,7 @@ const entryPoint = core.getInput("entryPoint");
const target = core.getInput("target");
const firebaseToolsVersion = core.getInput("firebaseToolsVersion");
const disableComment = core.getInput("disableComment");
const force = core.getInput("force") === "true";
async function run() {
const isPullRequest = !!github.context.payload.pull_request;
let finish = details => console.log(details);
Expand All @@ -93208,12 +93228,37 @@ async function run() {
const gacFilename = await createGacFile(googleApplicationCredentials);
console.log("Created a temporary file with Application Default Credentials.");
core.endGroup();
if (force) {
core.startGroup("Deploying with force flag");
const deployment = await deployWithForce(gacFilename, {
projectId,
target,
firebaseToolsVersion,
force
});
if (deployment.status === "error") {
throw Error(deployment.error);
}
core.endGroup();
const hostname = target ? `${target}.web.app` : `${projectId}.web.app`;
const url = `https://${hostname}/`;
await finish({
details_url: url,
conclusion: "success",
output: {
title: `Production deploy succeeded`,
summary: `[${hostname}](${url})`
}
});
return;
}
if (isProductionDeploy) {
core.startGroup("Deploying to production site");
const deployment = await deployProductionSite(gacFilename, {
projectId,
target,
firebaseToolsVersion
firebaseToolsVersion,
force
});
if (deployment.status === "error") {
throw Error(deployment.error);
Expand All @@ -93238,7 +93283,8 @@ async function run() {
expires,
channelId,
target,
firebaseToolsVersion
firebaseToolsVersion,
force
});
if (deployment.status === "error") {
throw Error(deployment.error);
Expand Down
46 changes: 41 additions & 5 deletions src/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,30 @@ export type ProductionSuccessResult = {
};
};

export type ForceSuccessResult = {
status: "success";
result: {
hosting: string | string[];
};
};

type DeployConfig = {
projectId: string;
target?: string;
// Optional version specification for firebase-tools. Defaults to `latest`.
firebaseToolsVersion?: string;
force?: boolean;
};

export type ChannelDeployConfig = DeployConfig & {
expires: string;
channelId: string;
};

export type ForceDeployConfig = DeployConfig & {
force: boolean;
};

export type ProductionDeployConfig = DeployConfig & {};

export function interpretChannelDeployResult(
Expand All @@ -74,18 +86,20 @@ async function execWithCredentials(
args: string[],
projectId,
gacFilename,
opts: { debug?: boolean; firebaseToolsVersion?: string }
opts: { debug?: boolean; firebaseToolsVersion?: string; force?: boolean }
) {
let deployOutputBuf: Buffer[] = [];
const debug = opts.debug || false;
const firebaseToolsVersion = opts.firebaseToolsVersion || "latest";
const force = opts.force;

try {
await exec(
`npx firebase-tools@${firebaseToolsVersion}`,
[
...args,
...(projectId ? ["--project", projectId] : []),
...(force ? ["--force"] : []),
debug
? "--debug" // gives a more thorough error message
: "--json", // allows us to easily parse the output
Expand Down Expand Up @@ -114,6 +128,7 @@ async function execWithCredentials(
await execWithCredentials(args, projectId, gacFilename, {
debug: true,
firebaseToolsVersion,
force,
});
} else {
throw e;
Expand All @@ -129,7 +144,7 @@ export async function deployPreview(
gacFilename: string,
deployConfig: ChannelDeployConfig
) {
const { projectId, channelId, target, expires, firebaseToolsVersion } =
const { projectId, channelId, target, expires, firebaseToolsVersion, force } =
deployConfig;

const deploymentText = await execWithCredentials(
Expand All @@ -141,7 +156,7 @@ export async function deployPreview(
],
projectId,
gacFilename,
{ firebaseToolsVersion }
{ firebaseToolsVersion, force }
);

const deploymentResult = JSON.parse(deploymentText.trim()) as
Expand All @@ -155,13 +170,14 @@ export async function deployProductionSite(
gacFilename,
productionDeployConfig: ProductionDeployConfig
) {
const { projectId, target, firebaseToolsVersion } = productionDeployConfig;
const { projectId, target, firebaseToolsVersion, force } =
productionDeployConfig;

const deploymentText = await execWithCredentials(
["deploy", "--only", `hosting${target ? ":" + target : ""}`],
projectId,
gacFilename,
{ firebaseToolsVersion }
{ firebaseToolsVersion, force }
);

const deploymentResult = JSON.parse(deploymentText) as
Expand All @@ -170,3 +186,23 @@ export async function deployProductionSite(

return deploymentResult;
}

export async function deployWithForce(
gacFilename,
deployConfig: ForceDeployConfig
) {
const { projectId, target, firebaseToolsVersion, force } = deployConfig;

const deploymentText = await execWithCredentials(
["deploy", "--only", `hosting${target ? ":" + target : ""}`, "--force"],
projectId,
gacFilename,
{ firebaseToolsVersion, force }
);

const deploymentResult = JSON.parse(deploymentText) as
| ForceSuccessResult
| ErrorResult;

return deploymentResult;
}
30 changes: 30 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { createGacFile } from "./createGACFile";
import {
deployPreview,
deployProductionSite,
deployWithForce,
ErrorResult,
interpretChannelDeployResult,
} from "./deploy";
Expand All @@ -51,6 +52,7 @@ const entryPoint = getInput("entryPoint");
const target = getInput("target");
const firebaseToolsVersion = getInput("firebaseToolsVersion");
const disableComment = getInput("disableComment");
const force = getInput("force") === "true";

async function run() {
const isPullRequest = !!context.payload.pull_request;
Expand Down Expand Up @@ -88,12 +90,39 @@ async function run() {
);
endGroup();

if (force) {
startGroup("Deploying with force flag");
const deployment = await deployWithForce(gacFilename, {
projectId,
target,
firebaseToolsVersion,
force,
});
if (deployment.status === "error") {
throw Error((deployment as ErrorResult).error);
}
endGroup();

const hostname = target ? `${target}.web.app` : `${projectId}.web.app`;
const url = `https://${hostname}/`;
await finish({
details_url: url,
conclusion: "success",
output: {
title: `Production deploy succeeded`,
summary: `[${hostname}](${url})`,
},
});
return;
}

if (isProductionDeploy) {
startGroup("Deploying to production site");
const deployment = await deployProductionSite(gacFilename, {
projectId,
target,
firebaseToolsVersion,
force,
});
if (deployment.status === "error") {
throw Error((deployment as ErrorResult).error);
Expand Down Expand Up @@ -122,6 +151,7 @@ async function run() {
channelId,
target,
firebaseToolsVersion,
force,
});

if (deployment.status === "error") {
Expand Down
Loading