Compare commits
144 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a61fbea322 | |||
| 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 | |||
| 4ff07f4310 | |||
| 4fb15f8b7a | |||
| e8cd5f0968 | |||
| e209094e62 | |||
| 181b9fb5d6 | |||
| aac328d155 | |||
| fc89533f1e | |||
| e819570b2d | |||
| 923db6f87f | |||
| d5031c3dbe | |||
| 17da943335 | |||
| 3aee84cd6d | |||
| 9d9bee8bfd | |||
| ee57f258dc | |||
| 9fed19aa4e | |||
| 3c6e4da7b3 | |||
| 1c0eef186f | |||
| b19342b08c | |||
| d44c80928a | |||
| cada7a6312 | |||
| 1e92eee7d2 | |||
| e7e5400334 | |||
| d58f116d72 | |||
| 64b6339110 | |||
| ac2fd0ec8f | |||
| 0140ffbc93 | |||
| cc4ede3f06 | |||
| 9bf87caf2b | |||
| 81f8814855 | |||
| 6c6d3321ef | |||
| bc61e66e47 | |||
| 0e2b86ba21 | |||
| 59c0462940 | |||
| 4c3f0c27ea | |||
| 4d045615a9 | |||
| a7a2388d7b | |||
| 95555f6ed0 | |||
| 2171bbdd69 | |||
| 4f4ff59d17 | |||
| 122ee5cf5b | |||
| 44d79edf44 | |||
| b2be7abbfc | |||
| b7158dc9a4 | |||
| 43767e73de | |||
| 10d5b9c1c1 | |||
| c01035706f | |||
| fa96c39bd4 | |||
| 78dad1df07 | |||
| 3ec7f8943e | |||
| cd8a317efb | |||
| 6aa2e8946a | |||
| de0526e09b | |||
| 84316ca95c | |||
| 134b5c2a32 | |||
| 47d51f25c1 | |||
| 93fff4f656 | |||
| 66334dff0a | |||
| 9966912c1b | |||
| 7b9aec5185 | |||
| c0e117f512 | |||
| bcff5386ec | |||
| c15498da36 | |||
| 8bc5c5f050 | |||
| ac21b71592 | |||
| f111269956 | |||
| 570d521df6 | |||
| 174337b3b4 | |||
| a1467a0c8f | |||
| 3ea6e3f132 | |||
| ea11499226 | |||
| 8f2f89521c | |||
| 66efca2cbb | |||
| aa5d18dd1e | |||
| b84268f0e0 | |||
| d1b97ebf4d | |||
| baf8bc61f1 | |||
| 8edd152f63 | |||
| 2d275a8f2d | |||
| 8ef515f899 | |||
| 9b27cf271c | |||
| 405a82610b | |||
| 0545ad285a | |||
| ea5754c9ad | |||
| 983b6349c2 | |||
| b05934f581 | |||
| 31a6263ef3 | |||
| 88906781f0 | |||
| ebef541795 | |||
| 9d4ca0f255 | |||
| 1be73b90f6 | |||
| 180648017b | |||
| 9003b06015 | |||
| fd98a7c990 | |||
| 4bf3047bb8 | |||
| f2c22accd3 | |||
| 0c36639f72 | |||
| d5a1abb013 | |||
| c214b69bae | |||
| bac2829d42 | |||
| 78c994fb95 | |||
| e3434a7e98 | |||
| 08514fdece | |||
| 7451182d38 | |||
| 522c117b7d | |||
| b92719544e | |||
| bbedd8e54f | |||
| ebaf896045 | |||
| 7e684fbad9 | |||
| 2521ec00cc | |||
| ba0f485178 |
@@ -1,7 +1,7 @@
|
||||
# Changesets
|
||||
|
||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
||||
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
||||
with multi-package repos, or single-package repos to help you version and deploy your code. You can
|
||||
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
||||
|
||||
We have a quick list of common questions to get you started engaging with this project in
|
||||
|
||||
@@ -9,12 +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:
|
||||
node-version: "latest"
|
||||
node-version: "24"
|
||||
cache: "npm"
|
||||
|
||||
- name: Install modules and build
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
- name: Only build app
|
||||
uses: ./
|
||||
with:
|
||||
workingDirectory: "./test/base"
|
||||
workingDirectory: "./src/test/fixtures/only-build"
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: deploy --dry-run
|
||||
@@ -38,20 +38,20 @@ jobs:
|
||||
uses: ./
|
||||
with:
|
||||
quiet: true
|
||||
workingDirectory: "./test/base"
|
||||
workingDirectory: "./src/test/fixtures/build-quiet"
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: deploy --dry-run
|
||||
|
||||
# START Setup and teardown of Worker Environment Tests
|
||||
- name: Environment support
|
||||
uses: ./
|
||||
with:
|
||||
workingDirectory: "./test/environment"
|
||||
workingDirectory: "./src/test/fixtures/environment"
|
||||
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
|
||||
postCommands: npx wrangler delete --name wrangler-action-dev-environment-test --force
|
||||
secrets: |
|
||||
SECRET1
|
||||
SECRET2
|
||||
@@ -59,21 +59,12 @@ jobs:
|
||||
SECRET1: ${{ secrets.SECRET1 }}
|
||||
SECRET2: ${{ secrets.SECRET2 }}
|
||||
|
||||
- name: Clean up Deployed Environment Worker
|
||||
uses: ./
|
||||
with:
|
||||
workingDirectory: "./test/base"
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: delete --name wrangler-action-dev-environment-test --force
|
||||
|
||||
# END Setup and teardown of Worker Environment Tests
|
||||
# START Setup and teardown of Workers w/ Secrets Tests
|
||||
- name: Deploy app secrets w/ hardcoded Wrangler v2
|
||||
uses: ./
|
||||
with:
|
||||
wranglerVersion: "2.20.0"
|
||||
workingDirectory: "./test/base"
|
||||
workingDirectory: "./src/test/fixtures/secrets-v2"
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
secrets: |
|
||||
@@ -84,13 +75,13 @@ jobs:
|
||||
SECRET2: ${{ secrets.SECRET2 }}
|
||||
|
||||
- name: Health Check Deployed Worker
|
||||
run: node .github/workflows/workerHealthCheck.cjs
|
||||
run: node .github/workflows/workerHealthCheck.ts wrangler-action-test-secrets-v2
|
||||
shell: bash
|
||||
|
||||
- name: Deploy app secrets w/ default version
|
||||
uses: ./
|
||||
with:
|
||||
workingDirectory: "./test/base"
|
||||
workingDirectory: "./src/test/fixtures/secrets-default"
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
secrets: |
|
||||
@@ -101,22 +92,23 @@ jobs:
|
||||
SECRET2: ${{ secrets.SECRET2 }}
|
||||
|
||||
- name: Health Check Deployed Worker
|
||||
run: node .github/workflows/workerHealthCheck.cjs
|
||||
run: node .github/workflows/workerHealthCheck.ts wrangler-action-test-secrets-default
|
||||
shell: bash
|
||||
|
||||
- name: Clean Up Deployed Workers
|
||||
uses: ./
|
||||
with:
|
||||
workingDirectory: "./test/base"
|
||||
workingDirectory: "./src/test/fixtures/secrets-default"
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: delete --name wrangler-action-test --force
|
||||
command: delete --name wrangler-action-test-secrets-v2 --force
|
||||
postCommands: npx wrangler delete --name wrangler-action-test-secrets-default --force
|
||||
# END Setup and teardown of Workers w/ Secrets Tests
|
||||
|
||||
- name: Support packageManager variable
|
||||
uses: ./
|
||||
with:
|
||||
workingDirectory: "./test/empty"
|
||||
workingDirectory: "./src/test/fixtures/specify-package-manager"
|
||||
packageManager: "npm"
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
@@ -125,7 +117,7 @@ jobs:
|
||||
- name: Support unspecified packageManager with no lockfile
|
||||
uses: ./
|
||||
with:
|
||||
workingDirectory: "./test/empty"
|
||||
workingDirectory: "./src/test/fixtures/unspecified-package-manager"
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: deploy --dry-run
|
||||
@@ -133,7 +125,7 @@ jobs:
|
||||
- name: Support npm package manager
|
||||
uses: ./
|
||||
with:
|
||||
workingDirectory: "./test/npm"
|
||||
workingDirectory: "./src/test/fixtures/npm"
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: deploy --dry-run
|
||||
@@ -144,18 +136,29 @@ jobs:
|
||||
- name: Support yarn package manager
|
||||
uses: ./
|
||||
with:
|
||||
workingDirectory: "./test/yarn"
|
||||
workingDirectory: "./src/test/fixtures/yarn"
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: deploy --dry-run
|
||||
|
||||
- name: Install pnpm
|
||||
run: npm i -g pnpm
|
||||
run: npm i -g pnpm@10
|
||||
|
||||
- name: Support pnpm package manager
|
||||
uses: ./
|
||||
with:
|
||||
workingDirectory: "./test/pnpm"
|
||||
workingDirectory: "./src/test/fixtures/pnpm"
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: deploy --dry-run
|
||||
|
||||
- name: Change directory to pre-installed-wrangler and install dependencies
|
||||
run: |
|
||||
cd ./src/test/fixtures/pre-installed-wrangler
|
||||
npm install
|
||||
|
||||
- name: Support pre-installed wrangler
|
||||
uses: ./
|
||||
with:
|
||||
workingDirectory: "./src/test/fixtures/pre-installed-wrangler"
|
||||
command: action-test
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
name: Issue
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- opened
|
||||
- transferred
|
||||
|
||||
jobs:
|
||||
add-to-project:
|
||||
name: Add issue to workers-sdk GH project
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/add-to-project@v0.5.0
|
||||
with:
|
||||
project-url: https://github.com/orgs/cloudflare/projects/1
|
||||
github-token: ${{ secrets.GH_ACCESS_TOKEN }}
|
||||
@@ -1,10 +0,0 @@
|
||||
name: Pull request
|
||||
|
||||
on: pull_request
|
||||
|
||||
jobs:
|
||||
add-to-project:
|
||||
name: Add PR to workers-sdk GH project
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: curl -X POST https://devprod-status-bot.devprod.workers.dev/pr-project/wrangler-action/${{ github.event.number }}
|
||||
@@ -19,15 +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:
|
||||
node-version: "latest"
|
||||
node-version: "24"
|
||||
cache: "npm"
|
||||
|
||||
- name: Install modules
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
name: Semgrep OSS scan
|
||||
on:
|
||||
pull_request: {}
|
||||
push:
|
||||
branches: [main, master]
|
||||
workflow_dispatch: {}
|
||||
schedule:
|
||||
- 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-oss
|
||||
runs-on: ubuntu-slim
|
||||
steps:
|
||||
- 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,20 +0,0 @@
|
||||
const { execSync } = require("child_process");
|
||||
|
||||
function workerHealthCheck() {
|
||||
const url =
|
||||
"https://wrangler-action-test.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;
|
||||
}
|
||||
|
||||
workerHealthCheck();
|
||||
@@ -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);
|
||||
},
|
||||
});
|
||||
+120
-1
@@ -1,11 +1,130 @@
|
||||
# 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
|
||||
|
||||
- [#345](https://github.com/cloudflare/wrangler-action/pull/345) [`e819570`](https://github.com/cloudflare/wrangler-action/commit/e819570b2d0a69816a1c2e9d2f2954e278748d80) Thanks [@Maximo-Guk](https://github.com/Maximo-Guk)! - fix: Pages GitHub Deployment not triggering
|
||||
|
||||
## 3.13.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#325](https://github.com/cloudflare/wrangler-action/pull/325) [`cada7a6`](https://github.com/cloudflare/wrangler-action/commit/cada7a63124ded3471bef7e8001b76356b838e40) Thanks [@Maximo-Guk](https://github.com/Maximo-Guk)! - Add GitHub deployments and job summaries for parity with pages-action
|
||||
|
||||
- [#334](https://github.com/cloudflare/wrangler-action/pull/334) [`9fed19a`](https://github.com/cloudflare/wrangler-action/commit/9fed19aa4ed79946f009e8aad7437a922e62d523) Thanks [@Maximo-Guk](https://github.com/Maximo-Guk)! - Bump default wrangler version to 3.90.0
|
||||
|
||||
## 3.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#319](https://github.com/cloudflare/wrangler-action/pull/319) [`59c04629408d58978884fadd18755f1a15f96157`](https://github.com/cloudflare/wrangler-action/commit/59c04629408d58978884fadd18755f1a15f96157) Thanks [@Maximo-Guk](https://github.com/Maximo-Guk)! - Fixes #317: Generate a new output directory with a randomUUID in the tmpDir, so that when the action is executed multiple times, we use the artifacts from that run, opposed to the artifacts from a previous run.
|
||||
|
||||
## 3.12.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#312](https://github.com/cloudflare/wrangler-action/pull/312) [`122ee5cf5b66847e0b6cfa67ecd9e03e38a67a42`](https://github.com/cloudflare/wrangler-action/commit/122ee5cf5b66847e0b6cfa67ecd9e03e38a67a42) Thanks [@Maximo-Guk](https://github.com/Maximo-Guk)! - 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.
|
||||
|
||||
## 3.11.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#309](https://github.com/cloudflare/wrangler-action/pull/309) [`10d5b9c1c1826adaec0a9ee49fdf5b91113508ef`](https://github.com/cloudflare/wrangler-action/commit/10d5b9c1c1826adaec0a9ee49fdf5b91113508ef) Thanks [@Maximo-Guk](https://github.com/Maximo-Guk)! - Revert "Add parity with pages-action for pages deploy outputs"
|
||||
|
||||
## 3.10.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#303](https://github.com/cloudflare/wrangler-action/pull/303) [`3ec7f8943ef83351f743cfaa8763a9056ef70993`](https://github.com/cloudflare/wrangler-action/commit/3ec7f8943ef83351f743cfaa8763a9056ef70993) Thanks [@courtney-sims](https://github.com/courtney-sims)! - Support id, environment, url, and alias outputs for Pages deploys.
|
||||
|
||||
## 3.9.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#298](https://github.com/cloudflare/wrangler-action/pull/298) [`134b5c2a3252d66b8c4d1cddd0b9baaeed6a4daa`](https://github.com/cloudflare/wrangler-action/commit/134b5c2a3252d66b8c4d1cddd0b9baaeed6a4daa) Thanks [@Maximo-Guk](https://github.com/Maximo-Guk)! - Update wrangler version from 3.13.2 to 3.78.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#278](https://github.com/cloudflare/wrangler-action/pull/278) [`47d51f25c113ee9205110728599b43ed6a1e273b`](https://github.com/cloudflare/wrangler-action/commit/47d51f25c113ee9205110728599b43ed6a1e273b) Thanks [@acusti](https://github.com/acusti)! - fix: Detect existing wrangler install even when wrangler version output is multiline
|
||||
|
||||
## 3.8.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#291](https://github.com/cloudflare/wrangler-action/pull/291) [`a1467a0c8f2a058f8d43a4d0c40a55176ed5efe6`](https://github.com/cloudflare/wrangler-action/commit/a1467a0c8f2a058f8d43a4d0c40a55176ed5efe6) Thanks [@Ambroos](https://github.com/Ambroos)! - Adds `deployment-alias-url` output for Pages deployment aliases (since Wrangler v3.78.0): https://github.com/cloudflare/workers-sdk/pull/6643
|
||||
|
||||
## 3.7.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#271](https://github.com/cloudflare/wrangler-action/pull/271) [`66efca2cbb82a5a49df6af2e14c4b58d53b0e266`](https://github.com/cloudflare/wrangler-action/commit/66efca2cbb82a5a49df6af2e14c4b58d53b0e266) Thanks [@Maximo-Guk](https://github.com/Maximo-Guk)! - This unreverts #235 ensuring wrangler-action will re-use existing wrangler installations, thanks @AdiRishi! and ensures we don't automatically install wrangler when checking if it present
|
||||
|
||||
## 3.6.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#265](https://github.com/cloudflare/wrangler-action/pull/265) [`2d275a8f2d279dc91912c1ff8023af109ef3280c`](https://github.com/cloudflare/wrangler-action/commit/2d275a8f2d279dc91912c1ff8023af109ef3280c) Thanks [@Maximo-Guk](https://github.com/Maximo-Guk)! - Reverts #235 which may have caused the latest version of wrangler to be installed, if no wrangler version was found
|
||||
|
||||
## 3.6.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#235](https://github.com/cloudflare/wrangler-action/pull/235) [`0545ad285acaff2b92053d636ee17fb303b4c5f5`](https://github.com/cloudflare/wrangler-action/commit/0545ad285acaff2b92053d636ee17fb303b4c5f5) Thanks [@AdiRishi](https://github.com/AdiRishi)! - wrangler-action will now re-use existing wrangler installations when available
|
||||
|
||||
## 3.5.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#255](https://github.com/cloudflare/wrangler-action/pull/255) [`31a6263ef3ec73ff2d03cb4c0260379f96f7598c`](https://github.com/cloudflare/wrangler-action/commit/31a6263ef3ec73ff2d03cb4c0260379f96f7598c) Thanks [@matthewdavidrodgers](https://github.com/matthewdavidrodgers)! - Stop racing secret uploads
|
||||
|
||||
For up to date versions of wrangler, secrets are uploaded via the 'secret:bulk' command, which batches updates in a single API call.
|
||||
|
||||
For versions of wrangler without that capability, the action falls back to the single 'secret put' command for each secret. It races all these with a Promise.all()
|
||||
|
||||
Unfortunately, the single secret API cannot handle concurrency - at best, these calls have to wait on one another, holding requests open all the while. Often it times out and errors.
|
||||
|
||||
This fixes the legacy secret upload errors by making these calls serially instead of concurrently.
|
||||
|
||||
## 3.4.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#227](https://github.com/cloudflare/wrangler-action/pull/227) [`bbedd8e54f256d36f81f81f1f05b90937d533bb7`](https://github.com/cloudflare/wrangler-action/commit/bbedd8e54f256d36f81f81f1f05b90937d533bb7) Thanks [@AdiRishi](https://github.com/AdiRishi)! - Surface inner exception when secret:bulk upload command fails
|
||||
|
||||
## 3.4.0
|
||||
|
||||
### 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
-2
@@ -1,3 +1,3 @@
|
||||
* @cloudflare/wrangler
|
||||
* @cloudflare/wrangler @cloudflare/banda-team
|
||||
|
||||
CHANGELOG.md @cloudflare/wrangler-admins
|
||||
CHANGELOG.md @cloudflare/wrangler-admins @cloudflare/banda-team
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# Contributing Guide
|
||||
|
||||
## Releases
|
||||
|
||||
### Changesets
|
||||
|
||||
Every non-trivial change to the project - those that should appear in the changelog - must be captured in a "changeset". We use the changesets tool for creating changesets, publishing versions and updating the changelog.
|
||||
|
||||
Create a changeset for the current change.
|
||||
|
||||
> npx changeset
|
||||
> Select which workspaces are affected by the change and whether the version requires a major, minor or patch release.
|
||||
> Update the generated changeset with a description of the change.
|
||||
> Include the generate changeset in the current commit.
|
||||
> git add ./changeset/\*.md
|
||||
|
||||
### Version Packages PRs
|
||||
|
||||
Once you merge your PR, a new Version Packages PR will be opened in the wrangler-action repo. Once that PR is merged, your change will be released. Example: https://github.com/cloudflare/wrangler-action/pull/305
|
||||
|
||||
Note: Version Packages PRs are only generated if there's been at least one PR merged with a changeset.
|
||||
@@ -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@v3
|
||||
- uses: actions/checkout@v6
|
||||
- name: Deploy
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
@@ -36,7 +47,7 @@ jobs:
|
||||
|
||||
## Authentication
|
||||
|
||||
You'll need to configure Wrangler using GitHub's Secrets feature - go to "Settings -> Secrets" and add your Cloudflare API token (for help finding this, see the [Workers documentation](https://developers.cloudflare.com/workers/quickstart/#api-token)). Your API token is encrypted by GitHub, and the action won't print it into logs, so it should be safe!
|
||||
You'll need to configure Wrangler using GitHub's Secrets feature - go to "Settings -> Secrets" and add your Cloudflare API token (for help finding this, see the [Workers documentation](https://developers.cloudflare.com/workers/wrangler/ci-cd/#api-token)). Your API token is encrypted by GitHub, and the action won't print it into logs, so it should be safe!
|
||||
|
||||
With your API token set as a secret for your repository, pass it to the action in the `with` block of your workflow. Below, I've set the secret name to `CLOUDFLARE_API_TOKEN`:
|
||||
|
||||
@@ -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
|
||||
@@ -76,7 +89,7 @@ jobs:
|
||||
workingDirectory: "subfoldername"
|
||||
```
|
||||
|
||||
[Worker secrets](https://developers.cloudflare.com/workers/tooling/wrangler/secrets/) can optionally be passed in via `secrets` as a string of names separated by newlines. Each secret name must match the name of an environment variable specified in the `env` field. This creates or replaces the value for the Worker secret using the `wrangler secret put` command.
|
||||
[Worker secrets](https://developers.cloudflare.com/workers/tooling/wrangler/secrets/) can optionally be passed in via `secrets` as a string of names separated by newlines. Each secret name must match the name of an environment variable specified in the `env` field. This creates or replaces the value for the Worker secret using the `wrangler secret put` command. It's also possible to specify worker environment using environment parameter.
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
@@ -85,6 +98,7 @@ jobs:
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
environment: production
|
||||
secrets: |
|
||||
SECRET1
|
||||
SECRET2
|
||||
@@ -121,6 +135,20 @@ jobs:
|
||||
command: whoami
|
||||
```
|
||||
|
||||
You can also add a command that spans multiple lines:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
deploy:
|
||||
steps:
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
command: |
|
||||
pages project list
|
||||
pages deploy .vercel/output/static --project-name=demo-actions --branch=test
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### Deploy when commits are merged to main
|
||||
@@ -138,7 +166,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v6
|
||||
- name: Deploy
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
@@ -158,14 +186,19 @@ jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy
|
||||
permissions:
|
||||
contents: read
|
||||
deployments: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v6
|
||||
- name: Deploy
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: pages deploy --project-name=example
|
||||
command: pages deploy YOUR_DIST_FOLDER --project-name=example
|
||||
# Optional: Enable this if you want to have GitHub Deployments triggered
|
||||
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
|
||||
### Deploying on a schedule
|
||||
@@ -182,7 +215,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v6
|
||||
- name: Deploy app
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
@@ -208,7 +241,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v6
|
||||
- name: Deploy app
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
@@ -218,8 +251,43 @@ jobs:
|
||||
|
||||
For more advanced usage or to programmatically trigger the workflow from scripts, refer to [the GitHub documentation](https://docs.github.com/en/rest/reference/actions#create-a-workflow-dispatch-event) for making API calls.
|
||||
|
||||
### Upload a Worker Version
|
||||
|
||||
To create a new version of your Worker that is not deployed immediately, use the `wrangler versions upload` command. Worker versions created in this way can then be deployed all at once at a later time or gradually deployed using the `wrangler versions deploy` command or via the Cloudflare dashboard under the Deployments tab. Wrangler v3.40.0 or above is required to use this feature.
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
upload:
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Upload Worker Version
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: versions upload
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Setting A Worker Secret for A Specific Environment
|
||||
|
||||
There is an environment parameter that can be set within the workflow to enable this. Example:
|
||||
|
||||
```yaml
|
||||
- uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
command: deploy --env production
|
||||
secrets: |
|
||||
SUPER_SECRET
|
||||
environment: production
|
||||
env:
|
||||
SUPER_SECRET: ${{ secrets.SUPER_SECRET }}
|
||||
```
|
||||
|
||||
### Using Wrangler Command Output in Subsequent Steps
|
||||
|
||||
More advanced workflows may need to parse the resulting output of Wrangler commands. To do this, you can use the `command-output` output variable in subsequent steps. For example, if you want to print the output of the Wrangler command, you can do the following:
|
||||
@@ -243,7 +311,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` Output Variable
|
||||
### 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:
|
||||
|
||||
@@ -268,6 +336,39 @@ 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 `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 pages-deployment-alias-url
|
||||
env:
|
||||
DEPLOYMENT_ALIAS_URL: ${{ steps.deploy.outputs.pages-deployment-alias-url }}
|
||||
run: echo $DEPLOYMENT_ALIAS_URL
|
||||
```
|
||||
|
||||
Resulting in:
|
||||
|
||||
```text
|
||||
https://new-feature.<your_pages_site>.pages.dev
|
||||
```
|
||||
|
||||
### Using a different package manager
|
||||
|
||||
By default, this action will detect which package manager to use, based on the presence of a `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, or `bun.lockb`/`bun.lock` file.
|
||||
|
||||
If you need to use a specific package manager for your application, you can set the `packageManager` input to `npm`, `yarn`, `pnpm`, or `bun`. You don't need to set this option unless you want to override the default behavior.
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
deploy:
|
||||
steps:
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
packageManager: pnpm
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "I just started using Workers/Wrangler and I don't know what this is!"
|
||||
@@ -286,7 +387,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v6
|
||||
- name: Deploy app
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
|
||||
+10
-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:
|
||||
@@ -44,6 +44,9 @@ inputs:
|
||||
packageManager:
|
||||
description: "The package manager you'd like to use to install and run wrangler. If not specified, the preferred package manager will be inferred based on the presence of a lockfile or fallback to using npm if no lockfile is found. Valid values are `npm` | `pnpm` | `yarn` | `bun`."
|
||||
required: false
|
||||
gitHubToken:
|
||||
description: "GitHub Token"
|
||||
required: false
|
||||
outputs:
|
||||
command-output:
|
||||
description: "The output of the Wrangler command (comes from stdout)"
|
||||
@@ -51,3 +54,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"
|
||||
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"
|
||||
|
||||
Generated
+2152
-3132
File diff suppressed because it is too large
Load Diff
+22
-12
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wrangler-action",
|
||||
"version": "3.4.0",
|
||||
"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",
|
||||
@@ -29,18 +29,28 @@
|
||||
"check": "prettier --check ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/exec": "^1.1.1"
|
||||
"@actions/core": "^1.11.1",
|
||||
"@actions/exec": "^1.1.1",
|
||||
"@actions/github": "^6.0.0",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@changesets/changelog-github": "^0.4.8",
|
||||
"@changesets/cli": "^2.26.2",
|
||||
"@cloudflare/workers-types": "^4.20231121.0",
|
||||
"@types/node": "^20.10.4",
|
||||
"@vercel/ncc": "^0.38.1",
|
||||
"prettier": "^3.1.0",
|
||||
"semver": "^7.5.4",
|
||||
"typescript": "^5.3.3",
|
||||
"vitest": "^1.0.3"
|
||||
"@changesets/changelog-github": "^0.5.0",
|
||||
"@changesets/cli": "^2.27.12",
|
||||
"@cloudflare/workers-types": "^4.20241022.0",
|
||||
"@types/mock-fs": "^4.13.4",
|
||||
"@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": "^3.2.4"
|
||||
},
|
||||
"overrides": {
|
||||
"undici": "^6.23.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
import { setOutput } from "@actions/core";
|
||||
import { info, WranglerActionConfig } from "./wranglerAction";
|
||||
import {
|
||||
getOutputEntry,
|
||||
OutputEntryDeployment,
|
||||
OutputEntryPagesDeployment,
|
||||
OutputEntryVersionUpload,
|
||||
} from "./wranglerArtifactManager";
|
||||
import { createGitHubDeploymentAndJobSummary } from "./service/github";
|
||||
|
||||
// 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 handlePagesDeployOutputEntry(
|
||||
config: WranglerActionConfig,
|
||||
pagesDeployOutputEntry: OutputEntryPagesDeployment,
|
||||
) {
|
||||
setOutput("deployment-url", pagesDeployOutputEntry.url);
|
||||
// DEPRECATED: deployment-alias-url in favour of pages-deployment-alias, drop in next wrangler-action major version change
|
||||
setOutput("deployment-alias-url", pagesDeployOutputEntry.alias);
|
||||
setOutput("pages-deployment-alias-url", pagesDeployOutputEntry.alias);
|
||||
setOutput("pages-deployment-id", pagesDeployOutputEntry.deployment_id);
|
||||
setOutput("pages-environment", pagesDeployOutputEntry.environment);
|
||||
|
||||
// Create github deployment, if GITHUB_TOKEN is present in config
|
||||
await createGitHubDeploymentAndJobSummary(config, pagesDeployOutputEntry);
|
||||
}
|
||||
|
||||
/**
|
||||
* If no wrangler output file found, fallback to extracting deployment-url from stdout.
|
||||
* @deprecated Use {@link handlePagesDeployOutputEntry} instead.
|
||||
*/
|
||||
function handlePagesDeployCommand(
|
||||
config: WranglerActionConfig,
|
||||
stdOut: string,
|
||||
) {
|
||||
info(
|
||||
config,
|
||||
"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);
|
||||
}
|
||||
|
||||
function handleWranglerDeployOutputEntry(
|
||||
config: WranglerActionConfig,
|
||||
wranglerDeployOutputEntry: OutputEntryDeployment,
|
||||
) {
|
||||
// If no deployment urls found in wrangler output file, log that we couldn't find any urls and return.
|
||||
if (
|
||||
!wranglerDeployOutputEntry.targets ||
|
||||
wranglerDeployOutputEntry.targets.length === 0
|
||||
) {
|
||||
info(config, "No deployment-url found in wrangler deploy output file");
|
||||
return;
|
||||
}
|
||||
|
||||
// If more than 1 deployment url found, log that we're going to set deployment-url to the first match.
|
||||
// In a future wrangler-action version we should consider how we're going to output multiple deployment-urls
|
||||
if (wranglerDeployOutputEntry.targets.length > 1) {
|
||||
info(
|
||||
config,
|
||||
"Multiple deployment urls found in wrangler deploy output file, deployment-url will be set to the first url",
|
||||
);
|
||||
}
|
||||
|
||||
setOutput("deployment-url", wranglerDeployOutputEntry.targets[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* If no wrangler output file found, fallback to extracting deployment-url from stdout.
|
||||
* @deprecated Use {@link handleWranglerDeployOutputEntry} instead.
|
||||
*/
|
||||
function handleWranglerDeployCommand(
|
||||
config: WranglerActionConfig,
|
||||
stdOut: string,
|
||||
) {
|
||||
info(
|
||||
config,
|
||||
"Unable to find a WRANGLER_OUTPUT_DIR, deployment-url may have an unreliable output. Have you updated wrangler to version >=3.88.0?",
|
||||
);
|
||||
const { deploymentUrl } = extractDeploymentUrlsFromStdout(stdOut);
|
||||
setOutput("deployment-url", deploymentUrl);
|
||||
}
|
||||
|
||||
function handleVersionsUploadOutputEntry(
|
||||
versionsOutputEntry: OutputEntryVersionUpload,
|
||||
) {
|
||||
setOutput("deployment-url", versionsOutputEntry.preview_url);
|
||||
}
|
||||
|
||||
/**
|
||||
* If no wrangler output file found, log a message stating deployment-url will be unavailable for output.
|
||||
* @deprecated Use {@link handleVersionsOutputEntry} instead.
|
||||
*/
|
||||
function handleVersionsOutputCommand(config: WranglerActionConfig) {
|
||||
info(
|
||||
config,
|
||||
"Unable to find a WRANGLER_OUTPUT_DIR, deployment-url will be unavailable for output. Have you updated wrangler to version >=3.88.0?",
|
||||
);
|
||||
}
|
||||
|
||||
function handleDeprectatedStdoutParsing(
|
||||
config: WranglerActionConfig,
|
||||
command: string,
|
||||
stdOut: string,
|
||||
) {
|
||||
// Check if this command is a pages deployment
|
||||
if (
|
||||
command.startsWith("pages deploy") ||
|
||||
command.startsWith("pages publish")
|
||||
) {
|
||||
handlePagesDeployCommand(config, stdOut);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this command is a workers deployment
|
||||
if (command.startsWith("deploy") || command.startsWith("publish")) {
|
||||
handleWranglerDeployCommand(config, stdOut);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this command is a versions deployment
|
||||
if (command.startsWith("versions upload")) {
|
||||
handleVersionsOutputCommand(config);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export async function handleCommandOutputParsing(
|
||||
config: WranglerActionConfig,
|
||||
command: string,
|
||||
stdOut: string,
|
||||
) {
|
||||
// get first OutputEntry found within wrangler artifact output directory
|
||||
const outputEntry = await getOutputEntry(config.WRANGLER_OUTPUT_DIR);
|
||||
|
||||
if (outputEntry === null) {
|
||||
// if no outputEntry found, fallback to deprecated stdOut parsing
|
||||
handleDeprectatedStdoutParsing(config, command, stdOut);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (outputEntry.type) {
|
||||
case "pages-deploy-detailed":
|
||||
await handlePagesDeployOutputEntry(config, outputEntry);
|
||||
break;
|
||||
case "deploy":
|
||||
handleWranglerDeployOutputEntry(config, outputEntry);
|
||||
break;
|
||||
case "version-upload":
|
||||
handleVersionsUploadOutputEntry(outputEntry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -43,7 +43,7 @@ export async function execShell(
|
||||
|
||||
await promise;
|
||||
return child.exitCode;
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
if (isExecAsyncException(err)) {
|
||||
process.stderr.write(err.stderr);
|
||||
throw new Error(`Process failed with exit code ${err.code}`);
|
||||
|
||||
+14
-278
@@ -1,25 +1,18 @@
|
||||
import {
|
||||
getBooleanInput,
|
||||
getInput,
|
||||
getMultilineInput,
|
||||
endGroup as originalEndGroup,
|
||||
error as originalError,
|
||||
info as originalInfo,
|
||||
startGroup as originalStartGroup,
|
||||
setFailed,
|
||||
setOutput,
|
||||
} from "@actions/core";
|
||||
import { exec, execShell } from "./exec";
|
||||
import { checkWorkingDirectory, semverCompare } from "./utils";
|
||||
import { getBooleanInput, getInput, getMultilineInput } from "@actions/core";
|
||||
import { tmpdir } from "os";
|
||||
import { join } from "path";
|
||||
import { getPackageManager } from "./packageManagers";
|
||||
import { checkWorkingDirectory } from "./utils";
|
||||
import { main, WranglerActionConfig } from "./wranglerAction";
|
||||
|
||||
const DEFAULT_WRANGLER_VERSION = "3.13.2";
|
||||
const DEFAULT_WRANGLER_VERSION = "4";
|
||||
|
||||
/**
|
||||
* A configuration object that contains all the inputs & immutable state for the action.
|
||||
*/
|
||||
const config = {
|
||||
const config: WranglerActionConfig = {
|
||||
WRANGLER_VERSION: getInput("wranglerVersion") || DEFAULT_WRANGLER_VERSION,
|
||||
didUserProvideWranglerVersion: Boolean(getInput("wranglerVersion")),
|
||||
secrets: getMultilineInput("secrets"),
|
||||
workingDirectory: checkWorkingDirectory(getInput("workingDirectory")),
|
||||
CLOUDFLARE_API_TOKEN: getInput("apiToken"),
|
||||
@@ -29,272 +22,15 @@ const config = {
|
||||
COMMANDS: getMultilineInput("command"),
|
||||
QUIET_MODE: getBooleanInput("quiet"),
|
||||
PACKAGE_MANAGER: getInput("packageManager"),
|
||||
WRANGLER_OUTPUT_DIR: `${join(
|
||||
tmpdir(),
|
||||
`wranglerArtifacts-${crypto.randomUUID()}`,
|
||||
)}`,
|
||||
GITHUB_TOKEN: getInput("gitHubToken", { required: false }),
|
||||
} as const;
|
||||
|
||||
const packageManager = getPackageManager(config.PACKAGE_MANAGER, {
|
||||
workingDirectory: config.workingDirectory,
|
||||
});
|
||||
|
||||
function info(message: string, bypass?: boolean): void {
|
||||
if (!config.QUIET_MODE || bypass) {
|
||||
originalInfo(message);
|
||||
}
|
||||
}
|
||||
|
||||
function error(message: string, bypass?: boolean): void {
|
||||
if (!config.QUIET_MODE || bypass) {
|
||||
originalError(message);
|
||||
}
|
||||
}
|
||||
|
||||
function startGroup(name: string): void {
|
||||
if (!config.QUIET_MODE) {
|
||||
originalStartGroup(name);
|
||||
}
|
||||
}
|
||||
|
||||
function endGroup(): void {
|
||||
if (!config.QUIET_MODE) {
|
||||
originalEndGroup();
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
authenticationSetup();
|
||||
await installWrangler();
|
||||
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 installWrangler() {
|
||||
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`,
|
||||
);
|
||||
}
|
||||
|
||||
startGroup("📥 Installing Wrangler");
|
||||
try {
|
||||
await exec(
|
||||
packageManager.install,
|
||||
[`wrangler@${config["WRANGLER_VERSION"]}`],
|
||||
{
|
||||
cwd: config["workingDirectory"],
|
||||
silent: config["QUIET_MODE"],
|
||||
},
|
||||
);
|
||||
|
||||
info(`✅ Wrangler installed`, true);
|
||||
} finally {
|
||||
endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
function authenticationSetup() {
|
||||
process.env.CLOUDFLARE_API_TOKEN = config["CLOUDFLARE_API_TOKEN"];
|
||||
process.env.CLOUDFLARE_ACCOUNT_ID = config["CLOUDFLARE_ACCOUNT_ID"];
|
||||
}
|
||||
|
||||
async function execCommands(commands: string[], cmdType: string) {
|
||||
if (!commands.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
startGroup(`🚀 Running ${cmdType}Commands`);
|
||||
try {
|
||||
for (const command of commands) {
|
||||
const cmd = command.startsWith("wrangler")
|
||||
? `${packageManager.exec} ${command}`
|
||||
: command;
|
||||
|
||||
await execShell(cmd, {
|
||||
cwd: config["workingDirectory"],
|
||||
silent: config["QUIET_MODE"],
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
function getSecret(secret: string) {
|
||||
if (!secret) {
|
||||
throw new Error("Secret name cannot be blank.");
|
||||
}
|
||||
|
||||
const value = process.env[secret];
|
||||
if (!value) {
|
||||
throw new Error(`Value for secret ${secret} not found in environment.`);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function getEnvVar(envVar: string) {
|
||||
if (!envVar) {
|
||||
throw new Error("Var name cannot be blank.");
|
||||
}
|
||||
|
||||
const value = process.env[envVar];
|
||||
if (!value) {
|
||||
throw new Error(`Value for var ${envVar} not found in environment.`);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function legacyUploadSecrets(
|
||||
secrets: string[],
|
||||
environment?: string,
|
||||
workingDirectory?: string,
|
||||
) {
|
||||
return Promise.all(
|
||||
secrets.map((secret) => {
|
||||
const args = ["wrangler", "secret", "put", secret];
|
||||
if (environment) {
|
||||
args.push("--env", environment);
|
||||
}
|
||||
return exec(packageManager.exec, args, {
|
||||
cwd: workingDirectory,
|
||||
silent: config["QUIET_MODE"],
|
||||
input: Buffer.from(getSecret(secret)),
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async function uploadSecrets() {
|
||||
const secrets: string[] = config["secrets"];
|
||||
const environment = config["ENVIRONMENT"];
|
||||
const workingDirectory = config["workingDirectory"];
|
||||
|
||||
if (!secrets.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
startGroup("🔑 Uploading secrets...");
|
||||
|
||||
try {
|
||||
if (semverCompare(config["WRANGLER_VERSION"], "3.4.0")) {
|
||||
return legacyUploadSecrets(secrets, environment, workingDirectory);
|
||||
}
|
||||
|
||||
const args = ["wrangler", "secret:bulk"];
|
||||
|
||||
if (environment) {
|
||||
args.push("--env", environment);
|
||||
}
|
||||
|
||||
await exec(packageManager.exec, args, {
|
||||
cwd: workingDirectory,
|
||||
silent: config["QUIET_MODE"],
|
||||
input: Buffer.from(
|
||||
JSON.stringify(
|
||||
Object.fromEntries(
|
||||
secrets.map((secret) => [secret, getSecret(secret)]),
|
||||
),
|
||||
),
|
||||
),
|
||||
});
|
||||
} catch (err) {
|
||||
throw new Error(`Failed to upload secrets.`);
|
||||
} finally {
|
||||
endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
async function wranglerCommands() {
|
||||
startGroup("🚀 Running Wrangler Commands");
|
||||
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);
|
||||
}
|
||||
|
||||
for (let command of commands) {
|
||||
const args = [];
|
||||
|
||||
if (environment && !command.includes("--env")) {
|
||||
args.push("--env", environment);
|
||||
}
|
||||
|
||||
if (
|
||||
config["VARS"].length &&
|
||||
(command.startsWith("deploy") || command.startsWith("publish")) &&
|
||||
!command.includes("--var")
|
||||
) {
|
||||
args.push("--var");
|
||||
for (const v of config["VARS"]) {
|
||||
args.push(`${v}:${getEnvVar(v)}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Used for saving the wrangler output
|
||||
let stdOut = "";
|
||||
let stdErr = "";
|
||||
|
||||
// Construct the options for the exec command
|
||||
const options = {
|
||||
cwd: config["workingDirectory"],
|
||||
silent: config["QUIET_MODE"],
|
||||
listeners: {
|
||||
stdout: (data: Buffer) => {
|
||||
stdOut += data.toString();
|
||||
},
|
||||
stderr: (data: Buffer) => {
|
||||
stdErr += data.toString();
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Execute the wrangler command
|
||||
await exec(`${packageManager.exec} wrangler ${command}`, args, options);
|
||||
|
||||
// Set the outputs for the command
|
||||
setOutput("command-output", stdOut);
|
||||
setOutput("command-stderr", stdErr);
|
||||
|
||||
// Check if this command is a workers or 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
export {
|
||||
authenticationSetup,
|
||||
execCommands,
|
||||
installWrangler,
|
||||
uploadSecrets,
|
||||
wranglerCommands,
|
||||
};
|
||||
main(config, packageManager);
|
||||
|
||||
+62
-46
@@ -3,92 +3,108 @@ import { getPackageManager } from "./packageManagers";
|
||||
|
||||
describe("getPackageManager", () => {
|
||||
test("should use provided value instead of inferring from lockfile", () => {
|
||||
expect(getPackageManager("npm", { workingDirectory: "test/npm" }))
|
||||
.toMatchInlineSnapshot(`
|
||||
expect(
|
||||
getPackageManager("npm", { workingDirectory: "src/test/fixtures/npm" }),
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"exec": "npx",
|
||||
"execNoInstall": "npx --no-install",
|
||||
"install": "npm i",
|
||||
}
|
||||
`);
|
||||
|
||||
expect(getPackageManager("yarn", { workingDirectory: "test/npm" }))
|
||||
.toMatchInlineSnapshot(`
|
||||
{
|
||||
"exec": "yarn",
|
||||
"install": "yarn add",
|
||||
}
|
||||
`);
|
||||
expect(
|
||||
getPackageManager("yarn", { workingDirectory: "src/test/fixtures/npm" }),
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"exec": "yarn",
|
||||
"execNoInstall": "yarn",
|
||||
"install": "yarn add",
|
||||
}
|
||||
`);
|
||||
|
||||
expect(getPackageManager("pnpm", { workingDirectory: "test/npm" }))
|
||||
.toMatchInlineSnapshot(`
|
||||
expect(
|
||||
getPackageManager("pnpm", { workingDirectory: "src/test/fixtures/npm" }),
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"exec": "pnpm exec",
|
||||
"execNoInstall": "pnpm exec",
|
||||
"install": "pnpm add",
|
||||
}
|
||||
`);
|
||||
|
||||
expect(getPackageManager("bun", { workingDirectory: "test/bun" }))
|
||||
.toMatchInlineSnapshot(`
|
||||
expect(
|
||||
getPackageManager("bun", { workingDirectory: "src/test/fixtures/bun" }),
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"exec": "bunx",
|
||||
"execNoInstall": "bun run",
|
||||
"install": "bun i",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test("should use npm if no value provided and package-lock.json exists", () => {
|
||||
expect(getPackageManager("", { workingDirectory: "test/npm" }))
|
||||
expect(getPackageManager("", { workingDirectory: "src/test/fixtures/npm" }))
|
||||
.toMatchInlineSnapshot(`
|
||||
{
|
||||
"exec": "npx",
|
||||
"install": "npm i",
|
||||
}
|
||||
`);
|
||||
{
|
||||
"exec": "npx",
|
||||
"execNoInstall": "npx --no-install",
|
||||
"install": "npm i",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test("should use yarn if no value provided and yarn.lock exists", () => {
|
||||
expect(getPackageManager("", { workingDirectory: "test/yarn" }))
|
||||
.toMatchInlineSnapshot(`
|
||||
{
|
||||
"exec": "yarn",
|
||||
"install": "yarn add",
|
||||
}
|
||||
`);
|
||||
expect(
|
||||
getPackageManager("", { workingDirectory: "src/test/fixtures/yarn" }),
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"exec": "yarn",
|
||||
"execNoInstall": "yarn",
|
||||
"install": "yarn add",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test("should use pnpm if no value provided and pnpm-lock.yaml exists", () => {
|
||||
expect(getPackageManager("", { workingDirectory: "test/pnpm" }))
|
||||
.toMatchInlineSnapshot(`
|
||||
{
|
||||
"exec": "pnpm exec",
|
||||
"install": "pnpm add",
|
||||
}
|
||||
`);
|
||||
expect(
|
||||
getPackageManager("", { workingDirectory: "src/test/fixtures/pnpm" }),
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"exec": "pnpm exec",
|
||||
"execNoInstall": "pnpm exec",
|
||||
"install": "pnpm add",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test("should use bun if no value provided and bun.lockb exists", () => {
|
||||
expect(getPackageManager("", { workingDirectory: "test/bun" }))
|
||||
expect(getPackageManager("", { workingDirectory: "src/test/fixtures/bun" }))
|
||||
.toMatchInlineSnapshot(`
|
||||
{
|
||||
"exec": "bunx",
|
||||
"install": "bun i",
|
||||
}
|
||||
{
|
||||
"exec": "bunx",
|
||||
"execNoInstall": "bun run",
|
||||
"install": "bun i",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test("should use npm if no value provided and no lockfile is present", () => {
|
||||
expect(getPackageManager("", { workingDirectory: "test/empty" }))
|
||||
.toMatchInlineSnapshot(`
|
||||
{
|
||||
"exec": "npx",
|
||||
"install": "npm i",
|
||||
}
|
||||
`);
|
||||
expect(
|
||||
getPackageManager("", { workingDirectory: "src/test/fixtures/empty" }),
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"exec": "npx",
|
||||
"execNoInstall": "npx --no-install",
|
||||
"install": "npm i",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test("should throw if an invalid value is provided", () => {
|
||||
expect(() =>
|
||||
getPackageManager("cargo", { workingDirectory: "test/npm" }),
|
||||
getPackageManager("cargo", { workingDirectory: "src/test/fixtures/npm" }),
|
||||
).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
+10
-2
@@ -1,27 +1,32 @@
|
||||
import { existsSync } from "node:fs";
|
||||
import * as path from "node:path";
|
||||
|
||||
interface PackageManager {
|
||||
export interface PackageManager {
|
||||
install: string;
|
||||
exec: string;
|
||||
execNoInstall: string;
|
||||
}
|
||||
|
||||
const PACKAGE_MANAGERS = {
|
||||
npm: {
|
||||
install: "npm i",
|
||||
exec: "npx",
|
||||
execNoInstall: "npx --no-install",
|
||||
},
|
||||
yarn: {
|
||||
install: "yarn add",
|
||||
exec: "yarn",
|
||||
execNoInstall: "yarn",
|
||||
},
|
||||
pnpm: {
|
||||
install: "pnpm add",
|
||||
exec: "pnpm exec",
|
||||
execNoInstall: "pnpm exec",
|
||||
},
|
||||
bun: {
|
||||
install: "bun i",
|
||||
exec: "bunx",
|
||||
execNoInstall: "bun run",
|
||||
},
|
||||
} as const satisfies Readonly<Record<string, PackageManager>>;
|
||||
|
||||
@@ -39,7 +44,10 @@ function detectPackageManager(
|
||||
if (existsSync(path.join(workingDirectory, "pnpm-lock.yaml"))) {
|
||||
return "pnpm";
|
||||
}
|
||||
if (existsSync(path.join(workingDirectory, "bun.lockb"))) {
|
||||
if (
|
||||
existsSync(path.join(workingDirectory, "bun.lockb")) ||
|
||||
existsSync(path.join(workingDirectory, "bun.lock"))
|
||||
) {
|
||||
return "bun";
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { setupServer } from "msw/node";
|
||||
import { createGitHubDeployment, createJobSummary } from "./github";
|
||||
import { getOctokit } from "@actions/github";
|
||||
import { mockGithubDeployments } from "../test/mocks";
|
||||
import { getTestConfig } from "../test/test-utils";
|
||||
import mockfs from "mock-fs";
|
||||
import { readFile } from "fs/promises";
|
||||
|
||||
afterEach(() => {
|
||||
mockfs.restore();
|
||||
});
|
||||
|
||||
describe("github", () => {
|
||||
it("Calls createGitHubDeployment successfully", async () => {
|
||||
const githubUser = "mock-user";
|
||||
const githubRepoName = "wrangler-action";
|
||||
const server = setupServer(
|
||||
...mockGithubDeployments({ githubUser, githubRepoName }).handlers,
|
||||
);
|
||||
server.listen({ onUnhandledRequest: "error" });
|
||||
vi.stubEnv("GITHUB_REPOSITORY", `${githubUser}/${githubRepoName}`);
|
||||
|
||||
const testConfig = getTestConfig();
|
||||
const octokit = getOctokit(testConfig.GITHUB_TOKEN, { request: fetch });
|
||||
await createGitHubDeployment({
|
||||
config: testConfig,
|
||||
octokit,
|
||||
productionBranch: "production-branch",
|
||||
deploymentId: "fake-deployment-id",
|
||||
projectName: "fake-project-name",
|
||||
deploymentUrl: "https://fake-deployment-url.com",
|
||||
environment: "production",
|
||||
});
|
||||
server.close();
|
||||
});
|
||||
it("Calls createJobSummary successfully", async () => {
|
||||
vi.stubEnv("GITHUB_STEP_SUMMARY", "summary");
|
||||
mockfs({
|
||||
summary: mockfs.file(),
|
||||
});
|
||||
await createJobSummary({
|
||||
commitHash: "fake-commit-hash",
|
||||
deploymentUrl: "https://fake-deployment-url.com",
|
||||
aliasUrl: "https://fake-alias-url.com",
|
||||
});
|
||||
expect((await readFile("summary")).toString()).toMatchInlineSnapshot(`
|
||||
"
|
||||
# Deploying with Cloudflare Pages
|
||||
|
||||
| Name | Result |
|
||||
| ----------------------- | - |
|
||||
| **Last commit:** | fake-commit-hash |
|
||||
| **Preview URL**: | https://fake-deployment-url.com |
|
||||
| **Branch Preview URL**: | https://fake-alias-url.com |
|
||||
"
|
||||
`);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,128 @@
|
||||
import { summary } from "@actions/core";
|
||||
import { context, getOctokit } from "@actions/github";
|
||||
import { env } from "process";
|
||||
import { info, warn } from "../utils";
|
||||
import { OutputEntryPagesDeployment } from "../wranglerArtifactManager";
|
||||
import { WranglerActionConfig } from "../wranglerAction";
|
||||
|
||||
type Octokit = ReturnType<typeof getOctokit>;
|
||||
|
||||
export async function createGitHubDeployment({
|
||||
config,
|
||||
octokit,
|
||||
productionBranch,
|
||||
environment,
|
||||
deploymentId,
|
||||
projectName,
|
||||
deploymentUrl,
|
||||
}: {
|
||||
config: WranglerActionConfig;
|
||||
octokit: Octokit;
|
||||
productionBranch: string;
|
||||
environment: string;
|
||||
deploymentId: string | null;
|
||||
projectName: string;
|
||||
deploymentUrl?: string;
|
||||
}) {
|
||||
const githubBranch = env.GITHUB_HEAD_REF || env.GITHUB_REF_NAME;
|
||||
const productionEnvironment = githubBranch === productionBranch;
|
||||
|
||||
const deployment = await octokit.rest.repos.createDeployment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: githubBranch || context.ref,
|
||||
auto_merge: false,
|
||||
description: "Cloudflare Pages",
|
||||
required_contexts: [],
|
||||
environment,
|
||||
production_environment: productionEnvironment,
|
||||
});
|
||||
|
||||
if (deployment.status !== 201) {
|
||||
info(config, "Error creating GitHub deployment");
|
||||
return;
|
||||
}
|
||||
await octokit.rest.repos.createDeploymentStatus({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
deployment_id: deployment.data.id,
|
||||
environment,
|
||||
environment_url: deploymentUrl,
|
||||
production_environment: productionEnvironment,
|
||||
// don't have project_name or deployment_id I think
|
||||
log_url: `https://dash.cloudflare.com/${config.CLOUDFLARE_ACCOUNT_ID}/pages/view/${projectName}/${deploymentId}`,
|
||||
description: "Cloudflare Pages",
|
||||
state: "success",
|
||||
auto_inactive: false,
|
||||
});
|
||||
}
|
||||
|
||||
export async function createJobSummary({
|
||||
commitHash,
|
||||
deploymentUrl,
|
||||
aliasUrl,
|
||||
}: {
|
||||
commitHash: string;
|
||||
deploymentUrl?: string;
|
||||
aliasUrl?: string;
|
||||
}) {
|
||||
await summary
|
||||
.addRaw(
|
||||
`
|
||||
# Deploying with Cloudflare Pages
|
||||
|
||||
| Name | Result |
|
||||
| ----------------------- | - |
|
||||
| **Last commit:** | ${commitHash} |
|
||||
| **Preview URL**: | ${deploymentUrl} |
|
||||
| **Branch Preview URL**: | ${aliasUrl} |
|
||||
`,
|
||||
)
|
||||
.write();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create github deployment, if GITHUB_TOKEN is present in config
|
||||
*/
|
||||
export async function createGitHubDeploymentAndJobSummary(
|
||||
config: WranglerActionConfig,
|
||||
pagesArtifactFields: OutputEntryPagesDeployment,
|
||||
) {
|
||||
if (
|
||||
config.GITHUB_TOKEN &&
|
||||
pagesArtifactFields.production_branch &&
|
||||
pagesArtifactFields.pages_project &&
|
||||
pagesArtifactFields.deployment_trigger
|
||||
) {
|
||||
const octokit = getOctokit(config.GITHUB_TOKEN);
|
||||
const [createGitHubDeploymentRes, createJobSummaryRes] =
|
||||
await Promise.allSettled([
|
||||
createGitHubDeployment({
|
||||
config,
|
||||
octokit,
|
||||
deploymentUrl: pagesArtifactFields.url,
|
||||
productionBranch: pagesArtifactFields.production_branch,
|
||||
environment: pagesArtifactFields.environment,
|
||||
deploymentId: pagesArtifactFields.deployment_id,
|
||||
projectName: pagesArtifactFields.pages_project,
|
||||
}),
|
||||
createJobSummary({
|
||||
commitHash:
|
||||
pagesArtifactFields.deployment_trigger.metadata.commit_hash.substring(
|
||||
0,
|
||||
8,
|
||||
),
|
||||
deploymentUrl: pagesArtifactFields.url,
|
||||
aliasUrl: pagesArtifactFields.alias,
|
||||
}),
|
||||
]);
|
||||
|
||||
if (createGitHubDeploymentRes.status === "rejected") {
|
||||
warn(config, "Creating Github Deployment failed");
|
||||
}
|
||||
|
||||
if (createJobSummaryRes.status === "rejected") {
|
||||
warn(config, "Creating Github Job summary failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@ export default {
|
||||
return new Response("OK");
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
return Response.json({
|
||||
...request,
|
||||
headers: Object.fromEntries(request.headers),
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
name = "wrangler-action-test-build-quiet"
|
||||
main = "./index.ts"
|
||||
compatibility_date = "2023-07-07"
|
||||
workers_dev = true
|
||||
@@ -17,7 +17,6 @@ export default {
|
||||
return new Response("OK");
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
return Response.json({
|
||||
...request,
|
||||
headers: Object.fromEntries(request.headers),
|
||||
@@ -12,7 +12,6 @@ export default {
|
||||
return new Response(`${SECRET1} ${SECRET2}`);
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
return Response.json({
|
||||
...request,
|
||||
headers: Object.fromEntries(request.headers),
|
||||
Generated
Vendored
@@ -17,7 +17,6 @@ export default {
|
||||
return new Response("OK");
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
return Response.json({
|
||||
...request,
|
||||
headers: Object.fromEntries(request.headers),
|
||||
@@ -17,7 +17,6 @@ export default {
|
||||
return new Response("OK");
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
return Response.json({
|
||||
...request,
|
||||
headers: Object.fromEntries(request.headers),
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "wrangler-action-test",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "wrangler-action-test"
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "wrangler-action-test",
|
||||
"license": "MIT",
|
||||
"private": true
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
name = "wrangler-action-test"
|
||||
name = "wrangler-action-test-only-build"
|
||||
main = "./index.ts"
|
||||
compatibility_date = "2023-07-07"
|
||||
workers_dev = true
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
type Env = {
|
||||
SECRET1?: string;
|
||||
SECRET2?: string;
|
||||
};
|
||||
|
||||
export default {
|
||||
fetch(request: Request, env: Env) {
|
||||
const url = new URL(request.url);
|
||||
|
||||
if (url.pathname === "/secret-health-check") {
|
||||
const { SECRET1, SECRET2 } = env;
|
||||
|
||||
if (SECRET1 !== "SECRET_1_VALUE" || SECRET2 !== "SECRET_2_VALUE") {
|
||||
throw new Error("SECRET1 or SECRET2 is not defined");
|
||||
}
|
||||
|
||||
return new Response("OK");
|
||||
}
|
||||
|
||||
return Response.json({
|
||||
...request,
|
||||
headers: Object.fromEntries(request.headers),
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
type Env = {
|
||||
SECRET1?: string;
|
||||
SECRET2?: string;
|
||||
};
|
||||
|
||||
export default {
|
||||
fetch(request: Request, env: Env) {
|
||||
const url = new URL(request.url);
|
||||
|
||||
if (url.pathname === "/secret-health-check") {
|
||||
const { SECRET1, SECRET2 } = env;
|
||||
|
||||
if (SECRET1 !== "SECRET_1_VALUE" || SECRET2 !== "SECRET_2_VALUE") {
|
||||
throw new Error("SECRET1 or SECRET2 is not defined");
|
||||
}
|
||||
|
||||
return new Response("OK");
|
||||
}
|
||||
|
||||
return Response.json({
|
||||
...request,
|
||||
headers: Object.fromEntries(request.headers),
|
||||
});
|
||||
},
|
||||
};
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const args = Array.from(process.argv);
|
||||
const command = args.pop();
|
||||
switch (command) {
|
||||
case "--version":
|
||||
console.log(`
|
||||
⛅️ wrangler 1.1.1 (update available 1.2.3)
|
||||
------------------------------------------`);
|
||||
process.exit(0);
|
||||
case "action-test":
|
||||
console.log("Test successful.");
|
||||
process.exit(0);
|
||||
default:
|
||||
console.error("Invalid command");
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "wrangler",
|
||||
"version": "1.1.1",
|
||||
"main": "index.js",
|
||||
"bin": "index.js"
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "wrangler-action-pre-installed-wrangler-test",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "wrangler-action-pre-installed-wrangler-test",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"wrangler": "file:mock_packages/wrangler"
|
||||
}
|
||||
},
|
||||
"mock_packages/wrangler": {
|
||||
"version": "1.1.1",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"wrangler": "index.js"
|
||||
}
|
||||
},
|
||||
"node_modules/wrangler": {
|
||||
"resolved": "mock_packages/wrangler",
|
||||
"link": true
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"wrangler": {
|
||||
"version": "file:mock_packages/wrangler"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "wrangler-action-pre-installed-wrangler-test",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"wrangler": "file:mock_packages/wrangler"
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
type Env = {
|
||||
SECRET1?: string;
|
||||
SECRET2?: string;
|
||||
};
|
||||
|
||||
export default {
|
||||
fetch(request: Request, env: Env) {
|
||||
const url = new URL(request.url);
|
||||
|
||||
if (url.pathname === "/secret-health-check") {
|
||||
const { SECRET1, SECRET2 } = env;
|
||||
|
||||
if (SECRET1 !== "SECRET_1_VALUE" || SECRET2 !== "SECRET_2_VALUE") {
|
||||
throw new Error("SECRET1 or SECRET2 is not defined");
|
||||
}
|
||||
|
||||
return new Response("OK");
|
||||
}
|
||||
|
||||
return Response.json({
|
||||
...request,
|
||||
headers: Object.fromEntries(request.headers),
|
||||
});
|
||||
},
|
||||
};
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "wrangler-action-test",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "wrangler-action-test"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "wrangler-action-test",
|
||||
"license": "MIT",
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
name = "wrangler-action-test-secrets-default"
|
||||
main = "./index.ts"
|
||||
compatibility_date = "2023-07-07"
|
||||
workers_dev = true
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
type Env = {
|
||||
SECRET1?: string;
|
||||
SECRET2?: string;
|
||||
};
|
||||
|
||||
export default {
|
||||
fetch(request: Request, env: Env) {
|
||||
const url = new URL(request.url);
|
||||
|
||||
if (url.pathname === "/secret-health-check") {
|
||||
const { SECRET1, SECRET2 } = env;
|
||||
|
||||
if (SECRET1 !== "SECRET_1_VALUE" || SECRET2 !== "SECRET_2_VALUE") {
|
||||
throw new Error("SECRET1 or SECRET2 is not defined");
|
||||
}
|
||||
|
||||
return new Response("OK");
|
||||
}
|
||||
|
||||
return Response.json({
|
||||
...request,
|
||||
headers: Object.fromEntries(request.headers),
|
||||
});
|
||||
},
|
||||
};
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "wrangler-action-test",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "wrangler-action-test"
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "wrangler-action-test",
|
||||
"license": "MIT",
|
||||
"private": true
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
name = "wrangler-action-test"
|
||||
name = "wrangler-action-test-secrets-v2"
|
||||
main = "./index.ts"
|
||||
compatibility_date = "2023-07-07"
|
||||
workers_dev = true
|
||||
@@ -0,0 +1,4 @@
|
||||
name = "wrangler-action-test-specify-package-manager"
|
||||
main = "./index.ts"
|
||||
compatibility_date = "2023-07-07"
|
||||
workers_dev = true
|
||||
@@ -0,0 +1 @@
|
||||
export default {};
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "wrangler-action-detect-package-manager-test",
|
||||
"license": "MIT",
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
name = "wrangler-action-test-unspecified-package-manager"
|
||||
main = "./index.ts"
|
||||
compatibility_date = "2023-07-07"
|
||||
workers_dev = true
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
type Env = {
|
||||
SECRET1?: string;
|
||||
SECRET2?: string;
|
||||
};
|
||||
|
||||
export default {
|
||||
fetch(request: Request, env: Env) {
|
||||
const url = new URL(request.url);
|
||||
|
||||
if (url.pathname === "/secret-health-check") {
|
||||
const { SECRET1, SECRET2 } = env;
|
||||
|
||||
if (SECRET1 !== "SECRET_1_VALUE" || SECRET2 !== "SECRET_2_VALUE") {
|
||||
throw new Error("SECRET1 or SECRET2 is not defined");
|
||||
}
|
||||
|
||||
return new Response("OK");
|
||||
}
|
||||
|
||||
return Response.json({
|
||||
...request,
|
||||
headers: Object.fromEntries(request.headers),
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
import { http, HttpResponse } from "msw";
|
||||
import { z } from "zod";
|
||||
|
||||
export function mockGithubDeployments({
|
||||
githubUser,
|
||||
githubRepoName,
|
||||
}: {
|
||||
githubUser: string;
|
||||
githubRepoName: string;
|
||||
}) {
|
||||
return {
|
||||
handlers: [
|
||||
http.post(
|
||||
`https://api.github.com/repos/${githubUser}/${githubRepoName}/deployments`,
|
||||
async ({ request }) => {
|
||||
if (request.headers.get("Authorization") === null) {
|
||||
return HttpResponse.text("error: no auth token", { status: 400 });
|
||||
}
|
||||
const GithubDeploymentsRequest = z.object({
|
||||
auto_merge: z.literal(false),
|
||||
description: z.literal("Cloudflare Pages"),
|
||||
required_contexts: z.array(z.string()).length(0),
|
||||
environment: z.literal("production"),
|
||||
production_environment: z.literal(false),
|
||||
});
|
||||
// validate request body
|
||||
GithubDeploymentsRequest.parse(await request.json());
|
||||
|
||||
return HttpResponse.json(null);
|
||||
},
|
||||
),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { WranglerActionConfig } from "../wranglerAction";
|
||||
|
||||
export function getTestConfig({
|
||||
config = {},
|
||||
}: {
|
||||
config?: Partial<WranglerActionConfig>;
|
||||
} = {}): WranglerActionConfig {
|
||||
return Object.assign(
|
||||
{
|
||||
WRANGLER_VERSION: "4.72.0",
|
||||
didUserProvideWranglerVersion: false,
|
||||
secrets: [],
|
||||
workingDirectory: "/src/test/fixtures",
|
||||
CLOUDFLARE_API_TOKEN: "foo",
|
||||
CLOUDFLARE_ACCOUNT_ID: "bar",
|
||||
ENVIRONMENT: "dev",
|
||||
VARS: [],
|
||||
COMMANDS: [],
|
||||
QUIET_MODE: false,
|
||||
PACKAGE_MANAGER: "npm",
|
||||
WRANGLER_OUTPUT_DIR: "/tmp/wranglerArtifacts",
|
||||
GITHUB_TOKEN: "xxxxyy23213123132131",
|
||||
} as const satisfies WranglerActionConfig,
|
||||
config,
|
||||
);
|
||||
}
|
||||
@@ -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],
|
||||
])(
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { existsSync } from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import semverGt from "semver/functions/gt";
|
||||
import {
|
||||
info as originalInfo,
|
||||
error as originalError,
|
||||
warning as originalWarn,
|
||||
} from "@actions/core";
|
||||
import { WranglerActionConfig } from "./wranglerAction";
|
||||
|
||||
/**
|
||||
* A helper function to compare two semver versions. If the second arg is greater than the first arg, it returns true.
|
||||
@@ -19,3 +25,33 @@ export function checkWorkingDirectory(workingDirectory = ".") {
|
||||
throw new Error(`Directory ${workingDirectory} does not exist.`);
|
||||
}
|
||||
}
|
||||
|
||||
export function info(
|
||||
config: WranglerActionConfig,
|
||||
message: string,
|
||||
bypass?: boolean,
|
||||
): void {
|
||||
if (!config.QUIET_MODE || bypass) {
|
||||
originalInfo(message);
|
||||
}
|
||||
}
|
||||
|
||||
export function warn(
|
||||
config: WranglerActionConfig,
|
||||
message: string,
|
||||
bypass?: boolean,
|
||||
): void {
|
||||
if (!config.QUIET_MODE || bypass) {
|
||||
originalWarn(message);
|
||||
}
|
||||
}
|
||||
|
||||
export function error(
|
||||
config: WranglerActionConfig,
|
||||
message: string,
|
||||
bypass?: boolean,
|
||||
): void {
|
||||
if (!config.QUIET_MODE || bypass) {
|
||||
originalError(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,512 @@
|
||||
import * as core from "@actions/core";
|
||||
import * as exec from "@actions/exec";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
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",
|
||||
exec: "npx",
|
||||
execNoInstall: "npx --no-install",
|
||||
};
|
||||
|
||||
it("Errors on unsupported wrangler version", async () => {
|
||||
const testConfig = getTestConfig({ config: { WRANGLER_VERSION: "1" } });
|
||||
await expect(
|
||||
installWrangler(testConfig, testPackageManager),
|
||||
).rejects.toThrowError(
|
||||
`Wrangler v1 is no longer supported by this action. Please use major version 2 or greater`,
|
||||
);
|
||||
});
|
||||
|
||||
it("Does nothing if no wrangler version is specified and wrangler is already installed", async () => {
|
||||
const testConfig = getTestConfig();
|
||||
vi.spyOn(exec, "getExecOutput").mockImplementation(async () => {
|
||||
return {
|
||||
exitCode: 0,
|
||||
stderr: "",
|
||||
stdout: ` ⛅️ wrangler 3.48.0 (update available 3.53.1)`,
|
||||
};
|
||||
});
|
||||
const infoSpy = vi.spyOn(core, "info");
|
||||
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 () => {
|
||||
const testConfig = getTestConfig({
|
||||
config: {
|
||||
WRANGLER_VERSION: "3.48.0",
|
||||
didUserProvideWranglerVersion: true,
|
||||
},
|
||||
});
|
||||
vi.spyOn(exec, "getExecOutput").mockImplementation(async () => {
|
||||
return {
|
||||
exitCode: 0,
|
||||
stderr: "",
|
||||
stdout: ` ⛅️ wrangler 3.48.0 (update available 3.53.1)`,
|
||||
};
|
||||
});
|
||||
const infoSpy = vi.spyOn(core, "info");
|
||||
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: {
|
||||
WRANGLER_VERSION: "3.48.0",
|
||||
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.48.0`,
|
||||
};
|
||||
});
|
||||
vi.spyOn(exec, "exec").mockImplementation(async () => {
|
||||
return 0;
|
||||
});
|
||||
const infoSpy = vi.spyOn(core, "info");
|
||||
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();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,420 @@
|
||||
import {
|
||||
debug,
|
||||
getMultilineInput,
|
||||
endGroup as originalEndGroup,
|
||||
startGroup as originalStartGroup,
|
||||
setFailed,
|
||||
setOutput,
|
||||
} from "@actions/core";
|
||||
import { getExecOutput } from "@actions/exec";
|
||||
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({
|
||||
WRANGLER_VERSION: z.string(),
|
||||
didUserProvideWranglerVersion: z.boolean(),
|
||||
secrets: z.array(z.string()),
|
||||
workingDirectory: z.string(),
|
||||
CLOUDFLARE_API_TOKEN: z.string(),
|
||||
CLOUDFLARE_ACCOUNT_ID: z.string(),
|
||||
ENVIRONMENT: z.string(),
|
||||
VARS: z.array(z.string()),
|
||||
COMMANDS: z.array(z.string()),
|
||||
QUIET_MODE: z.boolean(),
|
||||
PACKAGE_MANAGER: z.string(),
|
||||
WRANGLER_OUTPUT_DIR: z.string(),
|
||||
GITHUB_TOKEN: z.string(),
|
||||
});
|
||||
|
||||
function startGroup(config: WranglerActionConfig, name: string): void {
|
||||
if (!config.QUIET_MODE) {
|
||||
originalStartGroup(name);
|
||||
}
|
||||
}
|
||||
|
||||
function endGroup(config: WranglerActionConfig): void {
|
||||
if (!config.QUIET_MODE) {
|
||||
originalEndGroup();
|
||||
}
|
||||
}
|
||||
|
||||
async function main(
|
||||
config: WranglerActionConfig,
|
||||
packageManager: PackageManager,
|
||||
) {
|
||||
try {
|
||||
wranglerActionConfig.parse(config);
|
||||
authenticationSetup(config);
|
||||
const resolvedVersion = await installWrangler(config, packageManager);
|
||||
const resolvedConfig = { ...config, WRANGLER_VERSION: resolvedVersion };
|
||||
|
||||
await execCommands(
|
||||
resolvedConfig,
|
||||
packageManager,
|
||||
getMultilineInput("preCommands"),
|
||||
"pre",
|
||||
);
|
||||
await uploadSecrets(resolvedConfig, packageManager);
|
||||
await wranglerCommands(resolvedConfig, packageManager);
|
||||
await execCommands(
|
||||
resolvedConfig,
|
||||
packageManager,
|
||||
getMultilineInput("postCommands"),
|
||||
"post",
|
||||
);
|
||||
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`,
|
||||
);
|
||||
}
|
||||
|
||||
startGroup(config, "🔍 Checking for existing Wrangler installation");
|
||||
let installedVersion = "";
|
||||
let versionSatisfied = false;
|
||||
try {
|
||||
installedVersion = await resolveInstalledVersion(config, packageManager);
|
||||
|
||||
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(
|
||||
config,
|
||||
`✅ No wrangler version specified, using pre-installed wrangler version ${installedVersion}`,
|
||||
true,
|
||||
);
|
||||
endGroup(config);
|
||||
return installedVersion;
|
||||
}
|
||||
if (config.didUserProvideWranglerVersion && versionSatisfied) {
|
||||
info(config, `✅ Using Wrangler ${installedVersion}`, true);
|
||||
endGroup(config);
|
||||
return installedVersion;
|
||||
}
|
||||
info(
|
||||
config,
|
||||
"⚠️ Wrangler not found or version is incompatible. Installing...",
|
||||
true,
|
||||
);
|
||||
} catch (error) {
|
||||
debug(`Error checking Wrangler version: ${error}`);
|
||||
info(
|
||||
config,
|
||||
"⚠️ Wrangler not found or version is incompatible. Installing...",
|
||||
true,
|
||||
);
|
||||
} finally {
|
||||
endGroup(config);
|
||||
}
|
||||
|
||||
startGroup(config, "📥 Installing Wrangler");
|
||||
try {
|
||||
await exec(
|
||||
packageManager.install,
|
||||
[`wrangler@${config["WRANGLER_VERSION"]}`],
|
||||
{
|
||||
cwd: config["workingDirectory"],
|
||||
silent: config["QUIET_MODE"],
|
||||
},
|
||||
);
|
||||
|
||||
info(config, `✅ Wrangler installed`, true);
|
||||
} 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) {
|
||||
process.env.CLOUDFLARE_API_TOKEN = config["CLOUDFLARE_API_TOKEN"];
|
||||
process.env.CLOUDFLARE_ACCOUNT_ID = config["CLOUDFLARE_ACCOUNT_ID"];
|
||||
}
|
||||
|
||||
async function execCommands(
|
||||
config: WranglerActionConfig,
|
||||
packageManager: PackageManager,
|
||||
commands: string[],
|
||||
cmdType: string,
|
||||
) {
|
||||
if (!commands.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
startGroup(config, `🚀 Running ${cmdType}Commands`);
|
||||
try {
|
||||
for (const command of commands) {
|
||||
const cmd = command.startsWith("wrangler")
|
||||
? `${packageManager.exec} ${command}`
|
||||
: command;
|
||||
|
||||
await execShell(cmd, {
|
||||
cwd: config["workingDirectory"],
|
||||
silent: config["QUIET_MODE"],
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
endGroup(config);
|
||||
}
|
||||
}
|
||||
|
||||
function getSecret(secret: string) {
|
||||
if (!secret) {
|
||||
throw new Error("Secret name cannot be blank.");
|
||||
}
|
||||
|
||||
const value = process.env[secret];
|
||||
if (!value) {
|
||||
throw new Error(`Value for secret ${secret} not found in environment.`);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function getEnvVar(envVar: string) {
|
||||
if (!envVar) {
|
||||
throw new Error("Var name cannot be blank.");
|
||||
}
|
||||
|
||||
const value = process.env[envVar];
|
||||
if (!value) {
|
||||
throw new Error(`Value for var ${envVar} not found in environment.`);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
async function legacyUploadSecrets(
|
||||
config: WranglerActionConfig,
|
||||
packageManager: PackageManager,
|
||||
secrets: string[],
|
||||
environment?: string,
|
||||
workingDirectory?: string,
|
||||
) {
|
||||
for (const secret of secrets) {
|
||||
const args = ["wrangler", "secret", "put", secret];
|
||||
if (environment) {
|
||||
args.push("--env", environment);
|
||||
}
|
||||
await exec(packageManager.exec, args, {
|
||||
cwd: workingDirectory,
|
||||
silent: config["QUIET_MODE"],
|
||||
input: Buffer.from(getSecret(secret)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function uploadSecrets(
|
||||
config: WranglerActionConfig,
|
||||
packageManager: PackageManager,
|
||||
) {
|
||||
const secrets: string[] = config["secrets"];
|
||||
const environment = config["ENVIRONMENT"];
|
||||
const workingDirectory = config["workingDirectory"];
|
||||
|
||||
if (!secrets.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
startGroup(config, "🔑 Uploading secrets...");
|
||||
|
||||
try {
|
||||
if (semverCompare(config["WRANGLER_VERSION"], "3.4.0")) {
|
||||
return legacyUploadSecrets(
|
||||
config,
|
||||
packageManager,
|
||||
secrets,
|
||||
environment,
|
||||
workingDirectory,
|
||||
);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
await exec(packageManager.exec, args, {
|
||||
cwd: workingDirectory,
|
||||
silent: config["QUIET_MODE"],
|
||||
input: Buffer.from(
|
||||
JSON.stringify(
|
||||
Object.fromEntries(
|
||||
secrets.map((secret) => [secret, getSecret(secret)]),
|
||||
),
|
||||
),
|
||||
),
|
||||
});
|
||||
} catch (err: unknown) {
|
||||
if (err instanceof Error) {
|
||||
error(config, err.message);
|
||||
err.stack && debug(err.stack);
|
||||
}
|
||||
throw new Error(`Failed to upload secrets.`);
|
||||
} finally {
|
||||
endGroup(config);
|
||||
}
|
||||
}
|
||||
|
||||
async function wranglerCommands(
|
||||
config: WranglerActionConfig,
|
||||
packageManager: PackageManager,
|
||||
) {
|
||||
startGroup(config, "🚀 Running Wrangler Commands");
|
||||
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);
|
||||
}
|
||||
|
||||
for (let command of commands) {
|
||||
const args = [];
|
||||
|
||||
if (environment && !command.includes("--env")) {
|
||||
args.push("--env", environment);
|
||||
}
|
||||
|
||||
if (
|
||||
config["VARS"].length &&
|
||||
(command.startsWith("deploy") || command.startsWith("publish")) &&
|
||||
!command.includes("--var")
|
||||
) {
|
||||
args.push("--var");
|
||||
for (const v of config["VARS"]) {
|
||||
args.push(`${v}:${getEnvVar(v)}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Used for saving the wrangler output
|
||||
let stdOut = "";
|
||||
let stdErr = "";
|
||||
|
||||
// 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"],
|
||||
listeners: {
|
||||
stdout: (data: Buffer) => {
|
||||
stdOut += data.toString();
|
||||
},
|
||||
stderr: (data: Buffer) => {
|
||||
stdErr += data.toString();
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Execute the wrangler command
|
||||
await exec(`${packageManager.exec} wrangler ${command}`, args, options);
|
||||
|
||||
// Set the outputs for the command
|
||||
setOutput("command-output", stdOut);
|
||||
setOutput("command-stderr", stdErr);
|
||||
|
||||
// Handles setting github action outputs and creating github deployment and job summary
|
||||
await handleCommandOutputParsing(config, command, stdOut);
|
||||
}
|
||||
} finally {
|
||||
endGroup(config);
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
authenticationSetup,
|
||||
execCommands,
|
||||
info,
|
||||
installWrangler,
|
||||
isExactSemver,
|
||||
main,
|
||||
parseWranglerVersion,
|
||||
uploadSecrets,
|
||||
wranglerCommands,
|
||||
};
|
||||
@@ -0,0 +1,183 @@
|
||||
import mockfs from "mock-fs";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
getOutputEntry,
|
||||
getWranglerArtifacts,
|
||||
} from "./wranglerArtifactManager";
|
||||
|
||||
afterEach(() => {
|
||||
mockfs.restore();
|
||||
});
|
||||
describe("wranglerArtifactsManager", () => {
|
||||
describe("getWranglerArtifacts()", async () => {
|
||||
it("Returns only wrangler output files from a given directory", async () => {
|
||||
mockfs({
|
||||
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 () => {
|
||||
mockfs({
|
||||
notTheDirWeWant: {},
|
||||
});
|
||||
|
||||
const artifacts = await getWranglerArtifacts("./testOutputDir");
|
||||
expect(artifacts).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getOutputEntry()", async () => {
|
||||
describe("OutputEntryPagesDeployment", async () => {
|
||||
it("Returns only detailed pages deploy output from wrangler artifacts", async () => {
|
||||
mockfs({
|
||||
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 artifact = await getOutputEntry("./testOutputDir");
|
||||
if (artifact?.type !== "pages-deploy-detailed") {
|
||||
throw new Error(`Unexpected type ${artifact?.type}`);
|
||||
}
|
||||
|
||||
expect(artifact).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 () => {
|
||||
mockfs({
|
||||
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 artifact = await getOutputEntry("./testOutputDir");
|
||||
if (artifact?.type !== "pages-deploy-detailed") {
|
||||
throw new Error(`Unexpected type ${artifact?.type}`);
|
||||
}
|
||||
|
||||
expect(artifact).toEqual({
|
||||
version: 1,
|
||||
type: "pages-deploy-detailed",
|
||||
pages_project: "project",
|
||||
url: "url.com",
|
||||
environment: "production",
|
||||
deployment_id: "123",
|
||||
alias: "test.com",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("OutputEntryDeployment", async () => {
|
||||
it("Returns only wrangler deploy output from wrangler artifacts", async () => {
|
||||
mockfs({
|
||||
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":"deploy", "targets": ["https://example.com"]}`,
|
||||
"not-wrangler-output.json": "test",
|
||||
},
|
||||
});
|
||||
|
||||
const artifact = await getOutputEntry("./testOutputDir");
|
||||
if (artifact?.type !== "deploy") {
|
||||
throw new Error(`Unexpected type ${artifact?.type}`);
|
||||
}
|
||||
|
||||
expect(artifact).toEqual({
|
||||
version: 1,
|
||||
type: "deploy",
|
||||
targets: ["https://example.com"],
|
||||
});
|
||||
}),
|
||||
it("Skips artifact entries that are not parseable", async () => {
|
||||
mockfs({
|
||||
testOutputDir: {
|
||||
"wrangler-output-2024-10-17_18-48-40_463-2e6e83.json": `
|
||||
this line is invalid json.
|
||||
{"version": 1, "type":"deploy", "targets": ["https://example.com"]}`,
|
||||
"not-wrangler-output.json": "test",
|
||||
},
|
||||
});
|
||||
|
||||
const artifact = await getOutputEntry("./testOutputDir");
|
||||
if (artifact?.type !== "deploy") {
|
||||
throw new Error(`Unexpected type ${artifact?.type}`);
|
||||
}
|
||||
|
||||
expect(artifact).toEqual({
|
||||
version: 1,
|
||||
type: "deploy",
|
||||
targets: ["https://example.com"],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("OutputEntryVersionUpload", async () => {
|
||||
it("Returns only version upload output from wrangler artifacts", async () => {
|
||||
mockfs({
|
||||
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":"version-upload", "preview_url": "https://example.com"}`,
|
||||
"not-wrangler-output.json": "test",
|
||||
},
|
||||
});
|
||||
|
||||
const artifact = await getOutputEntry("./testOutputDir");
|
||||
if (artifact?.type !== "version-upload") {
|
||||
throw new Error(`Unexpected type ${artifact?.type}`);
|
||||
}
|
||||
|
||||
expect(artifact).toEqual({
|
||||
version: 1,
|
||||
type: "version-upload",
|
||||
preview_url: "https://example.com",
|
||||
});
|
||||
}),
|
||||
it("Skips artifact entries that are not parseable", async () => {
|
||||
mockfs({
|
||||
testOutputDir: {
|
||||
"wrangler-output-2024-10-17_18-48-40_463-2e6e83.json": `
|
||||
this line is invalid json.
|
||||
{"version": 1, "type":"version-upload", "preview_url": "https://example.com"}`,
|
||||
"not-wrangler-output.json": "test",
|
||||
},
|
||||
});
|
||||
|
||||
const artifact = await getOutputEntry("./testOutputDir");
|
||||
if (artifact?.type !== "version-upload") {
|
||||
throw new Error(`Unexpected type ${artifact?.type}`);
|
||||
}
|
||||
|
||||
expect(artifact).toEqual({
|
||||
version: 1,
|
||||
type: "version-upload",
|
||||
preview_url: "https://example.com",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,122 @@
|
||||
import { access, open, readdir } from "fs/promises";
|
||||
import { z } from "zod";
|
||||
|
||||
const OutputEntryBase = z.object({
|
||||
version: z.literal(1),
|
||||
type: z.string(),
|
||||
});
|
||||
|
||||
export type OutputEntryPagesDeployment = z.infer<
|
||||
typeof OutputEntryPagesDeployment
|
||||
>;
|
||||
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"]),
|
||||
// optional, added in wrangler@3.89.0
|
||||
production_branch: z.string().optional(),
|
||||
// optional, added in wrangler@3.89.0
|
||||
deployment_trigger: z
|
||||
.object({
|
||||
metadata: z.object({
|
||||
/** Commit hash of the deployment trigger metadata for the pages project */
|
||||
commit_hash: z.string(),
|
||||
}),
|
||||
})
|
||||
.optional(),
|
||||
}),
|
||||
);
|
||||
|
||||
export type OutputEntryDeployment = z.infer<typeof OutputEntryDeployment>;
|
||||
const OutputEntryDeployment = OutputEntryBase.merge(
|
||||
z.object({
|
||||
type: z.literal("deploy"),
|
||||
/** A list of URLs that represent the HTTP triggers associated with this deployment */
|
||||
/** basically, for wrangler-action purposes this is the deployment urls */
|
||||
targets: z.array(z.string()).optional(),
|
||||
}),
|
||||
);
|
||||
|
||||
export type OutputEntryVersionUpload = z.infer<typeof OutputEntryVersionUpload>;
|
||||
const OutputEntryVersionUpload = OutputEntryBase.merge(
|
||||
z.object({
|
||||
type: z.literal("version-upload"),
|
||||
/** The preview URL associated with this version upload */
|
||||
preview_url: z.string().optional(),
|
||||
}),
|
||||
);
|
||||
|
||||
export type SupportedOutputEntry = z.infer<typeof SupportedOutputEntry>;
|
||||
const SupportedOutputEntry = z.discriminatedUnion("type", [
|
||||
OutputEntryPagesDeployment,
|
||||
OutputEntryDeployment,
|
||||
OutputEntryVersionUpload,
|
||||
]);
|
||||
|
||||
/**
|
||||
* 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 a supported wrangler OutputEntry
|
||||
*
|
||||
* @param artifactDirectory
|
||||
* @returns The first SupportedOutputEntry found within a wrangler artifact directory
|
||||
*/
|
||||
export async function getOutputEntry(
|
||||
artifactDirectory: string,
|
||||
): Promise<SupportedOutputEntry | null> {
|
||||
const artifactFilePaths = await getWranglerArtifacts(artifactDirectory);
|
||||
|
||||
for (const filePath of artifactFilePaths) {
|
||||
const file = await open(filePath, "r");
|
||||
try {
|
||||
for await (const line of file.readLines()) {
|
||||
try {
|
||||
// Attempt to parse and validate the JSON line against the union schema.
|
||||
// Assume, in the context of the action, the first OutputEntry seen will suffice
|
||||
return SupportedOutputEntry.parse(JSON.parse(line));
|
||||
} catch {
|
||||
// Skip lines that are invalid JSON or don't match any schema.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
await file.close();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
type Env = {
|
||||
SECRET1?: string;
|
||||
SECRET2?: string;
|
||||
};
|
||||
|
||||
export default {
|
||||
fetch(request: Request, env: Env) {
|
||||
const url = new URL(request.url);
|
||||
|
||||
if (url.pathname === "/secret-health-check") {
|
||||
const { SECRET1, SECRET2 } = env;
|
||||
|
||||
if (SECRET1 !== "SECRET_1_VALUE" || SECRET2 !== "SECRET_2_VALUE") {
|
||||
throw new Error("SECRET1 or SECRET2 is not defined");
|
||||
}
|
||||
|
||||
return new Response("OK");
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
return Response.json({
|
||||
...request,
|
||||
headers: Object.fromEntries(request.headers),
|
||||
});
|
||||
},
|
||||
};
|
||||
+1
-1
@@ -15,6 +15,6 @@
|
||||
"lib": ["ESNext"],
|
||||
"types": ["node", "@cloudflare/workers-types"]
|
||||
},
|
||||
"exclude": ["node_modules", "**/*.test.ts"],
|
||||
"exclude": ["node_modules"],
|
||||
"include": ["src"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user