mirror of
https://github.com/cloudflare/wrangler-action.git
synced 2024-11-24 02:44:45 +01:00
Merge pull request #312 from cloudflare/maximo/unrevert-add-parity-with-wrangler-action
Unrevert add parity with wrangler action
This commit is contained in:
commit
4f4ff59d17
8 changed files with 277 additions and 30 deletions
5
.changeset/brave-wasps-greet.md
Normal file
5
.changeset/brave-wasps-greet.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"wrangler-action": minor
|
||||
---
|
||||
|
||||
This reapplies [303](https://github.com/cloudflare/wrangler-action/pull/303) add parity with pages-action for pages deploy outputs. Thanks @courtney-sims! - Support pages-deployment-id, pages-environment, pages-deployment-alias-url and deployment-url outputs for Pages deploys when wrangler version is >=3.81.0. deployment-alias-url was also deprecated in favour of pages-deployment-alias.
|
|
@ -262,7 +262,7 @@ Now when you run your workflow, you will see the full output of the Wrangler com
|
|||
|
||||
> Note: the `command-stderr` output variable is also available if you need to parse the standard error output of the Wrangler command.
|
||||
|
||||
### Using the `deployment-url` and `deployment-alias-url` Output Variables
|
||||
### Using the `deployment-url` and `pages-deployment-alias-url` Output Variables
|
||||
|
||||
If you are executing a Wrangler command that results in either a Workers or Pages deployment, you can utilize the `deployment-url` output variable to get the URL of the deployment. For example, if you want to print the deployment URL after deploying your application, you can do the following:
|
||||
|
||||
|
@ -287,14 +287,14 @@ The resulting output will look something like this:
|
|||
https://<your_pages_site>.pages.dev
|
||||
```
|
||||
|
||||
Pages deployments will also provide their alias URL (since Wrangler v3.78.0). You can use the `deployment-alias-url` output variable to get the URL of the deployment alias. This is useful for, for example, branch aliases for preview deployments.
|
||||
Pages deployments will also provide their alias URL (since Wrangler v3.78.0). You can use the `pages-deployment-alias-url` output variable to get the URL of the deployment alias. This is useful for, for example, branch aliases for preview deployments.
|
||||
|
||||
If the sample action above was used to deploy a branch other than main, you could use the following to get the branch URL:
|
||||
|
||||
```yaml
|
||||
- name: print deployment-alias-url
|
||||
- name: print pages-deployment-alias-url
|
||||
env:
|
||||
DEPLOYMENT_ALIAS_URL: ${{ steps.deploy.outputs.deployment-alias-url }}
|
||||
DEPLOYMENT_ALIAS_URL: ${{ steps.deploy.outputs.pages-deployment-alias-url }}
|
||||
run: echo $DEPLOYMENT_ALIAS_URL
|
||||
```
|
||||
|
||||
|
|
|
@ -51,5 +51,9 @@ outputs:
|
|||
description: "The error output of the Wrangler command (comes from stderr)"
|
||||
deployment-url:
|
||||
description: "If the command was a Workers or Pages deployment, this will be the URL of the deployment"
|
||||
deployment-alias-url:
|
||||
description: "If the command was a Workers or Pages deployment, this can be the URL of the deployment alias (if it exists) - needs wrangler >= 3.78.0"
|
||||
pages-deployment-alias-url:
|
||||
description: "If the command was a Pages deployment, this will be the URL of the deployment alias (if it exists) - needs wrangler >= 3.78.0"
|
||||
pages-deployment-id:
|
||||
description: "If the command was a Pages deployment, this will be the ID of the deployment - needs wrangler >= 3.81.0"
|
||||
pages-environment:
|
||||
description: "If the command was a Pages deployment, this will be the environment of the deployment - needs wrangler >= 3.81.0"
|
||||
|
|
27
package-lock.json
generated
27
package-lock.json
generated
|
@ -1,16 +1,17 @@
|
|||
{
|
||||
"name": "wrangler-action",
|
||||
"version": "3.7.0",
|
||||
"version": "3.11.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "wrangler-action",
|
||||
"version": "3.7.0",
|
||||
"version": "3.11.0",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/exec": "^1.1.1"
|
||||
"@actions/exec": "^1.1.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@changesets/changelog-github": "^0.4.8",
|
||||
|
@ -18,6 +19,7 @@
|
|||
"@cloudflare/workers-types": "^4.20231121.0",
|
||||
"@types/node": "^20.10.4",
|
||||
"@vercel/ncc": "^0.38.1",
|
||||
"mock-fs": "^5.4.0",
|
||||
"prettier": "^3.1.0",
|
||||
"semver": "^7.5.4",
|
||||
"typescript": "^5.3.3",
|
||||
|
@ -3110,6 +3112,16 @@
|
|||
"ufo": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mock-fs": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.4.0.tgz",
|
||||
"integrity": "sha512-3ROPnEMgBOkusBMYQUW2rnT3wZwsgfOKzJDLvx/TZ7FL1WmWvwSwn3j4aDR5fLDGtgcc1WF0Z1y0di7c9L4FKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
|
@ -5011,6 +5023,15 @@
|
|||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.23.8",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
|
||||
"integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,8 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/exec": "^1.1.1"
|
||||
"@actions/exec": "^1.1.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@changesets/changelog-github": "^0.4.8",
|
||||
|
@ -39,6 +40,7 @@
|
|||
"@types/node": "^20.10.4",
|
||||
"@vercel/ncc": "^0.38.1",
|
||||
"prettier": "^3.1.0",
|
||||
"mock-fs": "^5.4.0",
|
||||
"semver": "^7.5.4",
|
||||
"typescript": "^5.3.3",
|
||||
"vitest": "^1.0.3"
|
||||
|
|
84
src/index.ts
84
src/index.ts
|
@ -1,11 +1,11 @@
|
|||
import {
|
||||
debug,
|
||||
getBooleanInput,
|
||||
getInput,
|
||||
getMultilineInput,
|
||||
endGroup as originalEndGroup,
|
||||
error as originalError,
|
||||
info as originalInfo,
|
||||
debug,
|
||||
startGroup as originalStartGroup,
|
||||
setFailed,
|
||||
setOutput,
|
||||
|
@ -13,10 +13,13 @@ import {
|
|||
import { getExecOutput } from "@actions/exec";
|
||||
import semverEq from "semver/functions/eq";
|
||||
import { exec, execShell } from "./exec";
|
||||
import { checkWorkingDirectory, semverCompare } from "./utils";
|
||||
import { getPackageManager } from "./packageManagers";
|
||||
import { checkWorkingDirectory, semverCompare } from "./utils";
|
||||
import { getDetailedPagesDeployOutput } from "./wranglerArtifactManager";
|
||||
import { join } from "path";
|
||||
import { tmpdir } from "os";
|
||||
|
||||
const DEFAULT_WRANGLER_VERSION = "3.78.10";
|
||||
const DEFAULT_WRANGLER_VERSION = "3.81.0";
|
||||
|
||||
/**
|
||||
* A configuration object that contains all the inputs & immutable state for the action.
|
||||
|
@ -33,6 +36,7 @@ const config = {
|
|||
COMMANDS: getMultilineInput("command"),
|
||||
QUIET_MODE: getBooleanInput("quiet"),
|
||||
PACKAGE_MANAGER: getInput("packageManager"),
|
||||
WRANGLER_OUTPUT_DIR: `${join(tmpdir(), "wranglerArtifacts")}`,
|
||||
} as const;
|
||||
|
||||
const packageManager = getPackageManager(config.PACKAGE_MANAGER, {
|
||||
|
@ -276,6 +280,29 @@ async function uploadSecrets() {
|
|||
}
|
||||
}
|
||||
|
||||
// fallback to trying to extract the deployment-url and pages-deployment-alias-url from stdout for wranglerVersion < 3.81.0
|
||||
function extractDeploymentUrlsFromStdout(stdOut: string): {
|
||||
deploymentUrl?: string;
|
||||
aliasUrl?: string;
|
||||
} {
|
||||
let deploymentUrl = "";
|
||||
let aliasUrl = "";
|
||||
|
||||
// Try to extract the deployment URL
|
||||
const deploymentUrlMatch = stdOut.match(/https?:\/\/[a-zA-Z0-9-./]+/);
|
||||
if (deploymentUrlMatch && deploymentUrlMatch[0]) {
|
||||
deploymentUrl = deploymentUrlMatch[0].trim();
|
||||
}
|
||||
|
||||
// And also try to extract the alias URL (since wrangler@3.78.0)
|
||||
const aliasUrlMatch = stdOut.match(/alias URL: (https?:\/\/[a-zA-Z0-9-./]+)/);
|
||||
if (aliasUrlMatch && aliasUrlMatch[1]) {
|
||||
aliasUrl = aliasUrlMatch[1].trim();
|
||||
}
|
||||
|
||||
return { deploymentUrl, aliasUrl };
|
||||
}
|
||||
|
||||
async function wranglerCommands() {
|
||||
startGroup("🚀 Running Wrangler Commands");
|
||||
try {
|
||||
|
@ -312,7 +339,9 @@ async function wranglerCommands() {
|
|||
let stdOut = "";
|
||||
let stdErr = "";
|
||||
|
||||
// Construct the options for the exec command
|
||||
// set WRANGLER_OUTPUT_FILE_DIRECTORY env for exec
|
||||
process.env.WRANGLER_OUTPUT_FILE_DIRECTORY = config.WRANGLER_OUTPUT_DIR;
|
||||
|
||||
const options = {
|
||||
cwd: config["workingDirectory"],
|
||||
silent: config["QUIET_MODE"],
|
||||
|
@ -333,28 +362,43 @@ async function wranglerCommands() {
|
|||
setOutput("command-output", stdOut);
|
||||
setOutput("command-stderr", stdErr);
|
||||
|
||||
// Check if this command is a workers or pages deployment
|
||||
// Check if this command is a workers deployment
|
||||
if (command.startsWith("deploy") || command.startsWith("publish")) {
|
||||
const { deploymentUrl, aliasUrl } =
|
||||
extractDeploymentUrlsFromStdout(stdOut);
|
||||
setOutput("deployment-url", deploymentUrl);
|
||||
// DEPRECATED: deployment-alias-url in favour of pages-deployment-alias, drop in next wrangler-action major version change
|
||||
setOutput("deployment-alias-url", aliasUrl);
|
||||
setOutput("pages-deployment-alias-url", aliasUrl);
|
||||
}
|
||||
// Check if this command is a pages deployment
|
||||
if (
|
||||
command.startsWith("deploy") ||
|
||||
command.startsWith("publish") ||
|
||||
command.startsWith("pages publish") ||
|
||||
command.startsWith("pages deploy")
|
||||
) {
|
||||
// If this is a workers or pages deployment, try to extract the deployment URL
|
||||
let deploymentUrl = "";
|
||||
const deploymentUrlMatch = stdOut.match(/https?:\/\/[a-zA-Z0-9-./]+/);
|
||||
if (deploymentUrlMatch && deploymentUrlMatch[0]) {
|
||||
deploymentUrl = deploymentUrlMatch[0].trim();
|
||||
setOutput("deployment-url", deploymentUrl);
|
||||
}
|
||||
|
||||
// And also try to extract the alias URL (since wrangler@3.78.0)
|
||||
const aliasUrlMatch = stdOut.match(
|
||||
/alias URL: (https?:\/\/[a-zA-Z0-9-./]+)/,
|
||||
const pagesArtifactFields = await getDetailedPagesDeployOutput(
|
||||
config.WRANGLER_OUTPUT_DIR,
|
||||
);
|
||||
if (aliasUrlMatch && aliasUrlMatch.length == 2 && aliasUrlMatch[1]) {
|
||||
const aliasUrl = aliasUrlMatch[1].trim();
|
||||
|
||||
if (pagesArtifactFields) {
|
||||
setOutput("deployment-url", pagesArtifactFields.url);
|
||||
// DEPRECATED: deployment-alias-url in favour of pages-deployment-alias, drop in next wrangler-action major version change
|
||||
setOutput("deployment-alias-url", pagesArtifactFields.alias);
|
||||
setOutput("pages-deployment-alias-url", pagesArtifactFields.alias);
|
||||
setOutput("pages-deployment-id", pagesArtifactFields.deployment_id);
|
||||
setOutput("pages-environment", pagesArtifactFields.environment);
|
||||
} else {
|
||||
info(
|
||||
"Unable to find a WRANGLER_OUTPUT_DIR, environment and id fields will be unavailable for output. Have you updated wrangler to version >=3.81.0?",
|
||||
);
|
||||
// DEPRECATED: deployment-alias-url in favour of pages-deployment-alias, drop in next wrangler-action major version change
|
||||
const { deploymentUrl, aliasUrl } =
|
||||
extractDeploymentUrlsFromStdout(stdOut);
|
||||
|
||||
setOutput("deployment-url", deploymentUrl);
|
||||
// DEPRECATED: deployment-alias-url in favour of pages-deployment-alias, drop in next wrangler-action major version change
|
||||
setOutput("deployment-alias-url", aliasUrl);
|
||||
setOutput("pages-deployment-alias-url", aliasUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
85
src/wranglerArtifactManager.test.ts
Normal file
85
src/wranglerArtifactManager.test.ts
Normal file
|
@ -0,0 +1,85 @@
|
|||
import mock from "mock-fs";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
getDetailedPagesDeployOutput,
|
||||
getWranglerArtifacts,
|
||||
} from "./wranglerArtifactManager";
|
||||
|
||||
afterEach(async () => {
|
||||
mock.restore();
|
||||
});
|
||||
describe("wranglerArtifactsManager", () => {
|
||||
describe("getWranglerArtifacts()", async () => {
|
||||
it("Returns only wrangler output files from a given directory", async () => {
|
||||
mock({
|
||||
testOutputDir: {
|
||||
"wrangler-output-2024-10-17_18-48-40_463-2e6e83.json": `
|
||||
{"version": 1, "type":"wrangler-session", "wrangler_version":"3.81.0", "command_line_args":["what's up"], "log_file_path": "/here"}
|
||||
{"version": 1, "type":"pages-deploy-detailed", "environment":"production", "alias":"test.com", "deployment_id": "123", "url":"url.com"}`,
|
||||
"not-wrangler-output.json": "test",
|
||||
},
|
||||
});
|
||||
|
||||
const artifacts = await getWranglerArtifacts("./testOutputDir");
|
||||
|
||||
expect(artifacts).toEqual([
|
||||
"./testOutputDir/wrangler-output-2024-10-17_18-48-40_463-2e6e83.json",
|
||||
]);
|
||||
});
|
||||
it("Returns an empty list when the output directory doesn't exist", async () => {
|
||||
mock({
|
||||
notTheDirWeWant: {},
|
||||
});
|
||||
|
||||
const artifacts = await getWranglerArtifacts("./testOutputDir");
|
||||
expect(artifacts).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getDetailedPagesDeployOutput()", async () => {
|
||||
it("Returns only detailed pages deploy output from wrangler artifacts", async () => {
|
||||
mock({
|
||||
testOutputDir: {
|
||||
"wrangler-output-2024-10-17_18-48-40_463-2e6e83.json": `
|
||||
{"version": 1, "type":"wrangler-session", "wrangler_version":"3.81.0", "command_line_args":["what's up"], "log_file_path": "/here"}
|
||||
{"version": 1, "type":"pages-deploy-detailed", "pages_project": "project", "environment":"production", "alias":"test.com", "deployment_id": "123", "url":"url.com"}`,
|
||||
"not-wrangler-output.json": "test",
|
||||
},
|
||||
});
|
||||
|
||||
const artifacts = await getDetailedPagesDeployOutput("./testOutputDir");
|
||||
|
||||
expect(artifacts).toEqual({
|
||||
version: 1,
|
||||
pages_project: "project",
|
||||
type: "pages-deploy-detailed",
|
||||
url: "url.com",
|
||||
environment: "production",
|
||||
deployment_id: "123",
|
||||
alias: "test.com",
|
||||
});
|
||||
}),
|
||||
it("Skips artifact entries that are not parseable", async () => {
|
||||
mock({
|
||||
testOutputDir: {
|
||||
"wrangler-output-2024-10-17_18-48-40_463-2e6e83.json": `
|
||||
this line is invalid json.
|
||||
{"version": 1, "type":"pages-deploy-detailed", "pages_project": "project", "environment":"production", "alias":"test.com", "deployment_id": "123", "url":"url.com"}`,
|
||||
"not-wrangler-output.json": "test",
|
||||
},
|
||||
});
|
||||
|
||||
const artifacts = await getDetailedPagesDeployOutput("./testOutputDir");
|
||||
|
||||
expect(artifacts).toEqual({
|
||||
version: 1,
|
||||
type: "pages-deploy-detailed",
|
||||
pages_project: "project",
|
||||
url: "url.com",
|
||||
environment: "production",
|
||||
deployment_id: "123",
|
||||
alias: "test.com",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
86
src/wranglerArtifactManager.ts
Normal file
86
src/wranglerArtifactManager.ts
Normal file
|
@ -0,0 +1,86 @@
|
|||
import { access, open, readdir } from "fs/promises";
|
||||
import { z } from "zod";
|
||||
|
||||
const OutputEntryBase = z.object({
|
||||
version: z.literal(1),
|
||||
type: z.string(),
|
||||
});
|
||||
|
||||
const OutputEntryPagesDeployment = OutputEntryBase.merge(
|
||||
z.object({
|
||||
type: z.literal("pages-deploy-detailed"),
|
||||
pages_project: z.string().nullable(),
|
||||
deployment_id: z.string().nullable(),
|
||||
url: z.string().optional(),
|
||||
alias: z.string().optional(),
|
||||
environment: z.enum(["production", "preview"]),
|
||||
}),
|
||||
);
|
||||
|
||||
type OutputEntryPagesDeployment = z.infer<typeof OutputEntryPagesDeployment>;
|
||||
|
||||
/**
|
||||
* Parses file names in a directory to find wrangler artifact files
|
||||
*
|
||||
* @param artifactDirectory
|
||||
* @returns All artifact files from the directory
|
||||
*/
|
||||
export async function getWranglerArtifacts(
|
||||
artifactDirectory: string,
|
||||
): Promise<string[]> {
|
||||
try {
|
||||
await access(artifactDirectory);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
|
||||
// read files in asset directory
|
||||
const dirent = await readdir(artifactDirectory, {
|
||||
withFileTypes: true,
|
||||
recursive: false,
|
||||
});
|
||||
|
||||
// Match files to wrangler-output-<timestamp>-xxxxxx.json
|
||||
const regex = new RegExp(
|
||||
/^wrangler-output-[\d]{4}-[\d]{2}-[\d]{2}_[\d]{2}-[\d]{2}-[\d]{2}_[\d]{3}-[A-Fa-f0-9]{6}\.json$/,
|
||||
);
|
||||
const artifactFilePaths = dirent
|
||||
.filter((d) => d.name.match(regex))
|
||||
.map((d) => `${artifactDirectory}/${d.name}`);
|
||||
|
||||
return artifactFilePaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for detailed wrangler output from a pages deploy
|
||||
*
|
||||
* @param artifactDirectory
|
||||
* @returns The first pages-output-detailed found within a wrangler artifact directory
|
||||
*/
|
||||
export async function getDetailedPagesDeployOutput(
|
||||
artifactDirectory: string,
|
||||
): Promise<OutputEntryPagesDeployment | null> {
|
||||
const artifactFilePaths = await getWranglerArtifacts(artifactDirectory);
|
||||
|
||||
for (let i = 0; i < artifactFilePaths.length; i++) {
|
||||
const file = await open(artifactFilePaths[i], "r");
|
||||
|
||||
for await (const line of file.readLines()) {
|
||||
try {
|
||||
const output = JSON.parse(line);
|
||||
const parsedOutput = OutputEntryPagesDeployment.parse(output);
|
||||
if (parsedOutput.type === "pages-deploy-detailed") {
|
||||
// Assume, in the context of the action, the first detailed deploy instance seen will suffice
|
||||
return parsedOutput;
|
||||
}
|
||||
} catch (err) {
|
||||
// If the line can't be parsed, skip it
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
await file.close();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
Loading…
Reference in a new issue