Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e804ea3dfd | |||
| 8d0324a88e | |||
| 2f18b18a46 | |||
| 622ff0d765 | |||
| f501f05fbd | |||
| f990691b6b | |||
| 652762d3f6 | |||
| bd3f4f0b30 | |||
| 38dbf5b59f | |||
| cdf65921e0 | |||
| bfdfae5e0f | |||
| a3edea85f7 | |||
| b6290ea70c | |||
| 65553b39e6 | |||
| 4f154b96e0 | |||
| 5f269ee58e | |||
| d181764e4b | |||
| 3c924dd85f | |||
| c1f90e624b | |||
| 0501554080 | |||
| d8f3eaa359 | |||
| febbda69f8 | |||
| 59b83385d8 | |||
| dd8ea1b72d | |||
| 1029e90033 | |||
| 707f637509 | |||
| e764ef3355 | |||
| 8d761e6bdc | |||
| cd6314a97b | |||
| ef1f9fb2b0 | |||
| 08959b2671 | |||
| abb67eda8d | |||
| 883eaf2de0 |
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"wrangler-action": patch
|
||||
---
|
||||
|
||||
Handle failures in createGitHubDeployment and createGitHubJobSummary
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"wrangler-action": minor
|
||||
---
|
||||
|
||||
Use wrangler outputs for version upload and wrangler deploy
|
||||
@@ -9,13 +9,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
# Pinned due to compatibility issues on 23.2.0
|
||||
node-version: "22"
|
||||
node-version: "24"
|
||||
cache: "npm"
|
||||
|
||||
- name: Install modules and build
|
||||
@@ -76,7 +75,7 @@ jobs:
|
||||
SECRET2: ${{ secrets.SECRET2 }}
|
||||
|
||||
- name: Health Check Deployed Worker
|
||||
run: node .github/workflows/workerHealthCheck.cjs wrangler-action-test-secrets-v2
|
||||
run: node .github/workflows/workerHealthCheck.ts wrangler-action-test-secrets-v2
|
||||
shell: bash
|
||||
|
||||
- name: Deploy app secrets w/ default version
|
||||
@@ -93,7 +92,7 @@ jobs:
|
||||
SECRET2: ${{ secrets.SECRET2 }}
|
||||
|
||||
- name: Health Check Deployed Worker
|
||||
run: node .github/workflows/workerHealthCheck.cjs wrangler-action-test-secrets-default
|
||||
run: node .github/workflows/workerHealthCheck.ts wrangler-action-test-secrets-default
|
||||
shell: bash
|
||||
|
||||
- name: Clean Up Deployed Workers
|
||||
@@ -143,7 +142,7 @@ jobs:
|
||||
command: deploy --dry-run
|
||||
|
||||
- name: Install pnpm
|
||||
run: npm i -g pnpm
|
||||
run: npm i -g pnpm@10
|
||||
|
||||
- name: Support pnpm package manager
|
||||
uses: ./
|
||||
|
||||
@@ -19,16 +19,15 @@ jobs:
|
||||
issues: read
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
# Pinned due to compatibility issues on 23.2.0
|
||||
node-version: "22"
|
||||
node-version: "24"
|
||||
cache: "npm"
|
||||
|
||||
- name: Install modules
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
name: Semgrep OSS scan
|
||||
on:
|
||||
pull_request: {}
|
||||
workflow_dispatch: {}
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
branches: [main, master]
|
||||
workflow_dispatch: {}
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
name: Semgrep config
|
||||
- cron: "0 0 20 * *"
|
||||
concurrency:
|
||||
group: semgrep-${{ github.event_name }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
semgrep:
|
||||
name: semgrep/ci
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
|
||||
SEMGREP_URL: https://cloudflare.semgrep.dev
|
||||
SEMGREP_APP_URL: https://cloudflare.semgrep.dev
|
||||
SEMGREP_VERSION_CHECK_URL: https://cloudflare.semgrep.dev/api/check-version
|
||||
container:
|
||||
image: semgrep/semgrep
|
||||
name: semgrep-oss
|
||||
runs-on: ubuntu-slim
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: semgrep ci
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- id: cache-semgrep
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/.local
|
||||
key: semgrep-1.160.0-${{ runner.os }}
|
||||
- if: steps.cache-semgrep.outputs.cache-hit != 'true'
|
||||
run: pip install --user semgrep==1.160.0
|
||||
- run: echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||
- run: semgrep scan --config=auto
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
const { execSync } = require("child_process");
|
||||
|
||||
function workerHealthCheck(workerName) {
|
||||
const url = `https://${workerName}.devprod-testing7928.workers.dev/secret-health-check`;
|
||||
|
||||
const buffer = execSync(`curl ${url}`);
|
||||
|
||||
const response = buffer.toString();
|
||||
|
||||
if (response.includes("OK")) {
|
||||
console.log(`Status: Worker is up! Response: ${response}`);
|
||||
} else {
|
||||
throw new Error(`Worker is down! Response: ${response}`);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
const args = Array.from(process.argv);
|
||||
const workerName = args.pop();
|
||||
|
||||
if (!workerName) {
|
||||
throw new Error(
|
||||
"Please provide the worker name as an argument when calling this program.",
|
||||
);
|
||||
}
|
||||
|
||||
workerHealthCheck(workerName);
|
||||
@@ -0,0 +1,41 @@
|
||||
import { Result } from "better-result";
|
||||
|
||||
async function workerHealthCheck(workerName) {
|
||||
const url = `https://${workerName}.devprod-testing7928.workers.dev/secret-health-check`;
|
||||
|
||||
const response = await fetch(url);
|
||||
const text = await response.text();
|
||||
|
||||
if (text.includes("OK")) {
|
||||
console.log(`Status: Worker is up! Response: ${text}`);
|
||||
} else {
|
||||
throw new Error(`Worker is down! Response: ${text}`);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
const args = Array.from(process.argv);
|
||||
const workerName = args.pop();
|
||||
|
||||
if (!workerName) {
|
||||
throw new Error(
|
||||
"Please provide the worker name as an argument when calling this program.",
|
||||
);
|
||||
}
|
||||
|
||||
const result = await Result.tryPromise(() => workerHealthCheck(workerName), {
|
||||
retry: {
|
||||
times: 5,
|
||||
delayMs: 2000,
|
||||
backoff: "exponential",
|
||||
},
|
||||
});
|
||||
|
||||
result.match({
|
||||
ok: () => {},
|
||||
err: (error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
},
|
||||
});
|
||||
+28
-1
@@ -1,5 +1,33 @@
|
||||
# Changelog
|
||||
|
||||
## 4.0.0
|
||||
|
||||
### Major Changes
|
||||
|
||||
- [#412](https://github.com/cloudflare/wrangler-action/pull/412) [`1029e90`](https://github.com/cloudflare/wrangler-action/commit/1029e90033977ccf46c2a9b3ddc55e42ad5da467) Thanks [@ericclemmons](https://github.com/ericclemmons)! - Update default Wrangler version to v4 (`latest`). The action now installs Wrangler v4 by default when no `wranglerVersion` input is specified. Users can still pin to v3 by setting `wranglerVersion: "3.90.0"` explicitly.
|
||||
|
||||
## 3.15.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#426](https://github.com/cloudflare/wrangler-action/pull/426) [`febbda6`](https://github.com/cloudflare/wrangler-action/commit/febbda69f8c5838bf8b07fd6b9dfc836f00962db) Thanks [@WillTaylorDev](https://github.com/WillTaylorDev)! - Support version ranges and tags in `wranglerVersion` input. You can now set `wranglerVersion` to values like `4`, `^4.0.0`, `4.x`, or `latest` instead of only exact versions like `4.81.0`.
|
||||
|
||||
## 3.14.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#358](https://github.com/cloudflare/wrangler-action/pull/358) [`cd6314a`](https://github.com/cloudflare/wrangler-action/commit/cd6314a97b09d9a764e30cacd0870edc86f92986) Thanks [@penalosa](https://github.com/penalosa)! - Use `secret bulk` instead of deprecated `secret:bulk` command
|
||||
|
||||
## 3.14.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#351](https://github.com/cloudflare/wrangler-action/pull/351) [`4ff07f4`](https://github.com/cloudflare/wrangler-action/commit/4ff07f4310dc5067d84a254cd9af3d2e91df119e) Thanks [@Maximo-Guk](https://github.com/Maximo-Guk)! - Use wrangler outputs for version upload and wrangler deploy
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#350](https://github.com/cloudflare/wrangler-action/pull/350) [`e209094`](https://github.com/cloudflare/wrangler-action/commit/e209094e624c6f6b418141b7e9d0ab7838d794a3) Thanks [@Maximo-Guk](https://github.com/Maximo-Guk)! - Handle failures in createGitHubDeployment and createGitHubJobSummary
|
||||
|
||||
## 3.13.1
|
||||
|
||||
### Patch Changes
|
||||
@@ -97,7 +125,6 @@
|
||||
### Minor Changes
|
||||
|
||||
- [#213](https://github.com/cloudflare/wrangler-action/pull/213) [`d13856dfc92816473ebf47f66e263a2668a97896`](https://github.com/cloudflare/wrangler-action/commit/d13856dfc92816473ebf47f66e263a2668a97896) Thanks [@GrantBirki](https://github.com/GrantBirki)! - This change introduces three new GitHub Actions output variables. These variables are as follows:
|
||||
|
||||
- `command-output` - contains the string results of `stdout`
|
||||
- `command-stderr` - contains the string results of `stderr`
|
||||
- `deployment-url` - contains the string results of the URL that was deployed (ex: `https://<your_pages_site>.pages.dev`)
|
||||
|
||||
@@ -2,6 +2,17 @@
|
||||
|
||||
Easy-to-use GitHub Action to use [Wrangler](https://developers.cloudflare.com/workers/cli-wrangler/). Makes deploying Workers a breeze.
|
||||
|
||||
## Wrangler v3 Support
|
||||
|
||||
The action now defaults to **Wrangler v4**. If you need to stay on Wrangler v3, you can pin the version explicitly:
|
||||
|
||||
```yaml
|
||||
- uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
wranglerVersion: "3.90.0"
|
||||
```
|
||||
|
||||
## Big Changes in v3
|
||||
|
||||
- Wrangler v1 is no longer supported.
|
||||
@@ -27,7 +38,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Deploy
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
@@ -52,7 +63,7 @@ jobs:
|
||||
|
||||
## Configuration
|
||||
|
||||
If you need to install a specific version of Wrangler to use for deployment, you can also pass the input `wranglerVersion` to install a specific version of Wrangler from NPM. This should be a [SemVer](https://semver.org/)-style version number, such as `2.20.0`:
|
||||
You can pass `wranglerVersion` to install a specific version of Wrangler from NPM. This accepts any version format NPM understands: an exact version like `4.81.0`, a major version like `4`, a range like `^4.0.0` or `4.x`, or `latest`.
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
@@ -61,9 +72,11 @@ jobs:
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
wranglerVersion: "2.20.0"
|
||||
wranglerVersion: "4"
|
||||
```
|
||||
|
||||
If you omit `wranglerVersion` and Wrangler is already installed in your environment, the action uses the existing installation. If Wrangler is not installed, the action installs a default version.
|
||||
|
||||
Optionally, you can also pass a `workingDirectory` key to the action. This will allow you to specify a subdirectory of the repo to run the Wrangler command from.
|
||||
|
||||
```yaml
|
||||
@@ -153,7 +166,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Deploy
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
@@ -177,7 +190,7 @@ jobs:
|
||||
contents: read
|
||||
deployments: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Deploy
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
@@ -202,7 +215,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Deploy app
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
@@ -228,7 +241,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Deploy app
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
@@ -248,7 +261,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Upload Worker Version
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
@@ -374,7 +387,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Deploy app
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ branding:
|
||||
description: "Deploy your Cloudflare projects from GitHub using Wrangler"
|
||||
runs:
|
||||
# Possible values: https://github.com/actions/runner/blob/main/src/Runner.Common/Util/NodeUtil.cs#L9
|
||||
using: "node20"
|
||||
using: "node24"
|
||||
main: "dist/index.mjs"
|
||||
inputs:
|
||||
apiToken:
|
||||
|
||||
Generated
+1490
-921
File diff suppressed because it is too large
Load Diff
+6
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wrangler-action",
|
||||
"version": "3.13.1",
|
||||
"version": "4.0.0",
|
||||
"description": "GitHub Action to use [Wrangler](https://developers.cloudflare.com/workers/cli-wrangler/).",
|
||||
"author": "wrangler@cloudflare.com",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
@@ -42,11 +42,15 @@
|
||||
"@types/node": "^22.9.0",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@vercel/ncc": "^0.38.2",
|
||||
"better-result": "^2.9.2",
|
||||
"mock-fs": "^5.4.1",
|
||||
"msw": "^2.6.4",
|
||||
"prettier": "^3.3.3",
|
||||
"semver": "^7.6.3",
|
||||
"typescript": "^5.6.3",
|
||||
"vitest": "^2.1.9"
|
||||
"vitest": "^3.2.4"
|
||||
},
|
||||
"overrides": {
|
||||
"undici": "^6.23.1"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import { getPackageManager } from "./packageManagers";
|
||||
import { checkWorkingDirectory } from "./utils";
|
||||
import { main, WranglerActionConfig } from "./wranglerAction";
|
||||
|
||||
const DEFAULT_WRANGLER_VERSION = "3.90.0";
|
||||
const DEFAULT_WRANGLER_VERSION = "4";
|
||||
|
||||
/**
|
||||
* A configuration object that contains all the inputs & immutable state for the action.
|
||||
|
||||
@@ -7,7 +7,7 @@ export function getTestConfig({
|
||||
} = {}): WranglerActionConfig {
|
||||
return Object.assign(
|
||||
{
|
||||
WRANGLER_VERSION: "3.81.0",
|
||||
WRANGLER_VERSION: "4.72.0",
|
||||
didUserProvideWranglerVersion: false,
|
||||
secrets: [],
|
||||
workingDirectory: "/src/test/fixtures",
|
||||
|
||||
@@ -42,6 +42,10 @@ describe("semverCompare", () => {
|
||||
["3.1.0", "3.15.0", true],
|
||||
["3.10.0", "3.1.0", false],
|
||||
["3.20.0", "3.2.0", false],
|
||||
["3.1.0", "4.0.0", true],
|
||||
["2.20.0", "4.72.0", true],
|
||||
["3.4.0", "4.72.0", true],
|
||||
["3.60.0", "4.72.0", true],
|
||||
["3.1.0", "latest", true],
|
||||
["4.0.0", "latest", true],
|
||||
])(
|
||||
|
||||
+439
-5
@@ -1,9 +1,59 @@
|
||||
import * as core from "@actions/core";
|
||||
import * as exec from "@actions/exec";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { installWrangler } from "./wranglerAction";
|
||||
import {
|
||||
installWrangler,
|
||||
isExactSemver,
|
||||
main,
|
||||
parseWranglerVersion,
|
||||
uploadSecrets,
|
||||
} from "./wranglerAction";
|
||||
import { getTestConfig } from "./test/test-utils";
|
||||
|
||||
describe("parseWranglerVersion", () => {
|
||||
it("parses version from verbose wrangler output", () => {
|
||||
expect(
|
||||
parseWranglerVersion(` ⛅️ wrangler 3.48.0 (update available 3.53.1)`),
|
||||
).toBe("3.48.0");
|
||||
});
|
||||
|
||||
it("parses version from bare version output", () => {
|
||||
expect(parseWranglerVersion("4.18.1\n")).toBe("4.18.1");
|
||||
});
|
||||
|
||||
it("returns empty string for unparseable output", () => {
|
||||
expect(parseWranglerVersion("something unexpected")).toBe("");
|
||||
});
|
||||
|
||||
it("parses prerelease version", () => {
|
||||
expect(parseWranglerVersion(` ⛅️ wrangler 4.0.0-beta.1`)).toBe(
|
||||
"4.0.0-beta.1",
|
||||
);
|
||||
});
|
||||
|
||||
it("parses bare prerelease version", () => {
|
||||
expect(parseWranglerVersion("4.0.0-rc.0\n")).toBe("4.0.0-rc.0");
|
||||
});
|
||||
});
|
||||
|
||||
describe("isExactSemver", () => {
|
||||
it.each([
|
||||
["4.81.0", true],
|
||||
["3.48.0", true],
|
||||
["2.20.0", true],
|
||||
["4.0.0-beta.1", true],
|
||||
["4", false],
|
||||
["4.x", false],
|
||||
["4.*", false],
|
||||
["^4.0.0", false],
|
||||
["~4.0.0", false],
|
||||
["latest", false],
|
||||
["", false],
|
||||
])("isExactSemver(%s) === %s", (input, expected) => {
|
||||
expect(isExactSemver(input)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe("installWrangler", () => {
|
||||
const testPackageManager = {
|
||||
install: "npm i",
|
||||
@@ -30,10 +80,14 @@ describe("installWrangler", () => {
|
||||
};
|
||||
});
|
||||
const infoSpy = vi.spyOn(core, "info");
|
||||
await installWrangler(testConfig, testPackageManager);
|
||||
const resolvedVersion = await installWrangler(
|
||||
testConfig,
|
||||
testPackageManager,
|
||||
);
|
||||
expect(infoSpy).toBeCalledWith(
|
||||
"✅ No wrangler version specified, using pre-installed wrangler version 3.48.0",
|
||||
);
|
||||
expect(resolvedVersion).toBe("3.48.0");
|
||||
});
|
||||
|
||||
it("Does nothing if the wrangler version specified is the same as the one installed", async () => {
|
||||
@@ -51,9 +105,14 @@ describe("installWrangler", () => {
|
||||
};
|
||||
});
|
||||
const infoSpy = vi.spyOn(core, "info");
|
||||
await installWrangler(testConfig, testPackageManager);
|
||||
const resolvedVersion = await installWrangler(
|
||||
testConfig,
|
||||
testPackageManager,
|
||||
);
|
||||
expect(infoSpy).toBeCalledWith("✅ Using Wrangler 3.48.0");
|
||||
expect(resolvedVersion).toBe("3.48.0");
|
||||
});
|
||||
|
||||
it("Should install wrangler if the version specified is not already available", async () => {
|
||||
const testConfig = getTestConfig({
|
||||
config: {
|
||||
@@ -61,18 +120,393 @@ describe("installWrangler", () => {
|
||||
didUserProvideWranglerVersion: true,
|
||||
},
|
||||
});
|
||||
let callCount = 0;
|
||||
vi.spyOn(exec, "getExecOutput").mockImplementation(async () => {
|
||||
callCount++;
|
||||
if (callCount === 1) {
|
||||
// Pre-install check: different version installed
|
||||
return {
|
||||
exitCode: 0,
|
||||
stderr: "",
|
||||
stdout: ` ⛅️ wrangler 3.20.0 (update available 3.53.1)`,
|
||||
};
|
||||
}
|
||||
// Post-install check: correct version now installed
|
||||
return {
|
||||
exitCode: 0,
|
||||
stderr: "",
|
||||
stdout: ` ⛅️ wrangler 3.20.0 (update available 3.53.1)`,
|
||||
stdout: ` ⛅️ wrangler 3.48.0`,
|
||||
};
|
||||
});
|
||||
vi.spyOn(exec, "exec").mockImplementation(async () => {
|
||||
return 0;
|
||||
});
|
||||
const infoSpy = vi.spyOn(core, "info");
|
||||
await installWrangler(testConfig, testPackageManager);
|
||||
const resolvedVersion = await installWrangler(
|
||||
testConfig,
|
||||
testPackageManager,
|
||||
);
|
||||
expect(infoSpy).toBeCalledWith("✅ Wrangler installed");
|
||||
expect(resolvedVersion).toBe("3.48.0");
|
||||
});
|
||||
|
||||
it("Should install and resolve version when a range like '4' is specified", async () => {
|
||||
const testConfig = getTestConfig({
|
||||
config: {
|
||||
WRANGLER_VERSION: "4",
|
||||
didUserProvideWranglerVersion: true,
|
||||
},
|
||||
});
|
||||
let callCount = 0;
|
||||
vi.spyOn(exec, "getExecOutput").mockImplementation(async () => {
|
||||
callCount++;
|
||||
if (callCount === 1) {
|
||||
// Pre-install check: older version installed
|
||||
return {
|
||||
exitCode: 0,
|
||||
stderr: "",
|
||||
stdout: ` ⛅️ wrangler 3.90.0`,
|
||||
};
|
||||
}
|
||||
// Post-install check: v4 now installed
|
||||
return {
|
||||
exitCode: 0,
|
||||
stderr: "",
|
||||
stdout: `4.18.1`,
|
||||
};
|
||||
});
|
||||
vi.spyOn(exec, "exec").mockImplementation(async (cmd, args) => {
|
||||
if (cmd === "npm i") expect(args).toStrictEqual(["wrangler@4"]);
|
||||
return 0;
|
||||
});
|
||||
const resolvedVersion = await installWrangler(
|
||||
testConfig,
|
||||
testPackageManager,
|
||||
);
|
||||
expect(resolvedVersion).toBe("4.18.1");
|
||||
});
|
||||
|
||||
it("Should install and resolve version when 'latest' is specified", async () => {
|
||||
const testConfig = getTestConfig({
|
||||
config: {
|
||||
WRANGLER_VERSION: "latest",
|
||||
didUserProvideWranglerVersion: true,
|
||||
},
|
||||
});
|
||||
let callCount = 0;
|
||||
vi.spyOn(exec, "getExecOutput").mockImplementation(async () => {
|
||||
callCount++;
|
||||
if (callCount === 1) {
|
||||
// Pre-install: no wrangler found
|
||||
throw new Error("command not found");
|
||||
}
|
||||
// Post-install check
|
||||
return {
|
||||
exitCode: 0,
|
||||
stderr: "",
|
||||
stdout: `4.20.0`,
|
||||
};
|
||||
});
|
||||
vi.spyOn(exec, "exec").mockImplementation(async (cmd, args) => {
|
||||
if (cmd === "npm i") {
|
||||
expect(args).toStrictEqual(["wrangler@latest"]);
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
const resolvedVersion = await installWrangler(
|
||||
testConfig,
|
||||
testPackageManager,
|
||||
);
|
||||
expect(resolvedVersion).toBe("4.20.0");
|
||||
});
|
||||
|
||||
it("Throws if version cannot be resolved after install", async () => {
|
||||
const testConfig = getTestConfig({
|
||||
config: {
|
||||
WRANGLER_VERSION: "4",
|
||||
didUserProvideWranglerVersion: true,
|
||||
},
|
||||
});
|
||||
let callCount = 0;
|
||||
vi.spyOn(exec, "getExecOutput").mockImplementation(async () => {
|
||||
callCount++;
|
||||
if (callCount === 1) {
|
||||
throw new Error("not found");
|
||||
}
|
||||
// Post-install: unparseable output
|
||||
return { exitCode: 0, stderr: "", stdout: "garbage output" };
|
||||
});
|
||||
vi.spyOn(exec, "exec").mockResolvedValue(0);
|
||||
await expect(
|
||||
installWrangler(testConfig, testPackageManager),
|
||||
).rejects.toThrowError(
|
||||
"Failed to determine installed Wrangler version after installing wrangler@4",
|
||||
);
|
||||
});
|
||||
|
||||
it("Falls back to raw version when post-install resolution fails for exact semver", async () => {
|
||||
const testConfig = getTestConfig({
|
||||
config: {
|
||||
WRANGLER_VERSION: "3.48.0",
|
||||
didUserProvideWranglerVersion: true,
|
||||
},
|
||||
});
|
||||
let callCount = 0;
|
||||
vi.spyOn(exec, "getExecOutput").mockImplementation(async () => {
|
||||
callCount++;
|
||||
if (callCount === 1) {
|
||||
throw new Error("not found");
|
||||
}
|
||||
throw new Error("wrangler --version failed");
|
||||
});
|
||||
vi.spyOn(exec, "exec").mockResolvedValue(0);
|
||||
const resolvedVersion = await installWrangler(
|
||||
testConfig,
|
||||
testPackageManager,
|
||||
);
|
||||
expect(resolvedVersion).toBe("3.48.0");
|
||||
});
|
||||
|
||||
it("Skips reinstall when range is satisfied by pre-installed version", async () => {
|
||||
const testConfig = getTestConfig({
|
||||
config: {
|
||||
WRANGLER_VERSION: "4",
|
||||
didUserProvideWranglerVersion: true,
|
||||
},
|
||||
});
|
||||
vi.spyOn(exec, "getExecOutput").mockImplementation(async () => {
|
||||
return {
|
||||
exitCode: 0,
|
||||
stderr: "",
|
||||
stdout: `4.18.1`,
|
||||
};
|
||||
});
|
||||
const execSpy = vi.spyOn(exec, "exec");
|
||||
const infoSpy = vi.spyOn(core, "info");
|
||||
const resolvedVersion = await installWrangler(
|
||||
testConfig,
|
||||
testPackageManager,
|
||||
);
|
||||
expect(infoSpy).toBeCalledWith("✅ Using Wrangler 4.18.1");
|
||||
expect(resolvedVersion).toBe("4.18.1");
|
||||
expect(execSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("Reinstalls when range is NOT satisfied by pre-installed version", async () => {
|
||||
const testConfig = getTestConfig({
|
||||
config: {
|
||||
WRANGLER_VERSION: "4",
|
||||
didUserProvideWranglerVersion: true,
|
||||
},
|
||||
});
|
||||
let callCount = 0;
|
||||
vi.spyOn(exec, "getExecOutput").mockImplementation(async () => {
|
||||
callCount++;
|
||||
if (callCount === 1) {
|
||||
// Pre-installed is v3, doesn't satisfy "4"
|
||||
return {
|
||||
exitCode: 0,
|
||||
stderr: "",
|
||||
stdout: ` ⛅️ wrangler 3.90.0`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
exitCode: 0,
|
||||
stderr: "",
|
||||
stdout: `4.18.1`,
|
||||
};
|
||||
});
|
||||
vi.spyOn(exec, "exec").mockResolvedValue(0);
|
||||
const resolvedVersion = await installWrangler(
|
||||
testConfig,
|
||||
testPackageManager,
|
||||
);
|
||||
expect(resolvedVersion).toBe("4.18.1");
|
||||
});
|
||||
});
|
||||
|
||||
describe("uploadSecrets", () => {
|
||||
const testPackageManager = {
|
||||
install: "npm i",
|
||||
exec: "npx",
|
||||
execNoInstall: "npx --no-install",
|
||||
};
|
||||
|
||||
it("WRANGLER_VERSION < 3.4.0 uses wrangler secret put", async () => {
|
||||
vi.stubEnv("FAKE_SECRET", "FAKE_VALUE");
|
||||
const testConfig = getTestConfig({
|
||||
config: {
|
||||
WRANGLER_VERSION: "3.3.0",
|
||||
didUserProvideWranglerVersion: true,
|
||||
secrets: ["FAKE_SECRET"],
|
||||
},
|
||||
});
|
||||
vi.spyOn(exec, "exec").mockImplementation(async (cmd, args) => {
|
||||
expect(cmd).toBe("npx");
|
||||
expect(args).toStrictEqual([
|
||||
"wrangler",
|
||||
"secret",
|
||||
"put",
|
||||
"FAKE_SECRET",
|
||||
"--env",
|
||||
"dev",
|
||||
]);
|
||||
return 0;
|
||||
});
|
||||
const startGroup = vi.spyOn(core, "startGroup");
|
||||
const endGroup = vi.spyOn(core, "endGroup");
|
||||
|
||||
await uploadSecrets(testConfig, testPackageManager);
|
||||
expect(startGroup).toBeCalledWith("🔑 Uploading secrets...");
|
||||
expect(endGroup).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("WRANGLER_VERSION < 3.60.0 uses wrangler secret:bulk", async () => {
|
||||
vi.stubEnv("FAKE_SECRET", "FAKE_VALUE");
|
||||
const testConfig = getTestConfig({
|
||||
config: {
|
||||
WRANGLER_VERSION: "3.59.0",
|
||||
didUserProvideWranglerVersion: true,
|
||||
secrets: ["FAKE_SECRET"],
|
||||
},
|
||||
});
|
||||
vi.spyOn(exec, "exec").mockImplementation(async (cmd, args) => {
|
||||
expect(cmd).toBe("npx");
|
||||
expect(args).toStrictEqual(["wrangler", "secret:bulk", "--env", "dev"]);
|
||||
return 0;
|
||||
});
|
||||
const startGroup = vi.spyOn(core, "startGroup");
|
||||
const endGroup = vi.spyOn(core, "endGroup");
|
||||
|
||||
await uploadSecrets(testConfig, testPackageManager);
|
||||
expect(startGroup).toBeCalledWith("🔑 Uploading secrets...");
|
||||
expect(endGroup).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("WRANGLER_VERSION 3.61.0 uses wrangler secret bulk", async () => {
|
||||
vi.stubEnv("FAKE_SECRET", "FAKE_VALUE");
|
||||
const testConfig = getTestConfig({
|
||||
config: {
|
||||
WRANGLER_VERSION: "3.61.0",
|
||||
didUserProvideWranglerVersion: true,
|
||||
secrets: ["FAKE_SECRET"],
|
||||
},
|
||||
});
|
||||
vi.spyOn(exec, "exec").mockImplementation(async (cmd, args) => {
|
||||
expect(cmd).toBe("npx");
|
||||
expect(args).toStrictEqual([
|
||||
"wrangler",
|
||||
"secret",
|
||||
"bulk",
|
||||
"--env",
|
||||
"dev",
|
||||
]);
|
||||
return 0;
|
||||
});
|
||||
const startGroup = vi.spyOn(core, "startGroup");
|
||||
const endGroup = vi.spyOn(core, "endGroup");
|
||||
|
||||
await uploadSecrets(testConfig, testPackageManager);
|
||||
expect(startGroup).toBeCalledWith("🔑 Uploading secrets...");
|
||||
expect(endGroup).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("WRANGLER_VERSION 4.x uses wrangler secret bulk", async () => {
|
||||
vi.stubEnv("FAKE_SECRET", "FAKE_VALUE");
|
||||
const testConfig = getTestConfig({
|
||||
config: {
|
||||
WRANGLER_VERSION: "4.72.0",
|
||||
didUserProvideWranglerVersion: true,
|
||||
secrets: ["FAKE_SECRET"],
|
||||
},
|
||||
});
|
||||
vi.spyOn(exec, "exec").mockImplementation(async (cmd, args) => {
|
||||
expect(cmd).toBe("npx");
|
||||
expect(args).toStrictEqual([
|
||||
"wrangler",
|
||||
"secret",
|
||||
"bulk",
|
||||
"--env",
|
||||
"dev",
|
||||
]);
|
||||
return 0;
|
||||
});
|
||||
const startGroup = vi.spyOn(core, "startGroup");
|
||||
const endGroup = vi.spyOn(core, "endGroup");
|
||||
|
||||
await uploadSecrets(testConfig, testPackageManager);
|
||||
expect(startGroup).toBeCalledWith("🔑 Uploading secrets...");
|
||||
expect(endGroup).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
|
||||
describe("main", () => {
|
||||
const testPackageManager = {
|
||||
install: "npm i",
|
||||
exec: "npx",
|
||||
execNoInstall: "npx --no-install",
|
||||
};
|
||||
|
||||
it("Completes with wranglerVersion '4' and secrets without Invalid Version error", async () => {
|
||||
vi.stubEnv("MY_SECRET", "secret_value");
|
||||
|
||||
const testConfig = getTestConfig({
|
||||
config: {
|
||||
WRANGLER_VERSION: "4",
|
||||
didUserProvideWranglerVersion: true,
|
||||
secrets: ["MY_SECRET"],
|
||||
COMMANDS: ["deploy"],
|
||||
},
|
||||
});
|
||||
|
||||
vi.spyOn(core, "getMultilineInput").mockReturnValue([]);
|
||||
|
||||
let callCount = 0;
|
||||
vi.spyOn(exec, "getExecOutput").mockImplementation(async () => {
|
||||
callCount++;
|
||||
if (callCount === 1) {
|
||||
throw new Error("command not found");
|
||||
}
|
||||
return { exitCode: 0, stderr: "", stdout: "4.18.1" };
|
||||
});
|
||||
|
||||
vi.spyOn(exec, "exec").mockResolvedValue(0);
|
||||
const setFailedSpy = vi.spyOn(core, "setFailed");
|
||||
|
||||
await main(testConfig, testPackageManager);
|
||||
|
||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("Completes with wranglerVersion 'latest' and secrets without Invalid Version error", async () => {
|
||||
vi.stubEnv("MY_SECRET", "secret_value");
|
||||
|
||||
const testConfig = getTestConfig({
|
||||
config: {
|
||||
WRANGLER_VERSION: "latest",
|
||||
didUserProvideWranglerVersion: true,
|
||||
secrets: ["MY_SECRET"],
|
||||
COMMANDS: ["deploy"],
|
||||
},
|
||||
});
|
||||
|
||||
vi.spyOn(core, "getMultilineInput").mockReturnValue([]);
|
||||
|
||||
let callCount = 0;
|
||||
vi.spyOn(exec, "getExecOutput").mockImplementation(async () => {
|
||||
callCount++;
|
||||
if (callCount === 1) {
|
||||
throw new Error("command not found");
|
||||
}
|
||||
return { exitCode: 0, stderr: "", stdout: "4.20.0" };
|
||||
});
|
||||
|
||||
vi.spyOn(exec, "exec").mockResolvedValue(0);
|
||||
|
||||
const setFailedSpy = vi.spyOn(core, "setFailed");
|
||||
|
||||
await main(testConfig, testPackageManager);
|
||||
|
||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
+86
-40
@@ -7,12 +7,14 @@ import {
|
||||
setOutput,
|
||||
} from "@actions/core";
|
||||
import { getExecOutput } from "@actions/exec";
|
||||
import semverEq from "semver/functions/eq";
|
||||
import semverSatisfies from "semver/functions/satisfies";
|
||||
import semverValid from "semver/functions/valid";
|
||||
import { z } from "zod";
|
||||
import { exec, execShell } from "./exec";
|
||||
import { PackageManager } from "./packageManagers";
|
||||
import { error, info, semverCompare } from "./utils";
|
||||
import { handleCommandOutputParsing } from "./commandOutputParsing";
|
||||
import semverLt from "semver/functions/lt";
|
||||
|
||||
export type WranglerActionConfig = z.infer<typeof wranglerActionConfig>;
|
||||
export const wranglerActionConfig = z.object({
|
||||
@@ -50,32 +52,60 @@ async function main(
|
||||
try {
|
||||
wranglerActionConfig.parse(config);
|
||||
authenticationSetup(config);
|
||||
await installWrangler(config, packageManager);
|
||||
const resolvedVersion = await installWrangler(config, packageManager);
|
||||
const resolvedConfig = { ...config, WRANGLER_VERSION: resolvedVersion };
|
||||
|
||||
await execCommands(
|
||||
config,
|
||||
resolvedConfig,
|
||||
packageManager,
|
||||
getMultilineInput("preCommands"),
|
||||
"pre",
|
||||
);
|
||||
await uploadSecrets(config, packageManager);
|
||||
await wranglerCommands(config, packageManager);
|
||||
await uploadSecrets(resolvedConfig, packageManager);
|
||||
await wranglerCommands(resolvedConfig, packageManager);
|
||||
await execCommands(
|
||||
config,
|
||||
resolvedConfig,
|
||||
packageManager,
|
||||
getMultilineInput("postCommands"),
|
||||
"post",
|
||||
);
|
||||
info(config, "🏁 Wrangler Action completed", true);
|
||||
info(resolvedConfig, "🏁 Wrangler Action completed", true);
|
||||
} catch (err: unknown) {
|
||||
err instanceof Error && error(config, err.message);
|
||||
setFailed("🚨 Action failed");
|
||||
}
|
||||
}
|
||||
|
||||
function parseWranglerVersion(stdout: string): string {
|
||||
const match =
|
||||
stdout.match(/wrangler (\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?)/) ??
|
||||
stdout.match(/^(\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?)/m);
|
||||
return match ? match[1] : "";
|
||||
}
|
||||
|
||||
function isExactSemver(version: string): boolean {
|
||||
return semverValid(version) !== null;
|
||||
}
|
||||
|
||||
async function resolveInstalledVersion(
|
||||
config: WranglerActionConfig,
|
||||
packageManager: PackageManager,
|
||||
): Promise<string> {
|
||||
const { stdout } = await getExecOutput(
|
||||
packageManager.execNoInstall,
|
||||
["wrangler", "--version"],
|
||||
{
|
||||
cwd: config["workingDirectory"],
|
||||
silent: config.QUIET_MODE,
|
||||
},
|
||||
);
|
||||
return parseWranglerVersion(stdout);
|
||||
}
|
||||
|
||||
async function installWrangler(
|
||||
config: WranglerActionConfig,
|
||||
packageManager: PackageManager,
|
||||
) {
|
||||
): Promise<string> {
|
||||
if (config["WRANGLER_VERSION"].startsWith("1")) {
|
||||
throw new Error(
|
||||
`Wrangler v1 is no longer supported by this action. Please use major version 2 or greater`,
|
||||
@@ -84,33 +114,25 @@ async function installWrangler(
|
||||
|
||||
startGroup(config, "🔍 Checking for existing Wrangler installation");
|
||||
let installedVersion = "";
|
||||
let installedVersionSatisfiesRequirement = false;
|
||||
let versionSatisfied = false;
|
||||
try {
|
||||
const { stdout } = await getExecOutput(
|
||||
// We want to simply invoke wrangler to check if it's installed, but don't want to auto-install it at this stage
|
||||
packageManager.execNoInstall,
|
||||
["wrangler", "--version"],
|
||||
{
|
||||
cwd: config["workingDirectory"],
|
||||
silent: config.QUIET_MODE,
|
||||
},
|
||||
);
|
||||
installedVersion = await resolveInstalledVersion(config, packageManager);
|
||||
|
||||
// There are two possible outputs from `wrangler --version`:
|
||||
// ` ⛅️ wrangler 3.48.0 (update available 3.53.1)`
|
||||
// and
|
||||
// `3.48.0`
|
||||
const versionMatch =
|
||||
stdout.match(/wrangler (\d+\.\d+\.\d+)/) ??
|
||||
stdout.match(/^(\d+\.\d+\.\d+)/m);
|
||||
if (versionMatch) {
|
||||
installedVersion = versionMatch[1];
|
||||
}
|
||||
if (config.didUserProvideWranglerVersion) {
|
||||
installedVersionSatisfiesRequirement = semverEq(
|
||||
installedVersion,
|
||||
config["WRANGLER_VERSION"],
|
||||
);
|
||||
if (config.didUserProvideWranglerVersion && installedVersion) {
|
||||
if (isExactSemver(config["WRANGLER_VERSION"])) {
|
||||
versionSatisfied = installedVersion === config["WRANGLER_VERSION"];
|
||||
} else {
|
||||
// semverSatisfies handles ranges like "4", "^4.0.0", "4.x".
|
||||
// Returns false for dist-tags like "latest", falling through to reinstall.
|
||||
try {
|
||||
versionSatisfied = semverSatisfies(
|
||||
installedVersion,
|
||||
config["WRANGLER_VERSION"],
|
||||
);
|
||||
} catch {
|
||||
versionSatisfied = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!config.didUserProvideWranglerVersion && installedVersion) {
|
||||
info(
|
||||
@@ -119,15 +141,12 @@ async function installWrangler(
|
||||
true,
|
||||
);
|
||||
endGroup(config);
|
||||
return;
|
||||
return installedVersion;
|
||||
}
|
||||
if (
|
||||
config.didUserProvideWranglerVersion &&
|
||||
installedVersionSatisfiesRequirement
|
||||
) {
|
||||
if (config.didUserProvideWranglerVersion && versionSatisfied) {
|
||||
info(config, `✅ Using Wrangler ${installedVersion}`, true);
|
||||
endGroup(config);
|
||||
return;
|
||||
return installedVersion;
|
||||
}
|
||||
info(
|
||||
config,
|
||||
@@ -160,6 +179,27 @@ async function installWrangler(
|
||||
} finally {
|
||||
endGroup(config);
|
||||
}
|
||||
|
||||
let resolvedVersion = "";
|
||||
try {
|
||||
resolvedVersion = await resolveInstalledVersion(config, packageManager);
|
||||
} catch (err) {
|
||||
debug(`Error resolving installed Wrangler version: ${err}`);
|
||||
}
|
||||
|
||||
if (resolvedVersion) {
|
||||
return resolvedVersion;
|
||||
}
|
||||
|
||||
// Fall back to the raw version string if it's already valid semver.
|
||||
// This preserves pre-existing behavior for exact version inputs.
|
||||
if (isExactSemver(config["WRANGLER_VERSION"])) {
|
||||
return config["WRANGLER_VERSION"];
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Failed to determine installed Wrangler version after installing wrangler@${config["WRANGLER_VERSION"]}`,
|
||||
);
|
||||
}
|
||||
|
||||
function authenticationSetup(config: WranglerActionConfig) {
|
||||
@@ -265,7 +305,11 @@ async function uploadSecrets(
|
||||
);
|
||||
}
|
||||
|
||||
const args = ["wrangler", "secret:bulk"];
|
||||
let args = ["wrangler", "secret", "bulk"];
|
||||
// if we're on a WRANGLER_VERSION prior to 3.60.0 use wrangler secret:bulk
|
||||
if (semverLt(config["WRANGLER_VERSION"], "3.60.0")) {
|
||||
args = ["wrangler", "secret:bulk"];
|
||||
}
|
||||
|
||||
if (environment) {
|
||||
args.push("--env", environment);
|
||||
@@ -368,7 +412,9 @@ export {
|
||||
execCommands,
|
||||
info,
|
||||
installWrangler,
|
||||
isExactSemver,
|
||||
main,
|
||||
parseWranglerVersion,
|
||||
uploadSecrets,
|
||||
wranglerCommands,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user