Compare commits

...

316 Commits

Author SHA1 Message Date
Eric Clemmons a61fbea322 Merge pull request #429 from cloudflare/changeset-release/main
Version Packages
2026-05-12 15:34:29 -05:00
github-actions[bot] e804ea3dfd Version Packages 2026-05-12 18:20:02 +00:00
Eric Clemmons 8d0324a88e fix: update release workflow to v5 actions and regenerate lockfile 2026-05-12 13:19:37 -05:00
Eric Clemmons 2f18b18a46 Merge pull request #431 from cloudflare/fix/semgrep-blocking-findings
fix: address Semgrep blocking findings
2026-05-12 13:10:33 -05:00
Eric Clemmons 622ff0d765 fix: upgrade checkout and setup-node to v5 for Node 24 runtime 2026-05-12 13:02:33 -05:00
Eric Clemmons f501f05fbd fix: force GitHub actions to run on Node 24 via env var 2026-05-12 13:01:47 -05:00
Eric Clemmons f990691b6b fix: resolve npm audit vulnerabilities via undici override and vitest v3 2026-05-12 11:58:45 -05:00
Eric Clemmons 652762d3f6 fix: migrate action runtime from node20 to node24 2026-05-12 11:43:52 -05:00
Eric Clemmons bd3f4f0b30 fix: add retry support to worker health check using better-result
Replace manual fetch() with Result.tryPromise from better-result,
which provides built-in retry with exponential backoff. This handles
the transient 404s that occur immediately after wrangler deploy due
to edge propagation delays.

- Rename workerHealthCheck.cjs -> .mjs for ESM support
- Add better-result as devDependency
- Update deploy.yml to invoke .mjs file
- Retry up to 5 times with 2s exponential backoff
2026-05-11 16:35:22 -05:00
Eric Clemmons 38dbf5b59f Merge pull request #428 from cloudflare/hrushikesh/add-semgrep-oss-workflow
ci: add Semgrep OSS scanning workflow
2026-05-11 15:04:44 -05:00
Eric Clemmons cdf65921e0 ci: pin pnpm to v10 to fix build script failures 2026-05-11 14:55:57 -05:00
Eric Clemmons bfdfae5e0f fix: address Semgrep blocking findings
- Replace execSync(`curl`) with native fetch() in workerHealthCheck.cjs
  to resolve javascript.lang.security.detect-child-process finding.
- Run npm audit fix to update rollup 4.34.4 -> 4.60.3 and vite 5.4.14 -> 5.4.21,
  resolving CVE-2026-27606 and other supply chain vulnerabilities.
2026-05-11 14:51:16 -05:00
Eric Clemmons a3edea85f7 style: fix prettier formatting in semgrep.yml 2026-05-11 14:10:07 -05:00
Eric Clemmons b6290ea70c Merge pull request #412 from cloudflare/BANDA-1664/wrangler-v4-support
Update default Wrangler version to v4.x
2026-05-11 13:55:58 -05:00
Eric Clemmons 65553b39e6 Clarify v4@latest is installed, not 4.72.0
Co-authored-by: Eric Clemmons <eric@smarterspam.com>
2026-04-26 13:56:44 -05:00
Hrushikesh Deshpande 4f154b96e0 ci: add Semgrep OSS scanning workflow 2026-04-23 18:36:26 -04:00
Somhairle MacLeòid 5f269ee58e Merge pull request #424 from ulgens/update-docs-checkout-action
Update actions/checkout in example code blocks
2026-04-17 12:00:59 +01:00
Eric Clemmons d181764e4b Merge pull request #427 from cloudflare/changeset-release/main
Version Packages
2026-04-15 15:48:16 -05:00
Eric Clemmons 3c924dd85f Default to 4, not 4.72.0 2026-04-15 15:39:44 -05:00
Eric Clemmons c1f90e624b major, not minor, version bump 2026-04-15 15:39:04 -05:00
github-actions[bot] 0501554080 Version Packages 2026-04-15 19:40:15 +00:00
Eric Clemmons d8f3eaa359 Merge pull request #426 from cloudflare/willtaylor/escalation-963-support-version-ranges
Support version ranges and tags in wranglerVersion input
2026-04-15 14:39:45 -05:00
Will Taylor febbda69f8 Support version ranges and tags in wranglerVersion input
The wranglerVersion input only accepted exact versions like '4.81.0'.
Setting it to a major version like '4', a range like '^4.0.0', or a
tag like 'latest' crashed with 'Invalid Version' during secret
uploads. npm installed the right version fine, but subsequent version
comparisons failed because they expected exact X.Y.Z format.

After installing wrangler, the action now runs wrangler --version to
get the concrete installed version and uses that for all version
comparisons. If version detection fails and the user gave an exact
version, the action falls back to using it directly, preserving
existing behavior.

Pre-installed versions that satisfy a range skip reinstallation.

Fixes #390
Relates to #366
Relates to #379
2026-04-15 14:40:29 -04:00
Ülgen Sarıkavak 59b83385d8 Update actions/checkout in example code blocks
Similar to https://github.com/cloudflare/cloudflare-docs/pull/29846
2026-04-14 18:04:30 +03:00
Eric Clemmons dd8ea1b72d Clarify Wrangler v3 Support
Co-authored-by: Ben <4991309+NuroDev@users.noreply.github.com>
2026-03-24 15:36:43 -05:00
Eric Clemmons 1029e90033 Update default Wrangler version to v4 (4.72.0)
The action now defaults to Wrangler v4 instead of v3. All existing
version-gated logic (secret upload, deploy vs publish) correctly
handles v4. Users can pin to v3 via wranglerVersion input.

Closes BANDA-1664
2026-03-12 17:51:09 -05:00
Maximo Guk 707f637509 Merge pull request #359 from cloudflare/changeset-release/main
Version Packages
2025-03-14 21:01:10 -05:00
github-actions[bot] e764ef3355 Version Packages 2025-03-15 01:57:01 +00:00
Maximo Guk 8d761e6bdc Merge pull request #358 from cloudflare/penalosa-patch-2
Support Wrangler v4 bulk secrets
2025-03-14 20:56:46 -05:00
Maximo Guk cd6314a97b Use wranglers secret:bulk on versions of wrangler prior to 3.60.0 2025-03-14 20:27:34 -05:00
Somhairle MacLeòid ef1f9fb2b0 Update wranglerAction.ts 2025-03-15 00:25:40 +00:00
Maximo Guk 08959b2671 Merge pull request #353 from cloudflare/changeset-release/main
Version Packages
2025-02-10 13:02:55 -06:00
github-actions[bot] abb67eda8d Version Packages 2025-02-10 18:57:44 +00:00
Maximo Guk 883eaf2de0 Merge pull request #351 from cloudflare/maximo/use-wrangler-outputs-for-version-upload-and-wrangler-deploy
Use wrangler outputs for wrangler deploy and versions upload
2025-02-10 12:56:18 -06:00
Maximo Guk 4ff07f4310 Use wrangler outputs for version upload and wrangler deploy 2025-02-09 17:10:09 -06:00
Maximo Guk 4fb15f8b7a Bump @changesets/cli to 2.27.12 and vitest to 2.1.9 2025-02-09 16:51:41 -06:00
Maximo Guk e8cd5f0968 Merge pull request #350 from cloudflare/maximo/handle-failures-in-create-github-deployment-and-job-summary
Handle failures in createGitHubDeployment and createGitHubJobSummary
2025-02-09 16:51:09 -06:00
Maximo Guk e209094e62 Handle failures in createGitHubDeployment and createGitHubJobSummary 2025-02-09 16:49:07 -06:00
Maximo Guk 181b9fb5d6 Merge pull request #346 from cloudflare/changeset-release/main
Version Packages
2025-01-17 09:54:16 -06:00
github-actions[bot] aac328d155 Version Packages 2025-01-17 15:47:45 +00:00
Maximo Guk fc89533f1e Merge pull request #345 from cloudflare/fix/pages-gh-deployment
fix: pages github deployment not triggering due to incorrect condition #2
2025-01-17 09:47:27 -06:00
Maximo Guk e819570b2d Remove stages from pages-deploy-detailed artifact and add changeset 2025-01-17 09:31:23 -06:00
Alexandre Pereira 923db6f87f fix: pages github deployment not triggering due to missing pages artifact stages 2025-01-17 11:30:50 +00:00
Courtney Sims d5031c3dbe Merge pull request #340 from armandsalle/patch-1
Update typo in README.md
2024-12-18 16:22:28 -06:00
Armand SALLE 17da943335 Update typo in README.md 2024-12-14 23:12:56 +01:00
Maximo Guk 3aee84cd6d Merge pull request #330 from cloudflare/changeset-release/main
Version Packages
2024-11-26 16:09:40 -04:00
github-actions[bot] 9d9bee8bfd Version Packages 2024-11-26 19:27:54 +00:00
Maximo Guk ee57f258dc Merge pull request #334 from cloudflare/maximo/bump-default-wrangler-version
Bump default wrangler version to 3.90.0
2024-11-26 15:26:40 -04:00
Maximo Guk 9fed19aa4e Bump default wrangler version to 3.90.0 2024-11-26 07:47:24 -06:00
Maximo Guk 3c6e4da7b3 Merge pull request #332 from uFloppyDisk/chore/action-input-fix
chore(action.yml): rename gitHubToken input in action.yml;
2024-11-25 23:09:49 -04:00
Pawel Bartusiak 1c0eef186f chore(action.yml): rename input;
Rename 'githubToken' input to 'gitHubToken' in action.yml to reflect the key used for intake in the script;
2024-11-25 19:00:09 -08:00
Maximo Guk b19342b08c Merge pull request #325 from cloudflare/maximo/add-github-deployments-parity-for-pages-deployments-in-wrangler-action
Add GitHub deployments for parity with pages-action
2024-11-21 14:52:58 -04:00
Jacob Hands d44c80928a Merge pull request #315 from Alexkuva/patch-1
chore: Add environment parameter for worker secret
2024-11-19 14:34:40 -06:00
Maximo Guk cada7a6312 Add tests for github service, and move all github deployments logic to be inside of github.ts 2024-11-19 12:08:36 -06:00
Maximo Guk 1e92eee7d2 Restructure text fixtures directories, add test-utils.ts and add msw dep 2024-11-19 11:38:18 -06:00
Maximo Guk e7e5400334 Remove pages specific outputs from wrangler deploy command 2024-11-19 11:38:18 -06:00
Maximo Guk d58f116d72 Add github deployments to wrangler action for pages parity 2024-11-19 11:38:18 -06:00
Maximo Guk 64b6339110 Update dependencies and add @actions/github 2024-11-19 11:38:17 -06:00
Courtney Sims ac2fd0ec8f Merge pull request #324 from cloudflare/courtney-sims-documentation-update
Update documentation
2024-11-13 16:49:06 -06:00
Courtney Sims 0140ffbc93 Update documentation 2024-11-13 12:06:27 -06:00
Courtney Sims cc4ede3f06 Merge pull request #322 from cloudflare/courtney-sims-testing
Add tests for installWrangler
2024-11-13 09:15:02 -06:00
Courtney Sims 9bf87caf2b Pin node version 2024-11-12 17:25:56 -06:00
Courtney Sims 81f8814855 BANDA-484: Add tests for InstallWrangler 2024-11-12 11:54:43 -06:00
Maximo Guk 6c6d3321ef Merge pull request #320 from cloudflare/changeset-release/main
Version Packages
2024-11-06 11:27:06 -04:00
github-actions[bot] bc61e66e47 Version Packages 2024-11-05 21:26:00 +00:00
Maximo Guk 0e2b86ba21 Merge pull request #319 from cloudflare/maximo/add-changeset-for-wrangler-artifacts-dir
Add missing changeset for using randomUUID for wrangler artifacts dir
2024-11-05 17:25:38 -04:00
Maximo Guk 59c0462940 Add missing changeset for using randomUUID for wrangler artifacts dir 2024-11-05 15:20:12 -06:00
Maximo Guk 4c3f0c27ea Merge pull request #318 from cloudflare/maximo/use-random-uuid-for-wrangler-artifacts-dir
Use random UUID when creating wrangler artifacts output directory
2024-11-05 17:05:58 -04:00
Maximo Guk 4d045615a9 Use random UUID when creating wrangler artifacts output directory 2024-11-05 14:41:27 -06:00
Maximo Guk a7a2388d7b Merge pull request #313 from cloudflare/changeset-release/main
Version Packages
2024-11-04 11:31:39 -04:00
Alex 95555f6ed0 add environment parameter for worker secret
Add environment parameter for worker secret in readme.md

linked issue example: https://github.com/cloudflare/wrangler-action/issues/251
2024-11-02 17:02:18 +01:00
github-actions[bot] 2171bbdd69 Version Packages 2024-11-01 18:40:47 +00:00
Maximo Guk 4f4ff59d17 Merge pull request #312 from cloudflare/maximo/unrevert-add-parity-with-wrangler-action
Unrevert add parity with wrangler action
2024-11-01 15:40:25 -03:00
Maximo Guk 122ee5cf5b Moves wrangler output to tmpdir rather than /opt/ since /opt/ is owned by root.
- Github self hosted runners may not have permissions to write to /opt/

- Also fallsback to trying to extract the deployment-url and deployment-alias-url from stdout when WRANGLER_OUTPUT_DIR is not specified
2024-11-01 13:29:38 -05:00
Maximo Guk 44d79edf44 Reapply "Add parity with pages-action for pages deploy outputs"
This reverts commit 10d5b9c1c1.
2024-10-31 00:47:44 -05:00
Maximo Guk b2be7abbfc Merge pull request #310 from cloudflare/changeset-release/main
Version Packages
2024-10-24 11:56:05 -03:00
github-actions[bot] b7158dc9a4 Version Packages 2024-10-24 14:49:52 +00:00
Maximo Guk 43767e73de Merge pull request #309 from cloudflare/maximo/revert-add-parity-with-pages-action
Revert add parity with pages action
2024-10-24 11:48:43 -03:00
Maximo Guk 10d5b9c1c1 Revert "Add parity with pages-action for pages deploy outputs"
This reverts commit 3ec7f8943e.
2024-10-24 08:45:19 -05:00
Courtney Sims c01035706f Merge pull request #305 from cloudflare/changeset-release/main
Version Packages
2024-10-23 15:53:47 -05:00
github-actions[bot] fa96c39bd4 Version Packages 2024-10-23 19:55:00 +00:00
Courtney Sims 78dad1df07 Merge pull request #303 from cloudflare/courtney-sims-enable-outputs
Add parity with pages-action for Pages deploy outputs
2024-10-23 14:54:26 -05:00
Courtney Sims 3ec7f8943e Add parity with pages-action for pages deploy outputs 2024-10-23 10:18:55 -05:00
Maximo Guk cd8a317efb Merge pull request #299 from cloudflare/changeset-release/main
Version Packages
2024-10-08 13:03:50 -03:00
github-actions[bot] 6aa2e8946a Version Packages 2024-10-07 21:39:16 +00:00
Maximo Guk de0526e09b Merge pull request #278 from acusti/patch-1
When checking for existing wrangler install, match wrangler version even if multiline output
2024-10-07 18:38:52 -03:00
Maximo Guk 84316ca95c Merge pull request #298 from cloudflare/maximo/add-changeset-for-update-wrangler-version
Add changeset for update wrangler version from 3.13.2 to 3.78.10
2024-10-07 18:37:59 -03:00
Maximo Guk 134b5c2a32 Add changeset for update wrangler version from 3.13.2 to 3.78.10 2024-10-07 16:30:14 -05:00
Andrew Patton 47d51f25c1 Match wrangler version even if multiline output cloudflare#277 2024-10-07 20:54:34 +00:00
Somhairle MacLeòid 93fff4f656 Merge pull request #295 from Ankcorn/main 2024-10-04 22:01:44 +01:00
Maximo Guk 66334dff0a Merge pull request #297 from cloudflare/changeset-release/main
Version Packages
2024-10-02 11:46:26 -03:00
github-actions[bot] 9966912c1b Version Packages 2024-10-01 22:12:10 +00:00
Maximo Guk 7b9aec5185 Merge pull request #291 from Ambroos/add-pages-deployment-alias
Add deployment-alias-url for pages deployments with Wrangler 3.78.0+
2024-10-01 19:11:49 -03:00
Ankcorn c0e117f512 WO-202: update wrangler version 2024-09-27 11:42:53 +01:00
Maximo Guk bcff5386ec Merge pull request #294 from Cherry/chore/bun-lockfile-new
chore: add support for new bun.lock
2024-09-26 12:38:45 -03:00
Maximo Guk c15498da36 Merge pull request #293 from cloudflare/hrushikeshdeshpande-updating-semgrep-docker-image
Updating semgrep docker image for the exising semgrep workflow.
2024-09-26 12:32:17 -03:00
James Ross 8bc5c5f050 chore: add support for new bun.lock 2024-09-26 01:02:47 +01:00
Hrushikesh Deshpande ac21b71592 Updating semgrep docker image for the exising semgrep workflow. 2024-09-25 18:32:41 -04:00
Maximo Guk f111269956 Merge pull request #292 from cloudflare/hrushikeshdeshpande-updating-semgrep-yml
Update semgrep.yml
2024-09-21 12:54:17 -05:00
hrushikeshdeshpande 570d521df6 Update semgrep.yml
Updating Semgrep.yml file - Semgrep is a tool that will be used to scan Cloudflare's public repos for Supply chain, code and secrets. This work is part of Application & Product Security team's initiative to onboard Semgrep onto all of Cloudflare's public repos.

In case of any questions, please reach out to "Hrushikesh Deshpande" on cf internal chat.
2024-09-21 12:06:53 -04:00
Hrushikesh Deshpande 174337b3b4 Adding semgrep yaml file 2024-09-19 21:54:02 -04:00
Ambroos Vaes a1467a0c8f Add deployment-alias-url for pages deployments with Wrangler 3.78.0+ 2024-09-19 18:54:02 +02:00
Maximo Guk 3ea6e3f132 Merge pull request #272 from cloudflare/changeset-release/main
Version Packages
2024-07-02 08:31:12 -05:00
github-actions[bot] ea11499226 Version Packages 2024-06-20 21:23:25 +00:00
Maximo Guk 8f2f89521c Merge pull request #271 from cloudflare/maximo/fixup-235
Unreverts #235 and don't automatically install wrangler when checking if it present
2024-06-20 16:23:02 -05:00
Maximo Guk 66efca2cbb Invoke wrangler to check if it's installed, but don't auto-install through npx/bunx 2024-06-15 23:22:47 -05:00
Maximo Guk aa5d18dd1e Revert "Revert "(feat): Use existing wrangler installation when appropriate (#235)""
This reverts commit 2d275a8f2d.
2024-06-15 22:57:11 -05:00
Maximo Guk b84268f0e0 Merge pull request #268 from cloudflare/changeset-release/main
Version Packages
2024-05-24 10:18:03 -05:00
github-actions[bot] d1b97ebf4d Version Packages 2024-05-24 15:16:10 +00:00
Jacob Hands baf8bc61f1 Merge pull request #265 from Maximo-Guk/maximo/revert-235
Revert "(feat): Use existing wrangler installation when appropriate #235"
2024-05-24 10:15:49 -05:00
Jacob Hands 8edd152f63 Update CODEOWNERS (#266) 2024-05-24 16:13:49 +01:00
Maximo Guk 2d275a8f2d Revert "(feat): Use existing wrangler installation when appropriate (#235)"
This reverts commit 0545ad285a.
2024-05-24 10:04:14 -05:00
github-actions[bot] 8ef515f899 Version Packages (#262)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-05-23 15:58:20 +01:00
dependabot[bot] 9b27cf271c Bump undici from 5.28.3 to 5.28.4 (#249)
Bumps [undici](https://github.com/nodejs/undici) from 5.28.3 to 5.28.4.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v5.28.3...v5.28.4)

---
updated-dependencies:
- dependency-name: undici
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-15 14:01:17 +01:00
dependabot[bot] 405a82610b Bump vite from 5.0.12 to 5.2.8 (#248)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.0.12 to 5.2.8.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.2.8/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-15 13:13:16 +01:00
Adishwar Rishi 0545ad285a (feat): Use existing wrangler installation when appropriate (#235)
* (feat): Check for existing wrangler installation

* Add test for pre-installed wrangler

* Add changeset

* Address CR comments - check for an exact wrangler version match

* Tweak the fixture test for the pre-installed-wrangler test

* Simplify if/else logic for checking wrangler versions as per review notes

* fix(test): Fix execution for fake wrangler installation

* fixup! fix(test): Fix execution for fake wrangler installation

* Setup new CI test convention for wrangler-action

* Remove unncessary ts-expect-error comments

---------

Co-authored-by: Peter Bacon Darwin <pbacondarwin@cloudflare.com>
2024-05-15 09:53:41 +01:00
Somhairle MacLeòid ea5754c9ad Merge pull request #257 from cloudflare/changeset-release/main
Version Packages
2024-05-01 10:03:22 +01:00
github-actions[bot] 983b6349c2 Version Packages 2024-04-30 12:52:35 +00:00
Rahul Sethi b05934f581 Merge pull request #255 from matthewdavidrodgers/legacy-concurrent-secret-uploads
Stop racing secret uploads
2024-04-30 13:52:11 +01:00
Matthew Rodgers 31a6263ef3 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.
2024-04-26 15:44:18 -07:00
Ema Suriano 88906781f0 docs: Update README.md (#252) 2024-04-11 08:50:40 +01:00
Somhairle MacLeòid ebef541795 Delete .github/workflows/pullrequests.yml (#243)
* Delete .github/workflows/pullrequests.yml

* fixup! Delete .github/workflows/pullrequests.yml

---------

Co-authored-by: Peter Bacon Darwin <pbacondarwin@cloudflare.com>
2024-04-08 10:37:04 +01:00
Rahul Sethi 9d4ca0f255 Merge pull request #247 from cloudflare/versions-upload
Add `versions upload` instructions to README
2024-04-04 13:42:17 +01:00
Rahul Sethi 1be73b90f6 update copy 2024-04-03 18:31:44 +01:00
Rahul Sethi 180648017b add minimum wrangler version info 2024-04-03 18:27:15 +01:00
Rahul Sethi 9003b06015 add versions upload instructions to README 2024-04-03 18:17:45 +01:00
Igor Minar fd98a7c990 Merge pull request #245 from Cherry/fix/packageManager
docs: remove warning about packageManager and pnpm default
2024-03-26 15:22:04 -07:00
Igor Minar 4bf3047bb8 Update README.md 2024-03-26 11:07:50 -07:00
Igor Minar f2c22accd3 Update README.md
remote ws
2024-03-26 11:03:23 -07:00
James Ross 0c36639f72 docs: add brief docs about packageManager 2024-03-26 00:31:15 +00:00
James Ross d5a1abb013 docs: remove warning about packageManager and pnpm default 2024-03-25 22:04:20 +00:00
Jiri Spac c214b69bae docs: add warning about packageManager config (#242) 2024-03-11 09:16:06 +00:00
dependabot[bot] bac2829d42 Bump undici from 5.28.2 to 5.28.3 (#237)
Bumps [undici](https://github.com/nodejs/undici) from 5.28.2 to 5.28.3.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v5.28.2...v5.28.3)

---
updated-dependencies:
- dependency-name: undici
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-19 08:49:35 +00:00
Raphael Boidol 78c994fb95 Update README.md to remove deprecation warnings (#236)
`actions/checkout@v3` runs on node16 which is sunset by GitHub Action runners.
2024-02-16 13:12:01 +00:00
dependabot[bot] e3434a7e98 Bump vite from 5.0.7 to 5.0.12 (#230)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.0.7 to 5.0.12.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.0.12/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.0.12/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-01 15:23:55 +00:00
lrapoport-cf 08514fdece Merge pull request #228 from cloudflare/changeset-release/main
Version Packages
2024-01-09 12:32:52 -05:00
github-actions[bot] 7451182d38 Version Packages 2024-01-05 18:08:21 +00:00
Cina Saffary 522c117b7d Merge pull request #227 from AdiRishi/arishi-secrets-error
(enhancement): Surface inner exception when secret:bulk upload command fails
2024-01-05 12:07:56 -06:00
Adishwar Rishi b92719544e Remove extra debug description for err.stack 2024-01-05 18:08:54 +11:00
Adishwar Rishi bbedd8e54f Add changeset 2024-01-05 18:06:15 +11:00
Adishwar Rishi ebaf896045 Improve debug error message for secret:bulk command failure 2024-01-03 13:19:58 +11:00
Adishwar Rishi 7e684fbad9 Surface inner exception when secret:bulk upload command fails 2024-01-03 13:07:49 +11:00
Cina Saffary 2521ec00cc Merge pull request #220 from davwheat/patch-1
docs: update link to api token generation instructions
2023-12-19 20:10:21 -06:00
David Wheatley ba0f485178 docs: update link to api token generation instructions 2023-12-20 02:07:57 +00:00
Cina Saffary 2de0b3dfd8 Merge pull request #218 from cloudflare/changeset-release/main
Version Packages
2023-12-19 11:52:32 -06:00
github-actions[bot] f2d9b652c4 Version Packages 2023-12-13 17:17:49 +00:00
Cina Saffary 0e62acaf21 Merge pull request #213 from GrantBirki/write-results-to-a-file
Save Wrangler Command Output to a Variable
2023-12-13 11:17:24 -06:00
Cina Saffary bd3857047e Apply suggestions from code review 2023-12-13 11:15:50 -06:00
GrantBirki d13856dfc9 add changeset 2023-12-13 10:10:20 -07:00
Grant Birkinbine f88aee4953 Merge branch 'main' into write-results-to-a-file 2023-12-13 10:04:14 -07:00
Grant Birkinbine a5f361f3f0 Update src/index.ts
Co-authored-by: Cina Saffary <itscina@gmail.com>
2023-12-13 10:02:54 -07:00
James Ross 9aba9c34da fix: semver comparison (#216)
* fix: semver comparison

chore: bump dependencies

* chore: add changeset

* fix: update snapshot for error test
2023-12-13 09:53:47 +00:00
Grant Birkinbine 9dd38ad790 Update package.json 2023-12-12 22:34:31 -07:00
Grant Birkinbine 1599928d8e Update src/index.ts 2023-12-12 22:24:09 -07:00
Grant Birkinbine 0fff39f4b7 Update action.yml
Co-authored-by: Cina Saffary <itscina@gmail.com>
2023-12-12 22:19:53 -07:00
GrantBirki ca0bf5eeba properly check for pages deployments 2023-12-07 23:17:03 -07:00
GrantBirki 97c920b38e parse and set the deployment-url 2023-12-07 22:48:10 -07:00
GrantBirki 11fa60953d set another output for stderr as it will almost always contain 'some' data 2023-12-07 22:30:51 -07:00
GrantBirki 5ee7b681bc update readme 2023-12-07 22:27:25 -07:00
GrantBirki 74a612d924 swap from file to output variable 2023-12-07 22:26:06 -07:00
GrantBirki 0558fc8c04 swap to commandOutput 2023-12-07 22:13:33 -07:00
GrantBirki 976ec4d78c bump minor version 2023-12-01 11:12:34 -07:00
GrantBirki a8845f6d95 update docs 2023-12-01 11:10:27 -07:00
GrantBirki 8d6fade5ad save primary wrangler command output to a file (optionally) 2023-12-01 11:04:51 -07:00
Cina Saffary 65996a5230 Merge pull request #205 from cloudflare/cina/184-skip-release-if-tags-exist
Don't attempt to publish when there's no pending release
2023-10-31 10:54:14 -05:00
Cina Saffary 2b7ed1316d Don't trigger release process when there's no release
Currently our release process is kicked off whenever a PR is merged and there are no changesets within the `.changeset` directory. Typically this happens when we intend to publish a release, just after we merge a "Version Packages" PR which removes the changesets and adds the entries to our changelog.

However, this also means that merging any PR without user-facing changes during the period after we've made a release will trigger another release (which luckily fails because the created tag already exists on the remote. See #184).

This change avoids that scenario by fetching tags when checking out the repo. Now when `npx changeset tag` runs, it will see that the tag already exists and skip creating it (`🦋  Skipping tag (already exists):  v3.3.2`). The `git push --tags` step will no longer throw an error ("Everything up-to-date"). And lastly, the publish step won't get triggered because the output from `npx changeset tag` doesn't contain the string `"New tag:"`. The action should just finish successfully with nothing to left do.

Fixes #184
2023-10-31 10:21:29 -05:00
Cina Saffary 9554b3575b Merge pull request #204 from jgentes/patch-1
Swap CF_API_TOKEN with CLOUDFLARE_API_TOKEN
2023-10-30 15:21:15 -05:00
James Gentes 57159ca2ed updated CF_ACCOUNT_ID 2023-10-30 13:17:34 -07:00
James Gentes 7dd5c3621b Swap CF_API_TOKEN with CLOUDFLARE_API_TOKEN
If the token isn't specified, wrangler throws this error, indicating the CLOUDFLARE_API_TOKEN should be set, so it's worth being consistent so folks can copy/paste the examples and have it work out of the box:
```
In a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable for wrangler to work.
```
2023-10-29 17:34:03 -07:00
Cina Saffary 581e17bc51 Merge pull request #201 from cloudflare/changeset-release/main
Version Packages
2023-10-24 13:45:26 -05:00
github-actions[bot] 21a8414ba3 Version Packages 2023-10-19 17:39:52 +00:00
Cina Saffary 819ea006e6 Merge pull request #171 from cloudflare/cina/refactor-subprocess-exec
Refactor subprocess execution to use `@actions/exec`
2023-10-19 12:39:18 -05:00
Cina Saffary 473d9cbd29 Bump DEFAULT_WRANGLER_VERSION to 3.13.2 2023-10-18 17:04:41 -05:00
Cina Saffary 76d614f400 Add changeset 2023-10-18 16:42:34 -05:00
Cina Saffary d647227bbc Refactor subprocess execution to use @actions/exec
Instead of using a mix of `child_process.exec`, `child_process.execSync` and a promisified version of `child_process.exec`, we now (mostly) just use `@actions/exec`. That runs `child_process.spawn` under the hood and handles a lot of character escaping for us. We can also now pass Buffers directly into the subprocess as stdin instead of relying on shell piping.

This ends up fixing a few problems we had where secrets and env var values containing shell metacharacters were being misinterpreted.

Unfortunately, `@actions/exec` doesn't support running with a shell. That means we still have to roll our own wrapper around `child_process.exec` to avoid a breaking change to `preCommands` and `postCommands`, since users might be expecting these to run within a shell.

Also worth noting that we're no longer hiding stdout and stderr from the secret uploading step. We were previously doing this out of an abundance of caution, but it made debugging issues very difficult if secret upload failed for some reason. I feel ok doing this since we're no longer echoing & piping the secret values, wrangler doesn't ever output secret values, and as a last line of defense GitHub masks any secret values that accidentally get logged.
2023-10-18 16:42:34 -05:00
Somhairle MacLeòid 533097350a Merge pull request #196 from cloudflare/penalosa-patch-1 2023-10-12 00:55:30 +01:00
Cina Saffary bc7253f187 Merge pull request #195 from cloudflare/changeset-release/main
Version Packages
2023-10-11 11:33:03 -05:00
github-actions[bot] b2e459a047 Version Packages 2023-10-11 16:31:53 +00:00
Cina Saffary 96c3774b77 Merge pull request #197 from cloudflare/cina/add-missing-licenses-to-test-packages
Add missing licenses to dummy test packages
2023-10-11 11:31:22 -05:00
Cina Saffary b00f94ea6f Merge pull request #183 from cloudflare/dependabot/npm_and_yarn/postcss-8.4.31
Bump postcss from 8.4.27 to 8.4.31
2023-10-11 11:08:41 -05:00
dependabot[bot] c649733249 Bump postcss from 8.4.27 to 8.4.31
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.27 to 8.4.31.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.27...8.4.31)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-11 16:04:26 +00:00
Cina Saffary d43f9c81c7 Set dummy test packages as private 2023-10-11 10:59:27 -05:00
Cina Saffary 219a0887d7 Add missing licenses to dummy test packages
This is mostly to prevent yarn from throwing a warning during our test runs
2023-10-11 10:56:07 -05:00
Somhairle MacLeòid 25b114f472 Update pullrequests.yml 2023-10-11 15:24:12 +01:00
Cina Saffary f40a9f5f82 Merge pull request #193 from cloudflare/cina/fix-packagemanager-default
Don't set `packageManager` default via action.yml
2023-10-11 03:54:35 -05:00
Cina Saffary bd06b290b1 Fix code formatting errors 2023-10-10 18:00:40 -05:00
Cina Saffary a4509d507c Don't set packageManager default via action.yml
We need to distinguish between when the value is and isn't set in order to perform inference based on lockfile and only fallback to the default of npm if inference fails.
2023-10-10 18:00:32 -05:00
Cina Saffary 0e6f0d3080 Merge pull request #192 from cloudflare/changeset-release/main
Version Packages
2023-10-10 15:23:32 -05:00
github-actions[bot] 326ba30882 Version Packages 2023-10-10 20:20:04 +00:00
Cina Saffary 032a7248fa Merge pull request #188 from simpleauthority/feat/add-bun-support
Add bun support
2023-10-10 15:19:09 -05:00
Jacob Andersen 080373bdb6 Add bun as a package manager option in action.yml 2023-10-10 13:17:05 -07:00
Cina Saffary 231d787db1 Update test/bun/package.json 2023-10-10 15:05:59 -05:00
Jacob Andersen d9a0a00f8b Add changeset 2023-10-10 12:41:00 -07:00
Jacob Andersen 779191a652 add bun support 2023-10-10 12:36:03 -07:00
Cina Saffary 556068fb59 Merge pull request #191 from cloudflare/changeset-release/main
Version Packages
2023-10-10 09:25:51 -05:00
github-actions[bot] caa59485d0 Version Packages 2023-10-10 14:16:00 +00:00
Cina Saffary c2494c1ca6 Merge pull request #190 from cloudflare/cina/180-fix-undefined-package-manager
Use npm as fallback if no `packageManager` specified
2023-10-10 09:14:13 -05:00
Cina Saffary 528687aaf4 Refactor to use npm as a fallback if no packageManager specified...
and no lockfile is present. Fixes #180
2023-10-09 17:20:09 -05:00
lrapoport-cf bd50d10af9 Merge pull request #185 from cloudflare/lrapoport/fix-issue-workflow
Fix issue workflow
2023-10-05 15:34:02 -04:00
lrapoport-cf 2513ffc6cc Fix workflow syntax for multiple conditions 2023-10-05 15:29:35 -04:00
lrapoport-cf 9df50d42e4 Merge pull request #182 from cloudflare/lrapoport/update-issues-workflow
Update issues workflow
2023-10-05 15:06:06 -04:00
lrapoport-cf 4611c14c05 Fix format 2023-10-05 15:01:41 -04:00
lrapoport-cf ac3f259fcf Add PR workflow 2023-10-02 11:29:17 -04:00
lrapoport-cf b2645622e9 Rename issue workflow and job 2023-10-02 11:29:03 -04:00
lrapoport-cf f4e6269765 Add transferred issues to workers-sdk GH project 2023-10-02 11:26:14 -04:00
Cina Saffary 72931e59b2 Merge pull request #174 from cloudflare/changeset-release/main
Version Packages
2023-10-02 10:23:58 -05:00
github-actions[bot] e0254d06bc Version Packages 2023-09-18 19:12:19 +00:00
Jacob M-G Evans 13d70757d2 Merge pull request #166 from nix6839/many-package-manager
Support many package managers
2023-09-18 14:11:51 -05:00
Han Yeong-woo aec73ae744 Add wangler files in fixtures 2023-09-19 03:52:00 +09:00
Han Yeong-woo f0cc3efccf Install yarn and pnpm on test 2023-09-19 03:51:27 +09:00
Han Yeong-woo 50b529e7b8 Fix pass arg issue 2023-09-19 03:50:54 +09:00
Han Yeong-woo 6771675815 Run format 2023-09-19 01:37:01 +09:00
Jacob M-G Evans 4ae0557f8d Update two-rocks-hope.md 2023-09-18 11:33:50 -05:00
Han Yeong-woo 7d7b98826e Add changeset 2023-09-19 01:30:36 +09:00
Han Yeong-woo d1073d57ba Run format 2023-09-19 01:30:35 +09:00
Han Yeong-woo c3b99e2c18 Ignore to pnpm-lock.yaml in formatting 2023-09-19 01:30:35 +09:00
Han Yeong-woo 2375787b23 Move test fixtures to parent directory 2023-09-19 01:30:35 +09:00
Han Yeong-woo 11f981cea4 Add tests for package managers support 2023-09-19 01:30:34 +09:00
Han Yeong-woo 868dbd9a40 Remove unused function 2023-09-19 01:30:34 +09:00
Han Yeong-woo a009342d77 Add packageManager setting 2023-09-19 01:30:34 +09:00
Han Yeong-woo f2e4cda4dd Detect package manager and uses its commands
Fix #156
2023-09-19 01:30:33 +09:00
Han Yeong-woo f4f2e854d7 Add isValidPackcageManager() util 2023-09-19 01:30:33 +09:00
Han Yeong-woo cbe5f5b523 Add detect package manager util function 2023-09-19 01:30:33 +09:00
Jacob M-G Evans fcf2d83f3d Merge pull request #172 from cloudflare/demosjarco/main
Node 20 update
2023-09-18 11:29:49 -05:00
DemosJarco f5d1ca36ae Added comment with link 2023-09-04 11:54:36 -07:00
DemosJarco 4d6d6abfb4 Update action.yml 2023-09-04 11:50:59 -07:00
Cina Saffary 416205365a Merge pull request #165 from cloudflare/cina/fix-release-workflow-pt2
Build action before pushing dist-tags
2023-08-31 10:55:48 -05:00
Cina Saffary 7292a4ae99 Build action before pushing dist-tags 2023-08-31 10:26:14 -05:00
Cina Saffary 4528b66125 Merge pull request #164 from cloudflare/cina/fix-release-workflow
add `tag_name` input to Tag & Release Action step of release workflow
2023-08-31 09:51:29 -05:00
Cina Saffary ee3e2a9b37 add tag_name input to Tag & Release Action 2023-08-30 17:27:17 -05:00
Cina Saffary 361844799a Merge pull request #163 from cloudflare/changeset-release/main
Version Packages
2023-08-30 09:39:11 -05:00
github-actions[bot] 26103fa86c Version Packages 2023-08-30 14:04:59 +00:00
Cina Saffary 47d4afdbd8 Merge pull request #161 from cloudflare/cina/throw-on-failures
Refactor error handling to stop execution on errors
2023-08-30 09:04:22 -05:00
Cina Saffary d7637bf514 workaround to fix failing workflow. Refs #162
The action fails to upload secrets during one of the testing steps since the Worker doesn't exist yet.
2023-08-29 19:39:50 -05:00
Cina Saffary 71199d2757 prettier scripts incorrectly set --ignore-path
--ignore-path is meant to be the path of an ignore-file, not the actual directories to ignore.
2023-08-29 18:27:03 -05:00
Cina Saffary 2962e94ac8 Move unit-tested modules into utils file
This fixes the issue where running the unit tests invoked the entire action since index.ts calls `main()` at the top-level scope.
2023-08-29 18:16:41 -05:00
Cina Saffary 5517edbb28 Avoid double "Error: Error:" logging 2023-08-29 18:00:03 -05:00
Cina Saffary f74c325efc Limit error logging on secret upload 2023-08-29 17:57:19 -05:00
Cina Saffary 105f191b19 Update snapshot for checkWorkingDirectory test 2023-08-29 17:43:29 -05:00
Cina Saffary e5251df521 Refactor error handling to stop execution on errors. Fixes #160. 2023-08-29 16:36:50 -05:00
Jacob M-G Evans b076e889fb Merge pull request #159 from cloudflare/jacobmgevans/gh-workflow
Automation release
2023-08-24 15:45:07 -05:00
Jacob M-G Evans 30e126d1a4 consolidated workflows and triggers Action release based on changeset event output 'publish' being true 2023-08-24 15:35:32 -05:00
Jacob M-G Evans fcf648c789 Merge pull request #158 from cloudflare/changeset-release/main
Version Packages
2023-08-17 11:06:50 -05:00
github-actions[bot] fcbabec21e Version Packages 2023-08-16 15:55:01 +00:00
Jacob M-G Evans 0aa12f0c2b Merge pull request #154 from cloudflare/jacobmgevans/silence-mode
Quiet feature
2023-08-16 10:54:36 -05:00
Jacob M-G Evans ad7441b6ad Merge pull request #157 from EstebanBorai/main
fix: use `wrangler@3.5.1` by default
2023-08-16 10:51:54 -05:00
Jacob M-G Evans 3f40637a1c Quiet feature
Some of the stderr, stdout, info & groupings can be a little noisy for some users and use cases.
This feature allows for a option to be passed 'quiet: true' this would significantly reduce the noise.

There will still be output that lets the user know Wrangler Installed and Wrangler Action completed successfully.
Any failure status will still be output to the user as well, to prevent silent failures.

resolves #142
2023-08-16 09:37:29 -05:00
Esteban Borai 4132892387 fix: use wrangler@3.5.1 by default 2023-08-15 17:23:30 -04:00
lrapoport-cf 62ce9d23a3 Merge pull request #155 from ethanppl/fix-readme
Fix examples in README.md
2023-08-14 13:23:31 -04:00
Ethan f089b0a195 Update README.md
Co-authored-by: lrapoport-cf <107272160+lrapoport-cf@users.noreply.github.com>
2023-08-14 22:06:06 +08:00
Ethan Pang 4318a2fb97 Fix examples in README.md 2023-08-14 18:19:03 +08:00
lrapoport-cf c8bbc2b667 Merge pull request #153 from cloudflare/lrapoport/update-codeowners
Add wrangler-admin as codeowner
2023-08-11 14:32:08 -04:00
lrapoport-cf 3c262de2e3 Add wrangler-admin as codeowner 2023-08-11 14:29:40 -04:00
Jacob M-G Evans 36ba30dcc9 Merge pull request #152 from cloudflare/JacobMGEvans-patch-1
Update README.md
2023-08-11 11:40:47 -05:00
Jacob M-G Evans a9ac89cd00 Update README.md 2023-08-11 11:37:23 -05:00
Jacob M-G Evans 93b26a6664 Merge pull request #151 from cloudflare/jacobmgevans/release-trigger
Changing publish trigger for build & tagging to released or edit
2023-08-10 17:10:35 -05:00
Jacob M-G Evans 9641d961b5 Changing publish trigger for build & tagging to released or edit 2023-08-10 17:09:55 -05:00
Jacob M-G Evans 5b3d60c156 Testing Changeset behavior 2023-08-10 16:51:52 -05:00
Jacob M-G Evans 3252711404 hotfix 2023-08-10 16:46:29 -05:00
Jacob M-G Evans 2faabf36a2 Merge pull request #150 from cloudflare/jacobmgevans/changesets-tag-cli
Changesets needs a tag created for release
2023-08-10 16:45:05 -05:00
Jacob M-G Evans b734d85d74 Changesets needs a tag created for release 2023-08-10 16:40:12 -05:00
Jacob M-G Evans 8ca2ff1612 Merge pull request #148 from cloudflare/changeset-release/main
Version Packages
2023-08-10 16:19:21 -05:00
Jacob M-G Evans 7d08a8657e Merge pull request #149 from cloudflare/jacobmgevans/changesets-needs-publish
Spoofing publish for Changeset Action
2023-08-10 16:18:55 -05:00
Jacob M-G Evans e193627f19 Spoofing publish for Changeset Action 2023-08-10 16:17:56 -05:00
github-actions[bot] 55b80c5f62 Version Packages 2023-08-10 21:11:56 +00:00
Jacob M-G Evans f61dc4d5a9 Merge pull request #147 from cloudflare/revert-146-changeset-release/main
Revert "Version Packages"
2023-08-10 16:11:27 -05:00
Jacob M-G Evans 58f274b9f7 Revert "Version Packages" 2023-08-10 16:11:02 -05:00
Jacob M-G Evans ef7686b259 Merge pull request #146 from cloudflare/changeset-release/main
Version Packages
2023-08-10 16:06:17 -05:00
github-actions[bot] d63b9dc486 Version Packages 2023-08-10 21:06:00 +00:00
Jacob M-G Evans 88ccce72e0 Merge pull request #145 from cloudflare/revert-140-changeset-release/main
Revert "Version Packages"
2023-08-10 16:05:37 -05:00
Jacob M-G Evans 554eea134a Revert "Version Packages" 2023-08-10 16:05:14 -05:00
Jacob M-G Evans a584cc5606 Merge pull request #140 from cloudflare/changeset-release/main
Version Packages
2023-08-10 16:00:53 -05:00
github-actions[bot] 0f5076c3e7 Version Packages 2023-08-10 20:55:38 +00:00
Jacob M-G Evans c7d1bd35bf Merge pull request #143 from cloudflare/jacobmgevans/version-syntax-support
Update documentation to include new versioning syntax support
2023-08-10 15:55:12 -05:00
Jacob M-G Evans 3c728e1d5d Update README.md
Co-authored-by: lrapoport-cf <107272160+lrapoport-cf@users.noreply.github.com>
2023-08-10 15:45:08 -05:00
Jacob M-G Evans a333df3c9e Update documentation to include new versioning syntax support 2023-08-10 15:30:29 -05:00
Jacob M-G Evans bb0133dcfc Merge pull request #141 from cloudflare/jacobmgevans/add-more-error-handling
Additional Error Handling
2023-08-10 14:54:16 -05:00
Jacob M-G Evans 88466ea5ae Update .changeset/itchy-buses-grow.md
Co-authored-by: Cina Saffary <itscina@gmail.com>
2023-08-10 14:51:00 -05:00
Jacob M-G Evans 74433eb31e Additional Error Handling
Previously, we prevented any error logs from propagating too far to prevent leaking of any potentially sensitive information. However, this made it difficult for developers to debug their code.

In this release, we have updated our error handling to allow for more error messaging from pre/post and custom commands. We still discourage the use of these commands for secrets or other sensitive information, but we believe this change will make it easier for developers to debug their code.

Relates to #137
2023-08-10 10:11:35 -05:00
Jacob M-G Evans 559dd40fe6 Merge pull request #139 from cloudflare/jacobmgevans/gh-permissions
Another GH permission for GH Token for Changesets
2023-08-09 16:45:52 -05:00
Jacob M-G Evans 9930080b73 Another GH permission for GH Token for Changesets 2023-08-09 16:39:06 -05:00
Jacob M-G Evans 43bbbb16ab Merge pull request #138 from cloudflare/jacobmgevans/changeset-gh-dep
Add missing GH github dep
2023-08-09 15:42:57 -05:00
Jacob M-G Evans 598c6b4043 Add missing GH github dep 2023-08-09 15:38:59 -05:00
Jacob M-G Evans 0c094f5307 Merge pull request #132 from cloudflare/jacobmgevans/changesets
Add Changesets
2023-08-09 15:28:38 -05:00
Jacob M-G Evans 6ea2e616f2 Update .github/workflows/changesets.yml
Co-authored-by: Cina Saffary <itscina@gmail.com>
2023-08-09 15:17:25 -05:00
Jacob M-G Evans 1b4a61cee9 Remove unnecessary context from fetch depth and node_env 2023-08-09 14:50:06 -05:00
Jacob M-G Evans 8e3876a0c9 Update .github/workflows/changesets.yml
Co-authored-by: Cina Saffary <itscina@gmail.com>
2023-08-09 14:43:50 -05:00
Jacob M-G Evans 8fab856f8d Update .github/workflows/changesets.yml
Co-authored-by: Cina Saffary <itscina@gmail.com>
2023-08-09 14:43:40 -05:00
Jacob M-G Evans ce1ea4e7c3 Merge pull request #136 from cloudflare/jacobmgevans/esm-support
Updating build script to convert to .mjs for ESM support
2023-08-09 11:28:16 -05:00
Jacob M-G Evans e420d29d0a Updating build script to convert to .mjs for ESM support 2023-08-09 11:19:52 -05:00
Jacob M-G Evans 311d514d9d Add Changesets 2023-08-08 17:14:09 -05:00
Jacob M-G Evans d05a65f82b Merge pull request #129 from cloudflare/jacobmgevans/README-action-update
Update README with newer version of checkout action
2023-08-08 15:13:50 -05:00
Jacob M-G Evans c9da75b82e Update README with newer version of checkout action 2023-08-08 15:13:05 -05:00
Jacob M-G Evans 29cef5effc Merge pull request #131 from cloudflare/jacobmgevans/action-permissions
Add permissions to Action
2023-08-08 14:51:16 -05:00
Jacob M-G Evans ee60b7bd5e Add permissions to Action 2023-08-08 14:50:15 -05:00
Jacob M-G Evans 0ab3500d54 Merge pull request #128 from cloudflare/jacobmgevans/automate-action-publish
Automate Action Release
2023-08-08 14:14:19 -05:00
Jacob M-G Evans dfa59a861f start script not needed, using Act locally 2023-08-08 13:28:05 -05:00
Jacob M-G Evans 342e6243b7 remove dist & node_modules from repo 2023-08-08 13:21:44 -05:00
Jacob M-G Evans d6590f86f4 Automate Action Release
Utilizing build & tag action to automate the deps and build steps for releasing the Action. Additionally using NCC for the build process to easy compile the deps into a small executable with the JS file.
2023-08-08 13:03:11 -05:00
Jacob M-G Evans 97e54f3396 Merge pull request #127 from cloudflare/lrapoport/add-issues-to-gh-project
Automatically add issues to workers-sdk GH project
2023-08-08 08:35:11 -05:00
lrapoport-cf f85e1acfa7 Add workflow to add issues to GH project 2023-08-07 22:10:59 -04:00
lrapoport-cf e18e1dfc82 Merge pull request #124 from cloudflare/lrapoport/publish-to-deploy
Update publish to deploy
2023-08-07 16:31:50 -04:00
Jacob M-G Evans 20ea506049 Temp fix for deps issue 2023-08-07 15:11:15 -05:00
lrapoport-cf 45c57ae013 Update publish to deploy 2023-08-07 15:57:12 -04:00
Jacob M-G Evans a5fd4fdd34 Merge pull request #121 from cloudflare/jacobmgevans/codeowners
Owners: Wrangler Team
2023-08-07 14:42:13 -05:00
Jacob M-G Evans 002bb083fa Update README.md
Co-authored-by: lrapoport-cf <107272160+lrapoport-cf@users.noreply.github.com>
2023-08-07 14:42:06 -05:00
Jacob M-G Evans 42c609b2d6 included README update 2023-08-07 14:29:10 -05:00
Jacob M-G Evans c8f756bccf Fix Prettier check with /dist 2023-08-07 14:23:56 -05:00
Jacob M-G Evans 81fb6a9681 Owners: Wrangler Team 2023-08-07 14:11:03 -05:00
Jacob M-G Evans 6231e31709 hotfix missing dist 2023-08-07 14:06:55 -05:00
Jacob M-G Evans 0c419bee6c Merge pull request #119 from cloudflare/jacobmgevans/remove-v1-examples
Update README to remove v1 suggestions/examples
2023-08-07 13:30:09 -05:00
Jacob M-G Evans 74a9b9885f Update README to remove v1 suggestions/examples 2023-08-07 13:18:56 -05:00
Jacob M-G Evans 3dd57ff2bb Merge pull request #112 from cloudflare/jacobmgevans/typescript-overhaul
v4 w/ TypeScript Overhaul
2023-08-07 13:08:09 -05:00
Jacob M-G Evans edb2a58814 feat: rewrite Wrangler Action in TypeScript
* Removes dependencies such as Docker, decreasing spin-up time
* Adds community-requested features, including bulk secrets API utilization from Wrangler
* Fixes CI/CD
* Adds testing
* Improves command implementation
* Begins using Node for the Action engine/runner
* Openly discusses all changes with the community
  GitHub Discussions opened and Issues monitored

BREAKING CHANGES:
* Docker is no longer a dependency
* Wrangler v1 is no longer supported

Additional related Internal tickets:
Major Version Default: https://jira.cfdata.org/browse/DEVX-632
Rewrite Project: DEVX-804,802,800,632
2023-08-07 13:05:09 -05:00
Sunil Pai 4c10c1822a Merge pull request #72 from WalshyDev/walshy/pages-996-support-wrangler2
Support wrangler2 - v2
2022-05-09 12:02:45 +01:00
Daniel Walsh 3d1979ca8e Support wrangler2 - v2.0.0 2022-05-09 11:49:03 +01:00
87 changed files with 7564 additions and 443 deletions
+8
View File
@@ -0,0 +1,8 @@
# 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 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
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
+13
View File
@@ -0,0 +1,13 @@
{
"$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json",
"changelog": [
"@changesets/changelog-github",
{ "repo": "cloudflare/wrangler-action" }
],
"commit": false,
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
+143 -70
View File
@@ -1,91 +1,164 @@
on: push
name: Wrangler Action Self Testing
on:
pull_request:
branches:
- main
jobs:
lint:
wrangler_action_self_testing:
runs-on: ubuntu-latest
name: Lint
steps:
- uses: actions/checkout@v2
- name: Lint shell script
uses: azohra/shell-linter@v0.3.0
with:
path: "entrypoint.sh"
build-only:
runs-on: ubuntu-latest
name: Only build the app
steps:
- uses: actions/checkout@v2
- name: Build app
- name: Checkout Repo
uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: "24"
cache: "npm"
- name: Install modules and build
run: npm ci && npm run build
- name: Unit Tests
run: npm run test
- name: Check Formatting
run: npm run check
- name: Only build app
uses: ./
with:
workingDirectory: "./src/test/fixtures/only-build"
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
workingDirectory: "test"
publish: false
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --dry-run
- name: Only build app w/ quiet enabled
uses: ./
with:
quiet: true
workingDirectory: "./src/test/fixtures/build-quiet"
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --dry-run
- name: Environment support
uses: ./
with:
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
preCommands: echo "*** pre commands ***"
postCommands: |
echo "*** post commands ***"
wrangler build
echo "******"
env:
SECRET1: ${{ secrets.SECRET1 }}
SECRET2: ${{ secrets.SECRET2 }}
publish:
runs-on: ubuntu-latest
name: Publish app
steps:
- uses: actions/checkout@v2
- name: Publish app
uses: ./
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
environment: "production"
workingDirectory: 'test'
publish_legacy_credentials:
runs-on: ubuntu-latest
name: Publish app with legacy credentials
steps:
- uses: actions/checkout@v2
- name: Publish app
uses: ./
with:
apiKey: ${{ secrets.CLOUDFLARE_API_KEY }}
email: ${{ secrets.CLOUDFLARE_EMAIL }}
environment: "production"
workingDirectory: 'test'
publish_hardcoded_wrangler_version:
runs-on: ubuntu-latest
name: Publish app with hardcoded Wrangler version
steps:
- uses: actions/checkout@v2
- name: Publish app
uses: ./
with:
apiKey: ${{ secrets.CLOUDFLARE_API_KEY }}
email: ${{ secrets.CLOUDFLARE_EMAIL }}
environment: "production"
wranglerVersion: '1.5.0'
workingDirectory: 'test'
publish_secrets:
runs-on: ubuntu-latest
name: Publish app with secrets
steps:
- uses: actions/checkout@v2
- name: Publish app
# START Setup and teardown of Workers w/ Secrets Tests
- name: Deploy app secrets w/ hardcoded Wrangler v2
uses: ./
with:
wranglerVersion: "2.20.0"
workingDirectory: "./src/test/fixtures/secrets-v2"
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
environment: "production"
workingDirectory: "test"
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
secrets: |
SECRET1
SECRET2
preCommands: echo "*** pre command ***"
postCommands: |
echo "*** post commands ***"
echo "******"
env:
SECRET1: ${{ secrets.SECRET1 }}
SECRET2: ${{ secrets.SECRET2 }}
SECRET2: ${{ secrets.SECRET2 }}
- name: Health Check Deployed Worker
run: node .github/workflows/workerHealthCheck.ts wrangler-action-test-secrets-v2
shell: bash
- name: Deploy app secrets w/ default version
uses: ./
with:
workingDirectory: "./src/test/fixtures/secrets-default"
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
secrets: |
SECRET1
SECRET2
env:
SECRET1: ${{ secrets.SECRET1 }}
SECRET2: ${{ secrets.SECRET2 }}
- name: Health Check Deployed Worker
run: node .github/workflows/workerHealthCheck.ts wrangler-action-test-secrets-default
shell: bash
- name: Clean Up Deployed Workers
uses: ./
with:
workingDirectory: "./src/test/fixtures/secrets-default"
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
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: "./src/test/fixtures/specify-package-manager"
packageManager: "npm"
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --dry-run
- name: Support unspecified packageManager with no lockfile
uses: ./
with:
workingDirectory: "./src/test/fixtures/unspecified-package-manager"
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --dry-run
- name: Support npm package manager
uses: ./
with:
workingDirectory: "./src/test/fixtures/npm"
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --dry-run
- name: Install yarn
run: npm i -g yarn
- name: Support yarn package manager
uses: ./
with:
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@10
- name: Support pnpm package manager
uses: ./
with:
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
+55
View File
@@ -0,0 +1,55 @@
name: Release
on:
push:
branches:
- main
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
if: ${{ github.repository_owner == 'cloudflare' }}
name: Release
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write
pull-requests: write
issues: read
steps:
- name: Checkout Repo
uses: actions/checkout@v5
with:
fetch-depth: 0
fetch-tags: true
- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: "24"
cache: "npm"
- name: Install modules
run: npm ci
- name: Create Version PR
id: changesets
uses: changesets/action@v1
with:
createGithubReleases: true
publish: npx changeset tag
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build action
if: steps.changesets.outputs.published == 'true'
run: npm run build
- name: Push dist-tags
if: steps.changesets.outputs.published == 'true'
uses: JasonEtco/build-and-tag-action@v2
with:
tag_name: v${{ fromJSON(steps.changesets.outputs.publishedPackages)[0].version }}
env:
GITHUB_TOKEN: ${{ github.token }}
+30
View File
@@ -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
+41
View File
@@ -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);
},
});
+95
View File
@@ -0,0 +1,95 @@
dist
.idea
.vscode
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Node ###
node_modules
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Moved from ./templates for ignoring all locks in templates
templates/**/*-lock.*
templates/**/*.lock
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
+1
View File
@@ -0,0 +1 @@
pnpm-lock.yaml
+6
View File
@@ -0,0 +1,6 @@
{
"printWidth": 80,
"singleQuote": false,
"semi": true,
"useTabs": true
}
+266
View File
@@ -0,0 +1,266 @@
# 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`)
These output variables are intended to be used by more advanced workflows that require the output results or deployment url from Wrangler commands in subsequent workflow steps.
### Patch Changes
- [#216](https://github.com/cloudflare/wrangler-action/pull/216) [`9aba9c34daabca23a88191a5fe1b81fa721c1f11`](https://github.com/cloudflare/wrangler-action/commit/9aba9c34daabca23a88191a5fe1b81fa721c1f11) Thanks [@Cherry](https://github.com/Cherry)! - Fixes issues with semver comparison, where version parts were treated lexicographically instead of numerically.
Bulk secret uploading was introduced in wrangler `3.4.0`, and this action tries to check if the version used is greater than `3.4.0`, and then if so, using the new bulk secret API which is faster. Due to a bug in the semver comparison, `3.19.0` was being considered less than `3.4.0`, and then using an older and slower method for uploading secrets.
Now the semver comparison is fixed, the faster bulk method is used for uploading secrets when available.
## 3.3.2
### Patch Changes
- [#171](https://github.com/cloudflare/wrangler-action/pull/171) [`76d614f`](https://github.com/cloudflare/wrangler-action/commit/76d614f400bd92237ed23c3df559f2c31b14a790) Thanks [@1000hz](https://github.com/1000hz)! - Fixed issues that caused the action to fail if any secret or var values contained shell metacharacters.
- [#171](https://github.com/cloudflare/wrangler-action/pull/171) [`473d9cb`](https://github.com/cloudflare/wrangler-action/commit/473d9cbd296528b41c653af10062faba6540a7ab) Thanks [@1000hz](https://github.com/1000hz)! - Bumped `DEFAULT_WRANGLER_VERSION` to 3.13.2
## 3.3.1
### Patch Changes
- [#193](https://github.com/cloudflare/wrangler-action/pull/193) [`a4509d5`](https://github.com/cloudflare/wrangler-action/commit/a4509d507c62dd7f49fba7df7d2db3997222393a) Thanks [@1000hz](https://github.com/1000hz)! - Fixed the package manager not being inferred based on lockfile when the `packageManager` input isn't set.
## 3.3.0
### Minor Changes
- [#188](https://github.com/cloudflare/wrangler-action/pull/188) [`d9a0a00`](https://github.com/cloudflare/wrangler-action/commit/d9a0a00f8bc502ceea2a60e5af258416b35a85b9) Thanks [@simpleauthority](https://github.com/simpleauthority)! - Added support for Bun as a package manager
## 3.2.1
### Patch Changes
- [#190](https://github.com/cloudflare/wrangler-action/pull/190) [`528687a`](https://github.com/cloudflare/wrangler-action/commit/528687aaf436f67565918533ffd15c250f39c47b) Thanks [@1000hz](https://github.com/1000hz)! - Fixed action failure when no `packageManager` specified and no lockfile is found. The action now falls back to using npm.
## 3.2.0
### Minor Changes
- [#166](https://github.com/cloudflare/wrangler-action/pull/166) [`7d7b988`](https://github.com/cloudflare/wrangler-action/commit/7d7b98826e14e9ad422870a7263b7074b40bf16f) Thanks [@nix6839](https://github.com/nix6839)! - Support for package managers other than npm, such as pnpm and yarn.
fixes #156
## 3.1.1
### Patch Changes
- [#161](https://github.com/cloudflare/wrangler-action/pull/161) [`e5251df`](https://github.com/cloudflare/wrangler-action/commit/e5251df52154e9ebc98edb02ee0598c7210dcf0f) Thanks [@1000hz](https://github.com/1000hz)! - Refactored error handling to stop execution when action fails. Previously, the action would continue executing to completion if one of the steps encountered an error. Fixes #160.
## 3.1.0
### Minor Changes
- [#154](https://github.com/cloudflare/wrangler-action/pull/154) [`3f40637`](https://github.com/cloudflare/wrangler-action/commit/3f40637a1c48016d2101e412a7a06f1eb4b9c2fd) Thanks [@JacobMGEvans](https://github.com/JacobMGEvans)! - feat: Quiet mode
Some of the stderr, stdout, info & groupings can be a little noisy for some users and use cases.
This feature allows for a option to be passed 'quiet: true' this would significantly reduce the noise.
There will still be output that lets the user know Wrangler Installed and Wrangler Action completed successfully.
Any failure status will still be output to the user as well, to prevent silent failures.
resolves #142
## 3.0.2
### Patch Changes
- [#147](https://github.com/cloudflare/wrangler-action/pull/147) [`58f274b`](https://github.com/cloudflare/wrangler-action/commit/58f274b9f70867447519c6fa983c813e2b167b85) Thanks [@JacobMGEvans](https://github.com/JacobMGEvans)! - Added more error logging when a command fails to execute
Previously, we prevented any error logs from propagating too far to prevent leaking of any potentially sensitive information. However, this made it difficult for developers to debug their code.
In this release, we have updated our error handling to allow for more error messaging from pre/post and custom commands. We still discourage the use of these commands for secrets or other sensitive information, but we believe this change will make it easier for developers to debug their code.
Relates to #137
- [#147](https://github.com/cloudflare/wrangler-action/pull/147) [`58f274b`](https://github.com/cloudflare/wrangler-action/commit/58f274b9f70867447519c6fa983c813e2b167b85) Thanks [@JacobMGEvans](https://github.com/JacobMGEvans)! - Adding Changesets
- [Version 3.0.0](#version-300)
- [Version 2.0.0](#version-200)
## Version 3.0.0 (Breaking update)
### Additions
- **Rewritten Wrangler Action in TypeScript.**
- Bulk secrets API utilization from Wrangler.
- Added testing for improved reliability.
- Implemented multiline support for the `command` input to allow running multiple Wrangler commands.
- Now using Node for the Action engine/runner.
- Open discussions with the community on all changes through GitHub Discussions and monitored Issues.
### Removals
- Removed Docker as a dependency.
- Dropped support for Wrangler v1.
### Changes
- Fixed CI/CD issues.
### Breaking changes
- Wrangler v1 is no longer supported.
- Please update to the latest version of Wrangler.
- Updated default version of Wrangler to v3.4.0
### Additional Notes
- Major Version Default: [DEVX-632](https://jira.cfdata.org/browse/DEVX-632)
- Rewrite Project Tickets: [DEVX-804](https://jira.cfdata.org/browse/DEVX-804), [DEVX-802](https://jira.cfdata.org/browse/DEVX-802), [DEVX-800](https://jira.cfdata.org/browse/DEVX-800), [DEVX-632](https://jira.cfdata.org/browse/DEVX-632)
---
## Version 2.0.0 (Breaking update)
### Additions
- New `command` input
- This allows you to specify the Wrangler command you would like to run.
For example, if you want to publish the production version of your Worker you may run `publish --env=production`.
- This opens up other possibilities too like publishing a Pages project: `pages publish <directory> --project-name=<name>`.
- New `accountId` input
- This allows you to specify your account ID.
### Removals
- Removed `publish` input (refer to [Breaking changes](#breaking-changes)).
### Changes
-- no changes --
### Breaking changes
- `publish` has been removed.
- You should instead do `command:
+3
View File
@@ -0,0 +1,3 @@
* @cloudflare/wrangler @cloudflare/banda-team
CHANGELOG.md @cloudflare/wrangler-admins @cloudflare/banda-team
+21
View File
@@ -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.
-5
View File
@@ -1,5 +0,0 @@
FROM node:16
ENV XDG_CONFIG_HOME /github/workspace
ENV WRANGLER_HOME /github/workspace
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
+243 -92
View File
@@ -1,10 +1,29 @@
# Wrangler GitHub Action
✨ Zero-config [Cloudflare Workers](https://workers.cloudflare.com) deployment using [Wrangler](https://github.com/cloudflare/wrangler) and [GitHub Actions](https://github.com/actions)
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.
- Global API key & Email Auth no longer supported
- Action version syntax is newly supported. This means e.g. `uses: cloudflare/wrangler-action@v3`, `uses: cloudflare/wrangler-action@v3.x`, and `uses: cloudflare/wrangler-action@v3.x.x` are all now valid syntax. Previously supported syntax e.g. `uses: cloudflare/wrangler-action@3.x.x` is no longer supported -- the prefix `v` is now necessary.
[Refer to Changelog for more information](CHANGELOG.md).
## Usage
Add `wrangler-action` to the workflow for your Workers application. The below example will publish your application on pushes to the `master` branch:
Add `wrangler-action` to the workflow for your Workers/Pages application. The below example will deploy a Worker on a `git push` to the `main` branch:
```yaml
name: Deploy
@@ -12,74 +31,51 @@ name: Deploy
on:
push:
branches:
- master
- main
jobs:
deploy:
runs-on: ubuntu-latest
name: Deploy
steps:
- uses: actions/checkout@v2
- name: Publish
uses: cloudflare/wrangler-action@1.3.0
- uses: actions/checkout@v6
- name: Deploy
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
```
## 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 `CF_API_TOKEN`:
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`:
```yaml
jobs:
deploy:
name: Deploy
steps:
uses: cloudflare/wrangler-action@1.3.0
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
```
`wrangler-action` also supports using your [global API key and email](https://developers.cloudflare.com/workers/quickstart/#global-api-key) as an authentication method, although API tokens are preferred. Pass in `apiKey` and `email` to the GitHub Action to use this method:
```yaml
jobs:
deploy:
name: Deploy
steps:
uses: cloudflare/wrangler-action@1.3.0
with:
apiKey: ${{ secrets.CF_API_KEY }}
email: ${{ secrets.CF_EMAIL }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
```
## Configuration
If you're using Wrangler's [environments](https://developers.cloudflare.com/workers/tooling/wrangler/configuration/environments/) feature, you can customize _where_ the action deploys to by passing an `environment` in the `with` block of your workflow:
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:
deploy:
steps:
uses: cloudflare/wrangler-action@1.3.0
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
environment: 'production'
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
wranglerVersion: "4"
```
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 `1.6.0`:
```yaml
jobs:
deploy:
steps:
uses: cloudflare/wrangler-action@1.3.0
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
wranglerVersion: '1.6.0'
```
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.
@@ -87,38 +83,39 @@ Optionally, you can also pass a `workingDirectory` key to the action. This will
jobs:
deploy:
steps:
uses: cloudflare/wrangler-action@1.3.0
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
workingDirectory: 'subfoldername'
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
workingDirectory: "subfoldername"
```
[Worker secrets](https://developers.cloudflare.com/workers/tooling/wrangler/secrets/) can be optionally passed as a new line deliminated string of names in `secrets`. Each secret name must match an environment variable name specified in the `env` attribute. 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:
deploy:
steps:
uses: cloudflare/wrangler-action@1.3.0
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
environment: production
secrets: |
SECRET1
SECRET2
SECRET1
SECRET2
env:
SECRET1: ${{ secrets.SECRET1 }}
SECRET2: ${{ secrets.SECRET2 }}
```
If you need to run additional shell commands before or after `wrangler publish`, you can specify them as input to `preCommands` (before publish) or `postCommands` (after publish). These can include additional `wrangler` commands (i.e. `build`, `kv:key put`) or any other commands available inside the `wrangler-action` context.
If you need to run additional shell commands before or after your command, you can specify them as input to `preCommands` (before `deploy`) or `postCommands` (after `deploy`). These can include additional `wrangler` commands (that is, `whoami`, `kv:key put`) or any other commands available inside the `wrangler-action` context.
```yaml
jobs:
deploy:
steps:
uses: cloudflare/wrangler-action@1.3.0
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
preCommands: echo "*** pre command ***"
postCommands: |
echo "*** post commands ***"
@@ -126,120 +123,274 @@ jobs:
echo "******"
```
Set the optional `publish` input to false to skip publishing your Worker project and secrets. Useful in conjunction with pre and post commands. For example, if you only wanted to run `wrangler build` against your project:
You can use the `command` option to do specific actions such as running `wrangler whoami` against your project:
```yaml
jobs:
deploy:
steps:
uses: cloudflare/wrangler-action@1.3.0
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
publish: false
preCommands: wrangler build
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
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
### Deploying when commits are merged to master
### Deploy when commits are merged to main
The above workflow examples have already shown how to run `wrangler-action` when new commits are merged to the master branch. For most developers, this workflow will easily replace manual deploys and be a great first integration step with `wrangler-action`:
The above workflow examples have already shown how to run `wrangler-action` when new commits are merged to the main branch. For most developers, this workflow will easily replace manual deploys and be a great first integration step with `wrangler-action`:
```yaml
on:
push:
branches:
- master
- main
jobs:
deploy:
runs-on: ubuntu-latest
name: Deploy
steps:
- uses: actions/checkout@master
- name: Publish
uses: cloudflare/wrangler-action@1.3.0
- uses: actions/checkout@v6
- name: Deploy
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
```
Note that there are a number of possible events, like `push`, that can be used to trigger a workflow. For more details on the events available, check out the [GitHub Actions documentation](https://help.github.com/en/articles/workflow-syntax-for-github-actions#on).
Note that there are a number of possible events, like `push`, that can be used to trigger a workflow. For more details on the events available, refer to the [GitHub Actions documentation](https://help.github.com/en/articles/workflow-syntax-for-github-actions#on).
### Deploy your Pages site (production & preview)
If you want to deploy your Pages project with GitHub Actions rather than the built-in continous integration (CI), then this is a great way to do it. Wrangler 2 will populate the commit message and branch for you. You only need to pass the project name. If a push to a non-production branch is done, it will deploy as a preview deployment:
```yaml
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
name: Deploy
permissions:
contents: read
deployments: write
steps:
- 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 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
If you'd like to deploy your Workers application on a recurring basis for instance, every hour, or daily the `schedule` trigger allows you to use cron syntax to define a workflow schedule. The below example will deploy at the beginning of every hour:
If you would like to deploy your Workers application on a recurring basis for example, every hour, or daily the `schedule` trigger allows you to use cron syntax to define a workflow schedule. The below example will deploy at the beginning of every hour:
```yaml
on:
schedule:
- cron: '0 * * * *'
- cron: "0 * * * *"
jobs:
deploy:
runs-on: ubuntu-latest
name: Deploy
steps:
- uses: actions/checkout@master
- name: Publish app
uses: cloudflare/wrangler-action@1.3.0
- uses: actions/checkout@v6
- name: Deploy app
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
```
If you need help defining the correct cron syntax, check out [crontab.guru](https://crontab.guru/), which provides a friendly user interface for validating your cron schedule.
### Deploying on a "dispatched" event
### Manually triggering a deployment
If you need to trigger a workflow at-will, you can use GitHub's `workflow_dispatch` [event](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#workflow_dispatch) in your workflow file. By setting your workflow to trigger on that event, you'll be able to deploy your application via the GitHub UI. The UI also accepts inputs that can be used to configure the action :
If you need to trigger a workflow at-will, you can use GitHub's `workflow_dispatch` [event](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#workflow_dispatch) in your workflow file. By setting your workflow to trigger on that event, you will be able to deploy your application via the GitHub UI. The UI also accepts inputs that can be used to configure the action:
```yaml
on:
workflow_dispatch:
inputs:
environment:
description: 'Choose an environment to deploy to: <dev|staging|prod>'
description: "Choose an environment to deploy to: <dev|staging|prod>"
required: true
default: 'dev'
default: "dev"
jobs:
deploy:
runs-on: ubuntu-latest
name: Deploy
steps:
- uses: actions/checkout@master
- name: Publish app
uses: cloudflare/wrangler-action@1.3.0
- uses: actions/checkout@v6
- name: Deploy app
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
environment: ${{ github.event.inputs.environment }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
command: deploy --env ${{ github.event.inputs.environment }}
```
For more advanced usage or to programmatically trigger the workflow from scripts, check out [the docs](https://docs.github.com/en/rest/reference/actions#create-a-workflow-dispatch-event) for making API calls
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.
## Troubleshooting
### Upload a Worker Version
This action is in beta, and I'm looking for folks to use it! If something goes wrong, please file an issue! That being said, there's a couple things you should know:
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.
### "I just started using Workers/Wrangler and I don't know what this is!"
```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
```
No problem! Check out the [Quick Start guide](https://developers.cloudflare.com/workers/quickstart) in our docs to get started. Once you have a Workers application, you may want to set it up to automatically deploy from GitHub whenever you change your project. That's where this action comes in - nice!
## Advanced Usage
### "I'm trying to deploy my static site but it isn't working!"
### Setting A Worker Secret for A Specific Environment
To deploy static sites and frontend applications to Workers, check out the documentation for [Workers Sites](https://developers.cloudflare.com/workers/sites).
There is an environment parameter that can be set within the workflow to enable this. Example:
Note that this action makes no assumptions about _how_ your project is built! **If you need to run a pre-publish step, like building your application, you need to specify a build step in your Workflow.** For instance, if I have an NPM command called `build`, my workflow YAML might resemble the following:
```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:
```yaml
- name: Deploy
id: deploy
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy --project-name=example
- name: print wrangler command output
env:
CMD_OUTPUT: ${{ steps.deploy.outputs.command-output }}
run: echo $CMD_OUTPUT
```
Now when you run your workflow, you will see the full output of the Wrangler command in your workflow logs. You can also use this output in subsequent workflow steps to parse the output for specific values.
> Note: the `command-stderr` output variable is also available if you need to parse the standard error output of the Wrangler command.
### Using the `deployment-url` and `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:
```yaml
- name: Deploy
id: deploy
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy --project-name=example
- name: print deployment-url
env:
DEPLOYMENT_URL: ${{ steps.deploy.outputs.deployment-url }}
run: echo $DEPLOYMENT_URL
```
The resulting output will look something like this:
```text
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!"
Refer to the [Quick Start guide](https://developers.cloudflare.com/workers/quickstart) to get started. Once you have a Workers application, you may want to set it up to automatically deploy from GitHub whenever you change your project.
### "[ERROR] No account id found, quitting.."
You will need to add `account_id = ""` in your `wrangler.toml` file or set `accountId` in this GitHub Action.
```yaml
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
name: Deploy
steps:
- uses: actions/checkout@master
- name: Build site
run: 'npm run build'
- name: Publish
uses: cloudflare/wrangler-action@1.3.0
- uses: actions/checkout@v6
- name: Deploy app
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
```
+44 -15
View File
@@ -2,32 +2,61 @@ name: "Deploy to Cloudflare Workers with Wrangler"
branding:
icon: "upload-cloud"
color: "orange"
description: "Deploy your Cloudflare Workers applications and sites directly from GitHub, using Wrangler"
description: "Deploy your Cloudflare projects from GitHub using Wrangler"
runs:
using: "docker"
image: "Dockerfile"
# Possible values: https://github.com/actions/runner/blob/main/src/Runner.Common/Util/NodeUtil.cs#L9
using: "node24"
main: "dist/index.mjs"
inputs:
apiKey:
description: "(Legacy) Your Cloudflare API Key"
apiToken:
description: "Your Cloudflare API Token"
email:
description: "(Legacy) Your Cloudflare Email"
required: false
accountId:
description: "Your Cloudflare Account ID"
required: false
quiet:
description: "Supresses output from Wrangler commands, defaults to `false`"
required: false
default: "false"
environment:
description: "The environment you'd like to publish your Workers project to - must be defined in wrangler.toml"
description: "The environment you'd like to deploy your Workers project to - must be defined in wrangler.toml"
workingDirectory:
description: "The relative path which Wrangler commands should be run from"
required: false
wranglerVersion:
description: "The version of Wrangler you'd like to use to publish your Workers project"
description: "The version of Wrangler you'd like to use to deploy your Workers project"
required: false
secrets:
description: "A new line deliminated string of environment variable names that should be configured as Worker secrets"
description: "A string of environment variable names, separated by newlines. These will be bound to your Worker as Secrets and must match the names of environment variables declared in `env` of this workflow."
required: false
preCommands:
description: "Commands to execute before publishing the Workers project"
description: "Commands to execute before deploying the Workers project"
required: false
postCommands:
description: "Commands to execute after publishing the Workers project"
description: "Commands to execute after deploying the Workers project"
required: false
publish:
description: "Set to false to skip publishing your Worker project and secrets. Defaults to true."
required: false
command:
description: 'The Wrangler command (along with any arguments) you wish to run. Multiple Wrangler commands can be run by separating each command with a newline. Defaults to `"deploy"`.'
required: false
vars:
description: "A string of environment variable names, separated by newlines. These will be bound to your Worker using the values of matching environment variables declared in `env` of this workflow."
required: false
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)"
command-stderr:
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"
-122
View File
@@ -1,122 +0,0 @@
#!/bin/bash
set -e
export HOME="/github/workspace"
export WRANGLER_HOME="/github/workspace"
mkdir -p "$HOME/.wrangler"
chmod -R 770 "$HOME/.wrangler"
export API_CREDENTIALS=""
# Used to execute any specified pre and post commands
execute_commands() {
COMMANDS=$1
while IFS= read -r COMMAND; do
CHUNKS=()
for CHUNK in $COMMAND; do
CHUNKS+=("$CHUNK")
done
eval "${CHUNKS[@]}"
CHUNKS=()
done <<< "$COMMANDS"
}
secret_not_found() {
echo "::error::Specified secret \"$1\" not found in environment variables."
exit 1
}
# If an API token is detected as input
if [ -n "$INPUT_APITOKEN" ]
then
export CF_API_TOKEN="$INPUT_APITOKEN"
export API_CREDENTIALS="API Token"
fi
# If an API key and email are detected as input
if [ -n "$INPUT_APIKEY" ] && [ -n "$INPUT_EMAIL" ]
then
export CF_EMAIL="$INPUT_EMAIL"
export CF_API_KEY="$INPUT_APIKEY"
export API_CREDENTIALS="Email and API Key"
fi
if [ -n "$INPUT_APIKEY" ] && [ -z "$INPUT_EMAIL" ]
then
echo "Provided an API key without an email for authentication. Please pass in 'apiKey' and 'email' to the action."
fi
if [ -z "$INPUT_APIKEY" ] && [ -n "$INPUT_EMAIL" ]
then
echo "Provided an email without an API key for authentication. Please pass in 'apiKey' and 'email' to the action."
exit 1
fi
if [ -z "$API_CREDENTIALS" ]
then
>&2 echo "Unable to find authentication details. Please pass in an 'apiToken' as an input to the action, or a legacy 'apiKey' and 'email'."
exit 1
else
echo "Using $API_CREDENTIALS authentication"
fi
# If a Wrangler version is detected as input
if [ -z "$INPUT_WRANGLERVERSION" ]
then
npm i @cloudflare/wrangler -g
else
npm i "@cloudflare/wrangler@$INPUT_WRANGLERVERSION" -g
fi
# If a working directory is detected as input
if [ -n "$INPUT_WORKINGDIRECTORY" ]
then
cd "$INPUT_WORKINGDIRECTORY"
fi
# If precommands is detected as input
if [ -n "$INPUT_PRECOMMANDS" ]
then
execute_commands "$INPUT_PRECOMMANDS"
fi
# If an environment is detected as input, for each secret specified get the value of
# the matching named environment variable then configure using wrangler secret put.
# Skip if publish is set to false.
if [ "$INPUT_PUBLISH" != "false" ]
then
if [ -z "$INPUT_ENVIRONMENT" ]
then
wrangler publish
for SECRET in $INPUT_SECRETS; do
VALUE=$(printenv "$SECRET") || secret_not_found "$SECRET"
echo "$VALUE" | wrangler secret put "$SECRET"
done
else
wrangler publish -e "$INPUT_ENVIRONMENT"
for SECRET in $INPUT_SECRETS; do
VALUE=$(printenv "$SECRET") || secret_not_found "$SECRET"
echo "$VALUE" | wrangler secret put "$SECRET" --env "$INPUT_ENVIRONMENT"
done
fi
fi
# If postcommands is detected as input
if [ -n "$INPUT_POSTCOMMANDS" ]
then
execute_commands "$INPUT_POSTCOMMANDS"
fi
# If a working directory is detected as input, revert to the
# original directory before continuing with the workflow
if [ -n "$INPUT_WORKINGDIRECTORY" ]
then
cd $HOME
fi
+3982
View File
File diff suppressed because it is too large Load Diff
+56
View File
@@ -0,0 +1,56 @@
{
"name": "wrangler-action",
"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",
"bugs": {
"url": "https://github.com/cloudflare/wrangler-action/issues"
},
"homepage": "https://github.com/cloudflare/wrangler-action#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/cloudflare/wrangler-action.git"
},
"keywords": [
"script",
"cli",
"serverless",
"cloudflare",
"cloudflare-workers",
"typescript"
],
"type": "module",
"main": "dist/index.mjs",
"scripts": {
"build": "npx ncc build ./src/index.ts && mv ./dist/index.js ./dist/index.mjs",
"test": "vitest",
"format": "prettier --write .",
"check": "prettier --check ."
},
"dependencies": {
"@actions/core": "^1.11.1",
"@actions/exec": "^1.1.1",
"@actions/github": "^6.0.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@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"
}
}
+180
View File
@@ -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;
}
}
+54
View File
@@ -0,0 +1,54 @@
import {
exec as _childProcessExec,
type ExecException,
} from "node:child_process";
import { EOL } from "node:os";
import { promisify } from "node:util";
export { exec } from "@actions/exec";
const childProcessExec = promisify(_childProcessExec);
type ExecAsyncException = ExecException & {
stderr: string;
stdout: string;
};
function isExecAsyncException(err: unknown): err is ExecAsyncException {
return err instanceof Error && "code" in err && "stderr" in err;
}
export async function execShell(
command: string,
{
silent = false,
...options
}: Parameters<typeof childProcessExec>[1] & { silent?: boolean } = {},
) {
if (!silent) {
process.stdout.write("[command]" + command + EOL);
}
try {
const promise = childProcessExec(command, {
...options,
});
const { child } = promise;
if (!silent) {
child.stdout?.on("data", (data: Buffer) => process.stdout.write(data));
child.stderr?.on("data", (data: Buffer) => process.stderr.write(data));
}
await promise;
return child.exitCode;
} catch (err) {
if (isExecAsyncException(err)) {
process.stderr.write(err.stderr);
throw new Error(`Process failed with exit code ${err.code}`);
}
throw err;
}
}
Executable
+36
View File
@@ -0,0 +1,36 @@
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 = "4";
/**
* A configuration object that contains all the inputs & immutable state for the action.
*/
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"),
CLOUDFLARE_ACCOUNT_ID: getInput("accountId"),
ENVIRONMENT: getInput("environment"),
VARS: getMultilineInput("vars"),
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,
});
main(config, packageManager);
+110
View File
@@ -0,0 +1,110 @@
import { describe, expect, test } from "vitest";
import { getPackageManager } from "./packageManagers";
describe("getPackageManager", () => {
test("should use provided value instead of inferring from lockfile", () => {
expect(
getPackageManager("npm", { workingDirectory: "src/test/fixtures/npm" }),
).toMatchInlineSnapshot(`
{
"exec": "npx",
"execNoInstall": "npx --no-install",
"install": "npm i",
}
`);
expect(
getPackageManager("yarn", { workingDirectory: "src/test/fixtures/npm" }),
).toMatchInlineSnapshot(`
{
"exec": "yarn",
"execNoInstall": "yarn",
"install": "yarn add",
}
`);
expect(
getPackageManager("pnpm", { workingDirectory: "src/test/fixtures/npm" }),
).toMatchInlineSnapshot(`
{
"exec": "pnpm exec",
"execNoInstall": "pnpm exec",
"install": "pnpm add",
}
`);
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: "src/test/fixtures/npm" }))
.toMatchInlineSnapshot(`
{
"exec": "npx",
"execNoInstall": "npx --no-install",
"install": "npm i",
}
`);
});
test("should use yarn if no value provided and yarn.lock exists", () => {
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: "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: "src/test/fixtures/bun" }))
.toMatchInlineSnapshot(`
{
"exec": "bunx",
"execNoInstall": "bun run",
"install": "bun i",
}
`);
});
test("should use npm if no value provided and no lockfile is present", () => {
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: "src/test/fixtures/npm" }),
).toThrowError();
});
});
+76
View File
@@ -0,0 +1,76 @@
import { existsSync } from "node:fs";
import * as path from "node:path";
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>>;
type PackageManagerValue = keyof typeof PACKAGE_MANAGERS;
function detectPackageManager(
workingDirectory = ".",
): PackageManagerValue | null {
if (existsSync(path.join(workingDirectory, "package-lock.json"))) {
return "npm";
}
if (existsSync(path.join(workingDirectory, "yarn.lock"))) {
return "yarn";
}
if (existsSync(path.join(workingDirectory, "pnpm-lock.yaml"))) {
return "pnpm";
}
if (
existsSync(path.join(workingDirectory, "bun.lockb")) ||
existsSync(path.join(workingDirectory, "bun.lock"))
) {
return "bun";
}
return null;
}
function assertValidPackageManagerValue(
name: string,
): asserts name is PackageManagerValue | "" {
if (name && !Object.keys(PACKAGE_MANAGERS).includes(name)) {
throw new TypeError(
`invalid value provided for "packageManager": ${name}
Value must be one of: [${Object.keys(PACKAGE_MANAGERS).join(", ")}]`,
);
}
}
export function getPackageManager(
name: string,
{ workingDirectory = "." }: { workingDirectory?: string } = {},
) {
assertValidPackageManagerValue(name);
return PACKAGE_MANAGERS[
name || detectPackageManager(workingDirectory) || "npm"
];
}
+59
View File
@@ -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 |
"
`);
});
});
+128
View File
@@ -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");
}
}
}
+25
View File
@@ -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
View File
@@ -0,0 +1,10 @@
{
"name": "wrangler-action-test",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wrangler-action-test"
}
}
}
+5
View File
@@ -0,0 +1,5 @@
{
"name": "wrangler-action-test",
"license": "MIT",
"private": true
}
+4
View File
@@ -0,0 +1,4 @@
name = "wrangler-action-test-build-quiet"
main = "./index.ts"
compatibility_date = "2023-07-07"
workers_dev = true
View File
+25
View File
@@ -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),
});
},
};
+5
View File
@@ -0,0 +1,5 @@
{
"name": "wrangler-action-bun-test",
"license": "MIT",
"private": true
}
+4
View File
@@ -0,0 +1,4 @@
name = "wrangler-action-test"
main = "./index.ts"
compatibility_date = "2023-07-07"
workers_dev = true
+20
View File
@@ -0,0 +1,20 @@
type Env = {
SECRET1?: string;
SECRET2?: string;
};
export default {
fetch(request: Request, env: Env) {
const url = new URL(request.url);
if (url.pathname === "/secret") {
const { SECRET1 = "", SECRET2 = "" } = env;
return new Response(`${SECRET1} ${SECRET2}`);
}
return Response.json({
...request,
headers: Object.fromEntries(request.headers),
});
},
};
+10
View File
@@ -0,0 +1,10 @@
{
"name": "wrangler-action-environment-test",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wrangler-action-environment-test"
}
}
}
+5
View File
@@ -0,0 +1,5 @@
{
"name": "wrangler-action-environment-test",
"license": "MIT",
"private": true
}
+9
View File
@@ -0,0 +1,9 @@
name = "wrangler-action-environment-test"
main = "./index.ts"
compatibility_date = "2023-07-07"
workers_dev = true
[env.dev]
name = "wrangler-action-dev-environment-test"
[env.dev.vars]
DEV_VAR = "example_production_token"
+25
View File
@@ -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
View File
@@ -0,0 +1,10 @@
{
"name": "wrangler-action-npm-test",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wrangler-action-npm-test"
}
}
}
+3
View File
@@ -0,0 +1,3 @@
{
"name": "wrangler-action-npm-test"
}
+4
View File
@@ -0,0 +1,4 @@
name = "wrangler-action-test"
main = "./index.ts"
compatibility_date = "2023-07-07"
workers_dev = true
+25
View File
@@ -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
View File
@@ -0,0 +1,10 @@
{
"name": "wrangler-action-test",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wrangler-action-test"
}
}
}
+5
View File
@@ -0,0 +1,5 @@
{
"name": "wrangler-action-test",
"license": "MIT",
"private": true
}
+4
View File
@@ -0,0 +1,4 @@
name = "wrangler-action-test-only-build"
main = "./index.ts"
compatibility_date = "2023-07-07"
workers_dev = true
+25
View File
@@ -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),
});
},
};
+5
View File
@@ -0,0 +1,5 @@
{
"name": "wrangler-action-pnpm-test",
"license": "MIT",
"private": true
}
+5
View File
@@ -0,0 +1,5 @@
lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
+4
View File
@@ -0,0 +1,4 @@
name = "wrangler-action-test"
main = "./index.ts"
compatibility_date = "2023-07-07"
workers_dev = true
+25
View File
@@ -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,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
View File
@@ -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"
}
}
}
+8
View File
@@ -0,0 +1,8 @@
{
"name": "wrangler-action-pre-installed-wrangler-test",
"license": "MIT",
"private": true,
"devDependencies": {
"wrangler": "file:mock_packages/wrangler"
}
}
+25
View File
@@ -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
View File
@@ -0,0 +1,10 @@
{
"name": "wrangler-action-test",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wrangler-action-test"
}
}
}
+5
View File
@@ -0,0 +1,5 @@
{
"name": "wrangler-action-test",
"license": "MIT",
"private": true
}
+4
View File
@@ -0,0 +1,4 @@
name = "wrangler-action-test-secrets-default"
main = "./index.ts"
compatibility_date = "2023-07-07"
workers_dev = true
+25
View File
@@ -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
View File
@@ -0,0 +1,10 @@
{
"name": "wrangler-action-test",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wrangler-action-test"
}
}
}
+5
View File
@@ -0,0 +1,5 @@
{
"name": "wrangler-action-test",
"license": "MIT",
"private": true
}
+4
View File
@@ -0,0 +1,4 @@
name = "wrangler-action-test-secrets-v2"
main = "./index.ts"
compatibility_date = "2023-07-07"
workers_dev = true
+1
View File
@@ -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-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
+25
View File
@@ -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),
});
},
};
+5
View File
@@ -0,0 +1,5 @@
{
"name": "wrangler-action-yarn-test",
"license": "MIT",
"private": true
}
+4
View File
@@ -0,0 +1,4 @@
name = "wrangler-action-test"
main = "./index.ts"
compatibility_date = "2023-07-07"
workers_dev = true
+4
View File
@@ -0,0 +1,4 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
+34
View File
@@ -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);
},
),
],
};
}
+26
View File
@@ -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,
);
}
+59
View File
@@ -0,0 +1,59 @@
import path from "node:path";
import { describe, expect, test } from "vitest";
import { checkWorkingDirectory, semverCompare } from "./utils";
describe("semverCompare", () => {
test("should return false if the second argument is equal to the first argument", () => {
const isVersion1LessThanVersion2 = semverCompare("1.2.3", "1.2.3");
expect(isVersion1LessThanVersion2).toBe(false);
});
test("should return true if the first argument is less than the second argument", () => {
const isVersion1LessThanVersion2 = semverCompare("1.2.2", "1.2.3");
expect(isVersion1LessThanVersion2).toBe(true);
});
});
describe("checkWorkingDirectory", () => {
test("should return the normalized path if the directory exists", () => {
const normalizedPath = checkWorkingDirectory(".");
expect(normalizedPath).toBe(path.normalize("."));
});
test("should fail if the directory does not exist", () => {
expect(() =>
checkWorkingDirectory("/does/not/exist"),
).toThrowErrorMatchingInlineSnapshot(
`[Error: Directory /does/not/exist does not exist.]`,
);
});
});
describe("semverCompare", () => {
test.each([
["1.2.3", "1.2.3", false],
["1.2.2", "1.2.3", true],
["2.0.0", "3.0.0", true],
["3.1.0", "3.1.1", true],
["3.1.0", "3.5.0", true],
["3.1.0", "3.10.0", true],
["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],
])(
"should semver compare %s vs %s correctly, expecting %s",
(version1, version2, expected) => {
const isVersion1LessThanVersion2 = semverCompare(version1, version2);
expect(isVersion1LessThanVersion2).toBe(expected);
},
);
});
+57
View File
@@ -0,0 +1,57 @@
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.
*/
export function semverCompare(version1: string, version2: string) {
if (version2 === "latest") return true;
return semverGt(version2, version1);
}
export function checkWorkingDirectory(workingDirectory = ".") {
const normalizedPath = path.normalize(workingDirectory);
if (existsSync(normalizedPath)) {
return normalizedPath;
} else {
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);
}
}
+512
View File
@@ -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();
});
});
+420
View File
@@ -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,
};
+183
View File
@@ -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",
});
});
});
});
});
+122
View File
@@ -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
View File
@@ -1 +0,0 @@
dist
-6
View File
@@ -1,6 +0,0 @@
<h1>My Static Site Test</h1>
<p>This is the content of my site</p>
<footer>
And this is my footer
</footer>
-88
View File
@@ -1,88 +0,0 @@
import { getAssetFromKV, mapRequestToAsset } from '@cloudflare/kv-asset-handler'
/**
* The DEBUG flag will do two things that help during development:
* 1. we will skip caching on the edge, which makes it easier to
* debug.
* 2. we will return an error message on exception in your response rather
* than the default 404.html page.
*/
const DEBUG = false
addEventListener('fetch', event => {
try {
event.respondWith(handleEvent(event))
} catch (e) {
if (DEBUG) {
return event.respondWith(
new Response(e.message || e.toString(), {
status: 500,
}),
)
}
event.respondWith(new Response('Internal Error', { status: 500 }))
}
})
async function handleEvent(event) {
const url = new URL(event.request.url)
let options = {}
/**
* You can add custom logic to how we fetch your assets
* by configuring the function `mapRequestToAsset`
*/
// options.mapRequestToAsset = handlePrefix(/^\/docs/)
// Path to test secrets passed through Wrangler Action. Create SECRET1 and SECRET2 secrets
// in the Action repo to something innocuous like "Hello" and "World!".
if (url.pathname === "/secret") {
let sec1 = (typeof SECRET1 !== 'undefined') ? SECRET1 : ""
let sec2 = (typeof SECRET2 !== 'undefined') ? SECRET2 : ""
return new Response(`${sec1} ${sec2}`)
}
try {
if (DEBUG) {
// customize caching
options.cacheControl = {
bypassCache: true,
}
}
return await getAssetFromKV(event, options)
} catch (e) {
// if an error is thrown try to serve the asset at 404.html
if (!DEBUG) {
try {
let notFoundResponse = await getAssetFromKV(event, {
mapRequestToAsset: req => new Request(`${new URL(req.url).origin}/404.html`, req),
})
return new Response(notFoundResponse.body, { ...notFoundResponse, status: 404 })
} catch (e) {}
}
return new Response(e.message || e.toString(), { status: 500 })
}
}
/**
* Here's one example of how to modify a request to
* remove a specific prefix, in this case `/docs` from
* the url. This can be useful if you are deploying to a
* route on a zone, or if you only want your static content
* to exist at a specific path.
*/
function handlePrefix(prefix) {
return request => {
// compute the default (e.g. / -> index.html)
let defaultAssetKey = mapRequestToAsset(request)
let url = new URL(defaultAssetKey.url)
// strip the prefix from the path for lookup
url.pathname = url.pathname.replace(prefix, '/')
// inherit all other props from the default request
return new Request(url.toString(), defaultAssetKey)
}
}
-21
View File
@@ -1,21 +0,0 @@
{
"name": "worker",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@cloudflare/kv-asset-handler": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.0.5.tgz",
"integrity": "sha512-7yLMAUZD1XQNKzmktYCcUUPB+wXmQENv1MMi8QEMs0rzL01e0XEyCUUDauRXHzxi7dBbSUGA5RS23h890ncKog==",
"requires": {
"mime": "^2.4.4"
}
},
"mime": {
"version": "2.4.4",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
"integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA=="
}
}
}
-12
View File
@@ -1,12 +0,0 @@
{
"private": true,
"name": "worker",
"version": "1.0.0",
"description": "A template for kick starting a Cloudflare Workers project",
"main": "index.js",
"author": "Ashley Lewis <ashleymichal@gmail.com>",
"license": "MIT",
"dependencies": {
"@cloudflare/kv-asset-handler": "^0.0.5"
}
}
-11
View File
@@ -1,11 +0,0 @@
name = "static-test"
type = "webpack"
workers_dev = true
account_id = "dc56444c4c955a1653106ccf997c1067"
[env.production]
name = "static-test-prod"
[site]
bucket = "./public"
entry-point = "workers-site"
+20
View File
@@ -0,0 +1,20 @@
{
"files": ["./src/index.ts"],
"compilerOptions": {
"strict": true,
"composite": true,
"incremental": true,
"esModuleInterop": true,
"skipLibCheck": true,
"isolatedModules": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler",
"rootDir": "./src",
"outDir": "./dist",
"lib": ["ESNext"],
"types": ["node", "@cloudflare/workers-types"]
},
"exclude": ["node_modules"],
"include": ["src"]
}