mirror of
https://github.com/cloudflare/wrangler-action.git
synced 2024-11-24 02:44:45 +01:00
Merge pull request #161 from cloudflare/cina/throw-on-failures
Refactor error handling to stop execution on errors
This commit is contained in:
commit
47d4afdbd8
10 changed files with 138 additions and 166 deletions
5
.changeset/stale-spiders-turn.md
Normal file
5
.changeset/stale-spiders-turn.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"wrangler-action": patch
|
||||
---
|
||||
|
||||
Refactored error handling to stop execution when action fails. Previously, the action would continue executing to completion if one of the steps encountered an error. Fixes #160.
|
1
.github/workflows/deploy.yml
vendored
1
.github/workflows/deploy.yml
vendored
|
@ -51,6 +51,7 @@ jobs:
|
|||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
environment: dev
|
||||
preCommands: npx wrangler deploy --env dev # https://github.com/cloudflare/wrangler-action/issues/162
|
||||
secrets: |
|
||||
SECRET1
|
||||
SECRET2
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
process.env.INPUT_QUIET ??= "false";
|
|
@ -25,8 +25,8 @@
|
|||
"scripts": {
|
||||
"build": "npx ncc build ./src/index.ts && mv ./dist/index.js ./dist/index.mjs",
|
||||
"test": "vitest",
|
||||
"format": "prettier --write . --ignore-path ./dist/**",
|
||||
"check": "prettier --check . --ignore-path ./dist/**"
|
||||
"format": "prettier --write .",
|
||||
"check": "prettier --check ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.0"
|
||||
|
|
214
src/index.ts
214
src/index.ts
|
@ -9,8 +9,7 @@ import {
|
|||
getBooleanInput,
|
||||
} from "@actions/core";
|
||||
import { execSync, exec } from "node:child_process";
|
||||
import { existsSync } from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { checkWorkingDirectory, getNpxCmd, semverCompare } from "./utils";
|
||||
import * as util from "node:util";
|
||||
const execAsync = util.promisify(exec);
|
||||
|
||||
|
@ -31,10 +30,6 @@ const config = {
|
|||
QUIET_MODE: getBooleanInput("quiet"),
|
||||
} as const;
|
||||
|
||||
function getNpxCmd() {
|
||||
return process.env.RUNNER_OS === "Windows" ? "npx.cmd" : "npx";
|
||||
}
|
||||
|
||||
function info(message: string, bypass?: boolean): void {
|
||||
if (!config.QUIET_MODE || bypass) {
|
||||
originalInfo(message);
|
||||
|
@ -59,34 +54,19 @@ function endGroup(): void {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function to compare two semver versions. If the second arg is greater than the first arg, it returns true.
|
||||
*/
|
||||
function semverCompare(version1: string, version2: string) {
|
||||
if (version2 === "latest") return true;
|
||||
|
||||
const version1Parts = version1.split(".");
|
||||
const version2Parts = version2.split(".");
|
||||
|
||||
for (const version1Part of version1Parts) {
|
||||
const version2Part = version2Parts.shift();
|
||||
|
||||
if (version1Part !== version2Part && version2Part) {
|
||||
return version1Part < version2Part ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
installWrangler();
|
||||
authenticationSetup();
|
||||
await execCommands(getMultilineInput("preCommands"), "pre");
|
||||
await uploadSecrets();
|
||||
await wranglerCommands();
|
||||
await execCommands(getMultilineInput("postCommands"), "post");
|
||||
info("🏁 Wrangler Action completed", true);
|
||||
try {
|
||||
installWrangler();
|
||||
authenticationSetup();
|
||||
await execCommands(getMultilineInput("preCommands"), "pre");
|
||||
await uploadSecrets();
|
||||
await wranglerCommands();
|
||||
await execCommands(getMultilineInput("postCommands"), "post");
|
||||
info("🏁 Wrangler Action completed", true);
|
||||
} catch (err: unknown) {
|
||||
err instanceof Error && error(err.message);
|
||||
setFailed("🚨 Action failed");
|
||||
}
|
||||
}
|
||||
|
||||
async function runProcess(
|
||||
|
@ -103,29 +83,14 @@ async function runProcess(
|
|||
} catch (err: any) {
|
||||
err.stdout && info(err.stdout.toString());
|
||||
err.stderr && error(err.stderr.toString(), true);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
function checkWorkingDirectory(workingDirectory = ".") {
|
||||
try {
|
||||
const normalizedPath = path.normalize(workingDirectory);
|
||||
if (existsSync(normalizedPath)) {
|
||||
return normalizedPath;
|
||||
} else {
|
||||
setFailed(`🚨 Directory ${workingDirectory} does not exist.`);
|
||||
}
|
||||
} catch (error) {
|
||||
setFailed(
|
||||
`🚨 While checking/creating directory ${workingDirectory} received ${error}`,
|
||||
);
|
||||
throw new Error(`\`${command}\` returned non-zero exit code.`);
|
||||
}
|
||||
}
|
||||
|
||||
function installWrangler() {
|
||||
if (config["WRANGLER_VERSION"].startsWith("1")) {
|
||||
setFailed(
|
||||
`🚨 Wrangler v1 is no longer supported by this action. Please use major version 2 or greater`,
|
||||
throw new Error(
|
||||
`Wrangler v1 is no longer supported by this action. Please use major version 2 or greater`,
|
||||
);
|
||||
}
|
||||
startGroup("📥 Installing Wrangler");
|
||||
|
@ -145,27 +110,26 @@ async function execCommands(commands: string[], cmdType: string) {
|
|||
if (!commands.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
startGroup(`🚀 Running ${cmdType}Commands`);
|
||||
try {
|
||||
const arrPromises = commands.map(async (command) => {
|
||||
const cmd = command.startsWith("wrangler")
|
||||
? `${getNpxCmd()} ${command}`
|
||||
: command;
|
||||
|
||||
const arrPromises = commands.map(async (command) => {
|
||||
const cmd = command.startsWith("wrangler")
|
||||
? `${getNpxCmd()} ${command}`
|
||||
: command;
|
||||
info(`🚀 Executing command: ${cmd}`);
|
||||
|
||||
info(`🚀 Executing command: ${cmd}`);
|
||||
|
||||
return await runProcess(cmd, {
|
||||
cwd: config["workingDirectory"],
|
||||
env: process.env,
|
||||
return await runProcess(cmd, {
|
||||
cwd: config["workingDirectory"],
|
||||
env: process.env,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
await Promise.all(arrPromises).catch((result) => {
|
||||
result.stdout && info(result.stdout.toString());
|
||||
result.stderr && error(result.stderr.toString(), true);
|
||||
setFailed(`🚨 ${cmdType}Commands failed`);
|
||||
});
|
||||
endGroup();
|
||||
await Promise.all(arrPromises);
|
||||
} finally {
|
||||
endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -173,12 +137,12 @@ async function execCommands(commands: string[], cmdType: string) {
|
|||
*/
|
||||
function getSecret(secret: string) {
|
||||
if (!secret) {
|
||||
setFailed("No secret provided");
|
||||
throw new Error("Secret name cannot be blank.");
|
||||
}
|
||||
|
||||
const value = process.env[secret];
|
||||
if (!value) {
|
||||
setFailed(`Secret ${secret} not found`);
|
||||
throw new Error(`Value for secret ${secret} not found.`);
|
||||
}
|
||||
|
||||
return value;
|
||||
|
@ -189,26 +153,22 @@ async function legacyUploadSecrets(
|
|||
environment?: string,
|
||||
workingDirectory?: string,
|
||||
) {
|
||||
try {
|
||||
const arrPromises = secrets
|
||||
.map((secret) => {
|
||||
const command = `echo ${getSecret(
|
||||
secret,
|
||||
)} | ${getNpxCmd()} wrangler secret put ${secret}`;
|
||||
return environment ? command.concat(` --env ${environment}`) : command;
|
||||
})
|
||||
.map(
|
||||
async (command) =>
|
||||
await execAsync(command, {
|
||||
cwd: workingDirectory,
|
||||
env: process.env,
|
||||
}),
|
||||
);
|
||||
const arrPromises = secrets
|
||||
.map((secret) => {
|
||||
const command = `echo ${getSecret(
|
||||
secret,
|
||||
)} | ${getNpxCmd()} wrangler secret put ${secret}`;
|
||||
return environment ? command.concat(` --env ${environment}`) : command;
|
||||
})
|
||||
.map(
|
||||
async (command) =>
|
||||
await execAsync(command, {
|
||||
cwd: workingDirectory,
|
||||
env: process.env,
|
||||
}),
|
||||
);
|
||||
|
||||
await Promise.all(arrPromises);
|
||||
} catch {
|
||||
setFailed(`🚨 Error uploading secrets`);
|
||||
}
|
||||
await Promise.all(arrPromises);
|
||||
}
|
||||
|
||||
async function uploadSecrets() {
|
||||
|
@ -219,9 +179,10 @@ async function uploadSecrets() {
|
|||
if (!secrets.length) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
startGroup("🔑 Uploading Secrets");
|
||||
|
||||
startGroup("🔑 Uploading secrets...");
|
||||
|
||||
try {
|
||||
if (semverCompare(config["WRANGLER_VERSION"], "3.4.0"))
|
||||
return legacyUploadSecrets(secrets, environment, workingDirectory);
|
||||
|
||||
|
@ -244,9 +205,11 @@ async function uploadSecrets() {
|
|||
env: process.env,
|
||||
stdio: "ignore",
|
||||
});
|
||||
|
||||
info(`✅ Uploaded secrets`);
|
||||
} catch {
|
||||
setFailed(`🚨 Error uploading secrets`);
|
||||
} catch (err) {
|
||||
error(`❌ Upload failed`);
|
||||
throw new Error(`Failed to upload secrets.`);
|
||||
} finally {
|
||||
endGroup();
|
||||
}
|
||||
|
@ -258,7 +221,7 @@ function getVarArgs() {
|
|||
if (process.env[envVar] && process.env[envVar]?.length !== 0) {
|
||||
return `${envVar}:${process.env[envVar]!}`;
|
||||
} else {
|
||||
setFailed(`🚨 ${envVar} not found in variables.`);
|
||||
throw new Error(`Value for var ${envVar} not found in environment.`);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -267,47 +230,45 @@ function getVarArgs() {
|
|||
|
||||
async function wranglerCommands() {
|
||||
startGroup("🚀 Running Wrangler Commands");
|
||||
const commands = config["COMMANDS"];
|
||||
const environment = config["ENVIRONMENT"];
|
||||
try {
|
||||
const commands = config["COMMANDS"];
|
||||
const environment = config["ENVIRONMENT"];
|
||||
|
||||
if (!commands.length) {
|
||||
const wranglerVersion = config["WRANGLER_VERSION"];
|
||||
const deployCommand = semverCompare("2.20.0", wranglerVersion)
|
||||
? "deploy"
|
||||
: "publish";
|
||||
commands.push(deployCommand);
|
||||
}
|
||||
|
||||
const arrPromises = commands.map(async (command) => {
|
||||
if (environment.length > 0 && !command.includes(`--env`)) {
|
||||
command = command.concat(` --env ${environment}`);
|
||||
if (!commands.length) {
|
||||
const wranglerVersion = config["WRANGLER_VERSION"];
|
||||
const deployCommand = semverCompare("2.20.0", wranglerVersion)
|
||||
? "deploy"
|
||||
: "publish";
|
||||
commands.push(deployCommand);
|
||||
}
|
||||
|
||||
const cmd = `${getNpxCmd()} wrangler ${command} ${
|
||||
(command.startsWith("deploy") || command.startsWith("publish")) &&
|
||||
!command.includes(`--var`)
|
||||
? getVarArgs()
|
||||
: ""
|
||||
}`.trim();
|
||||
const arrPromises = commands.map(async (command) => {
|
||||
if (environment.length > 0 && !command.includes(`--env`)) {
|
||||
command = command.concat(` --env ${environment}`);
|
||||
}
|
||||
|
||||
info(`🚀 Executing command: ${cmd}`);
|
||||
const cmd = `${getNpxCmd()} wrangler ${command} ${
|
||||
(command.startsWith("deploy") || command.startsWith("publish")) &&
|
||||
!command.includes(`--var`)
|
||||
? getVarArgs()
|
||||
: ""
|
||||
}`.trim();
|
||||
|
||||
return await runProcess(cmd, {
|
||||
cwd: config["workingDirectory"],
|
||||
env: process.env,
|
||||
info(`🚀 Executing command: ${cmd}`);
|
||||
|
||||
return await runProcess(cmd, {
|
||||
cwd: config["workingDirectory"],
|
||||
env: process.env,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
await Promise.all(arrPromises).catch((result) => {
|
||||
result.stdout && info(result.stdout.toString());
|
||||
result.stderr && error(result.stderr.toString());
|
||||
setFailed(`🚨 Command failed`);
|
||||
});
|
||||
|
||||
endGroup();
|
||||
await Promise.all(arrPromises);
|
||||
} finally {
|
||||
endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(() => setFailed("🚨 Action failed"));
|
||||
main();
|
||||
|
||||
export {
|
||||
wranglerCommands,
|
||||
|
@ -315,7 +276,4 @@ export {
|
|||
uploadSecrets,
|
||||
authenticationSetup,
|
||||
installWrangler,
|
||||
checkWorkingDirectory,
|
||||
getNpxCmd,
|
||||
semverCompare,
|
||||
};
|
||||
|
|
|
@ -1,18 +1,7 @@
|
|||
import { expect, test, describe } from "vitest";
|
||||
import { checkWorkingDirectory, getNpxCmd, semverCompare } from "./index";
|
||||
import { checkWorkingDirectory, getNpxCmd, semverCompare } from "./utils";
|
||||
import path from "node:path";
|
||||
|
||||
const config = {
|
||||
WRANGLER_VERSION: "mockVersion",
|
||||
secrets: ["mockSercret", "mockSecretAgain"],
|
||||
workingDirectory: "./mockWorkingDirectory",
|
||||
CLOUDFLARE_API_TOKEN: "mockAPIToken",
|
||||
CLOUDFLARE_ACCOUNT_ID: "mockAccountID",
|
||||
ENVIRONMENT: undefined,
|
||||
VARS: ["mockVar", "mockVarAgain"],
|
||||
COMMANDS: ["mockCommand", "mockCommandAgain"],
|
||||
};
|
||||
|
||||
test("getNpxCmd ", async () => {
|
||||
process.env.RUNNER_OS = "Windows";
|
||||
expect(getNpxCmd()).toBe("npx.cmd");
|
||||
|
@ -47,18 +36,10 @@ describe("checkWorkingDirectory", () => {
|
|||
});
|
||||
|
||||
test("should fail if the directory does not exist", () => {
|
||||
try {
|
||||
checkWorkingDirectory("/does/not/exist");
|
||||
} catch (error) {
|
||||
expect(error.message).toMatchInlineSnapshot();
|
||||
}
|
||||
});
|
||||
|
||||
test("should fail if an error occurs while checking/creating the directory", () => {
|
||||
try {
|
||||
checkWorkingDirectory("/does/not/exist");
|
||||
} catch (error) {
|
||||
expect(error.message).toMatchInlineSnapshot();
|
||||
}
|
||||
expect(() =>
|
||||
checkWorkingDirectory("/does/not/exist"),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
'"Directory /does/not/exist does not exist."',
|
||||
);
|
||||
});
|
||||
});
|
35
src/utils.ts
Normal file
35
src/utils.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { existsSync } from "node:fs";
|
||||
import * as path from "node:path";
|
||||
|
||||
export function getNpxCmd() {
|
||||
return process.env.RUNNER_OS === "Windows" ? "npx.cmd" : "npx";
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function to compare two semver versions. If the second arg is greater than the first arg, it returns true.
|
||||
*/
|
||||
export function semverCompare(version1: string, version2: string) {
|
||||
if (version2 === "latest") return true;
|
||||
|
||||
const version1Parts = version1.split(".");
|
||||
const version2Parts = version2.split(".");
|
||||
|
||||
for (const version1Part of version1Parts) {
|
||||
const version2Part = version2Parts.shift();
|
||||
|
||||
if (version1Part !== version2Part && version2Part) {
|
||||
return version1Part < version2Part ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function checkWorkingDirectory(workingDirectory = ".") {
|
||||
const normalizedPath = path.normalize(workingDirectory);
|
||||
if (existsSync(normalizedPath)) {
|
||||
return normalizedPath;
|
||||
} else {
|
||||
throw new Error(`Directory ${workingDirectory} does not exist.`);
|
||||
}
|
||||
}
|
|
@ -9,12 +9,12 @@
|
|||
"isolatedModules": true,
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"lib": ["ESNext"],
|
||||
"types": ["node", "@cloudflare/workers-types"]
|
||||
},
|
||||
"exclude": ["node_modules", "**/*.test.ts"],
|
||||
"include": ["src/index.ts"]
|
||||
"include": ["src"]
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
import { defineConfig } from "vitest/dist/config";
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
setupFiles: ["./action-env-setup.ts"],
|
||||
},
|
||||
});
|
Loading…
Reference in a new issue