Compare commits

..

1 Commits

Author SHA1 Message Date
Maximo Guk 9fde81df51 test branch 2025-02-09 17:11:11 -06:00
47 changed files with 38794 additions and 2146 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"wrangler-action": patch
---
Handle failures in createGitHubDeployment and createGitHubJobSummary
+5
View File
@@ -0,0 +1,5 @@
---
"wrangler-action": minor
---
Use wrangler outputs for version upload and wrangler deploy
+7 -6
View File
@@ -9,12 +9,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v5
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v5
uses: actions/setup-node@v3
with:
node-version: "24"
# Pinned due to compatibility issues on 23.2.0
node-version: "22"
cache: "npm"
- name: Install modules and build
@@ -75,7 +76,7 @@ jobs:
SECRET2: ${{ secrets.SECRET2 }}
- name: Health Check Deployed Worker
run: node .github/workflows/workerHealthCheck.ts wrangler-action-test-secrets-v2
run: node .github/workflows/workerHealthCheck.cjs wrangler-action-test-secrets-v2
shell: bash
- name: Deploy app secrets w/ default version
@@ -92,7 +93,7 @@ jobs:
SECRET2: ${{ secrets.SECRET2 }}
- name: Health Check Deployed Worker
run: node .github/workflows/workerHealthCheck.ts wrangler-action-test-secrets-default
run: node .github/workflows/workerHealthCheck.cjs wrangler-action-test-secrets-default
shell: bash
- name: Clean Up Deployed Workers
@@ -142,7 +143,7 @@ jobs:
command: deploy --dry-run
- name: Install pnpm
run: npm i -g pnpm@10
run: npm i -g pnpm
- name: Support pnpm package manager
uses: ./
+4 -3
View File
@@ -19,15 +19,16 @@ jobs:
issues: read
steps:
- name: Checkout Repo
uses: actions/checkout@v5
uses: actions/checkout@v3
with:
fetch-depth: 0
fetch-tags: true
- name: Setup Node.js
uses: actions/setup-node@v5
uses: actions/setup-node@v3
with:
node-version: "24"
# Pinned due to compatibility issues on 23.2.0
node-version: "22"
cache: "npm"
- name: Install modules
+17 -23
View File
@@ -1,30 +1,24 @@
name: Semgrep OSS scan
on:
pull_request: {}
push:
branches: [main, master]
workflow_dispatch: {}
push:
branches:
- main
- master
schedule:
- cron: "0 0 20 * *"
concurrency:
group: semgrep-${{ github.event_name }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
- cron: "0 0 * * *"
name: Semgrep config
jobs:
semgrep:
name: semgrep-oss
runs-on: ubuntu-slim
name: semgrep/ci
runs-on: ubuntu-latest
env:
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
SEMGREP_URL: https://cloudflare.semgrep.dev
SEMGREP_APP_URL: https://cloudflare.semgrep.dev
SEMGREP_VERSION_CHECK_URL: https://cloudflare.semgrep.dev/api/check-version
container:
image: semgrep/semgrep
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
- uses: actions/checkout@v4
- run: semgrep ci
+28
View File
@@ -0,0 +1,28 @@
const { execSync } = require("child_process");
function workerHealthCheck(workerName) {
const url = `https://${workerName}.devprod-testing7928.workers.dev/secret-health-check`;
const buffer = execSync(`curl ${url}`);
const response = buffer.toString();
if (response.includes("OK")) {
console.log(`Status: Worker is up! Response: ${response}`);
} else {
throw new Error(`Worker is down! Response: ${response}`);
}
return response;
}
const args = Array.from(process.argv);
const workerName = args.pop();
if (!workerName) {
throw new Error(
"Please provide the worker name as an argument when calling this program.",
);
}
workerHealthCheck(workerName);
-41
View File
@@ -1,41 +0,0 @@
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);
},
});
-1
View File
@@ -1,4 +1,3 @@
dist
.idea
.vscode
+1 -28
View File
@@ -1,33 +1,5 @@
# 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
@@ -125,6 +97,7 @@
### 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`)
+9 -22
View File
@@ -2,17 +2,6 @@
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.
@@ -38,7 +27,7 @@ jobs:
runs-on: ubuntu-latest
name: Deploy
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Deploy
uses: cloudflare/wrangler-action@v3
with:
@@ -63,7 +52,7 @@ jobs:
## Configuration
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`.
If you need to install a specific version of Wrangler to use for deployment, you can also pass the input `wranglerVersion` to install a specific version of Wrangler from NPM. This should be a [SemVer](https://semver.org/)-style version number, such as `2.20.0`:
```yaml
jobs:
@@ -72,11 +61,9 @@ jobs:
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
wranglerVersion: "4"
wranglerVersion: "2.20.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.
```yaml
@@ -166,7 +153,7 @@ jobs:
runs-on: ubuntu-latest
name: Deploy
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Deploy
uses: cloudflare/wrangler-action@v3
with:
@@ -190,7 +177,7 @@ jobs:
contents: read
deployments: write
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Deploy
uses: cloudflare/wrangler-action@v3
with:
@@ -215,7 +202,7 @@ jobs:
runs-on: ubuntu-latest
name: Deploy
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Deploy app
uses: cloudflare/wrangler-action@v3
with:
@@ -241,7 +228,7 @@ jobs:
runs-on: ubuntu-latest
name: Deploy
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Deploy app
uses: cloudflare/wrangler-action@v3
with:
@@ -261,7 +248,7 @@ jobs:
runs-on: ubuntu-latest
name: Deploy
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Upload Worker Version
uses: cloudflare/wrangler-action@v3
with:
@@ -387,7 +374,7 @@ jobs:
runs-on: ubuntu-latest
name: Deploy
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Deploy app
uses: cloudflare/wrangler-action@v3
with:
+1 -1
View File
@@ -5,7 +5,7 @@ branding:
description: "Deploy your Cloudflare projects from GitHub using Wrangler"
runs:
# Possible values: https://github.com/actions/runner/blob/main/src/Runner.Common/Util/NodeUtil.cs#L9
using: "node24"
using: "node20"
main: "dist/index.mjs"
inputs:
apiToken:
+2
View File
@@ -0,0 +1,2 @@
import { WranglerActionConfig } from "./wranglerAction";
export declare function handleCommandOutputParsing(config: WranglerActionConfig, command: string, stdOut: string): Promise<void>;
+6
View File
@@ -0,0 +1,6 @@
import { exec as _childProcessExec } from "node:child_process";
export { exec } from "@actions/exec";
declare const childProcessExec: typeof _childProcessExec.__promisify__;
export declare function execShell(command: string, { silent, ...options }?: Parameters<typeof childProcessExec>[1] & {
silent?: boolean;
}): Promise<number | null>;
+1
View File
@@ -0,0 +1 @@
export {};
+37332
View File
File diff suppressed because one or more lines are too long
+3
View File
@@ -0,0 +1,3 @@
{
"type": "module"
}
+24
View File
@@ -0,0 +1,24 @@
export interface PackageManager {
install: string;
exec: string;
execNoInstall: string;
}
export declare function getPackageManager(name: string, { workingDirectory }?: {
workingDirectory?: string;
}): {
readonly install: "npm i";
readonly exec: "npx";
readonly execNoInstall: "npx --no-install";
} | {
readonly install: "yarn add";
readonly exec: "yarn";
readonly execNoInstall: "yarn";
} | {
readonly install: "pnpm add";
readonly exec: "pnpm exec";
readonly execNoInstall: "pnpm exec";
} | {
readonly install: "bun i";
readonly exec: "bunx";
readonly execNoInstall: "bun run";
};
+1
View File
@@ -0,0 +1 @@
export {};
+23
View File
@@ -0,0 +1,23 @@
import { getOctokit } from "@actions/github";
import { OutputEntryPagesDeployment } from "../wranglerArtifactManager";
import { WranglerActionConfig } from "../wranglerAction";
type Octokit = ReturnType<typeof getOctokit>;
export declare function createGitHubDeployment({ config, octokit, productionBranch, environment, deploymentId, projectName, deploymentUrl, }: {
config: WranglerActionConfig;
octokit: Octokit;
productionBranch: string;
environment: string;
deploymentId: string | null;
projectName: string;
deploymentUrl?: string;
}): Promise<void>;
export declare function createJobSummary({ commitHash, deploymentUrl, aliasUrl, }: {
commitHash: string;
deploymentUrl?: string;
aliasUrl?: string;
}): Promise<void>;
/**
* Create github deployment, if GITHUB_TOKEN is present in config
*/
export declare function createGitHubDeploymentAndJobSummary(config: WranglerActionConfig, pagesArtifactFields: OutputEntryPagesDeployment): Promise<void>;
export {};
+1
View File
@@ -0,0 +1 @@
export {};
+8
View File
@@ -0,0 +1,8 @@
type Env = {
SECRET1?: string;
SECRET2?: string;
};
declare const _default: {
fetch(request: Request, env: Env): Response;
};
export default _default;
+8
View File
@@ -0,0 +1,8 @@
type Env = {
SECRET1?: string;
SECRET2?: string;
};
declare const _default: {
fetch(request: Request, env: Env): Response;
};
export default _default;
+8
View File
@@ -0,0 +1,8 @@
type Env = {
SECRET1?: string;
SECRET2?: string;
};
declare const _default: {
fetch(request: Request, env: Env): Response;
};
export default _default;
+8
View File
@@ -0,0 +1,8 @@
type Env = {
SECRET1?: string;
SECRET2?: string;
};
declare const _default: {
fetch(request: Request, env: Env): Response;
};
export default _default;
+8
View File
@@ -0,0 +1,8 @@
type Env = {
SECRET1?: string;
SECRET2?: string;
};
declare const _default: {
fetch(request: Request, env: Env): Response;
};
export default _default;
+8
View File
@@ -0,0 +1,8 @@
type Env = {
SECRET1?: string;
SECRET2?: string;
};
declare const _default: {
fetch(request: Request, env: Env): Response;
};
export default _default;
+8
View File
@@ -0,0 +1,8 @@
type Env = {
SECRET1?: string;
SECRET2?: string;
};
declare const _default: {
fetch(request: Request, env: Env): Response;
};
export default _default;
+8
View File
@@ -0,0 +1,8 @@
type Env = {
SECRET1?: string;
SECRET2?: string;
};
declare const _default: {
fetch(request: Request, env: Env): Response;
};
export default _default;
+8
View File
@@ -0,0 +1,8 @@
type Env = {
SECRET1?: string;
SECRET2?: string;
};
declare const _default: {
fetch(request: Request, env: Env): Response;
};
export default _default;
+2
View File
@@ -0,0 +1,2 @@
declare const _default: {};
export default _default;
@@ -0,0 +1,2 @@
declare const _default: {};
export default _default;
+8
View File
@@ -0,0 +1,8 @@
type Env = {
SECRET1?: string;
SECRET2?: string;
};
declare const _default: {
fetch(request: Request, env: Env): Response;
};
export default _default;
+6
View File
@@ -0,0 +1,6 @@
export declare function mockGithubDeployments({ githubUser, githubRepoName, }: {
githubUser: string;
githubRepoName: string;
}): {
handlers: import("msw").HttpHandler[];
};
+4
View File
@@ -0,0 +1,4 @@
import { WranglerActionConfig } from "../wranglerAction";
export declare function getTestConfig({ config, }?: {
config?: Partial<WranglerActionConfig>;
}): WranglerActionConfig;
+9
View File
@@ -0,0 +1,9 @@
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 declare function semverCompare(version1: string, version2: string): boolean;
export declare function checkWorkingDirectory(workingDirectory?: string): string;
export declare function info(config: WranglerActionConfig, message: string, bypass?: boolean): void;
export declare function warn(config: WranglerActionConfig, message: string, bypass?: boolean): void;
export declare function error(config: WranglerActionConfig, message: string, bypass?: boolean): void;
+1
View File
@@ -0,0 +1 @@
export {};
+54
View File
@@ -0,0 +1,54 @@
import { z } from "zod";
import { PackageManager } from "./packageManagers";
import { info } from "./utils";
export type WranglerActionConfig = z.infer<typeof wranglerActionConfig>;
export declare const wranglerActionConfig: z.ZodObject<{
WRANGLER_VERSION: z.ZodString;
didUserProvideWranglerVersion: z.ZodBoolean;
secrets: z.ZodArray<z.ZodString, "many">;
workingDirectory: z.ZodString;
CLOUDFLARE_API_TOKEN: z.ZodString;
CLOUDFLARE_ACCOUNT_ID: z.ZodString;
ENVIRONMENT: z.ZodString;
VARS: z.ZodArray<z.ZodString, "many">;
COMMANDS: z.ZodArray<z.ZodString, "many">;
QUIET_MODE: z.ZodBoolean;
PACKAGE_MANAGER: z.ZodString;
WRANGLER_OUTPUT_DIR: z.ZodString;
GITHUB_TOKEN: z.ZodString;
}, "strip", z.ZodTypeAny, {
WRANGLER_VERSION: string;
didUserProvideWranglerVersion: boolean;
secrets: string[];
workingDirectory: string;
CLOUDFLARE_API_TOKEN: string;
CLOUDFLARE_ACCOUNT_ID: string;
ENVIRONMENT: string;
VARS: string[];
COMMANDS: string[];
QUIET_MODE: boolean;
PACKAGE_MANAGER: string;
WRANGLER_OUTPUT_DIR: string;
GITHUB_TOKEN: string;
}, {
WRANGLER_VERSION: string;
didUserProvideWranglerVersion: boolean;
secrets: string[];
workingDirectory: string;
CLOUDFLARE_API_TOKEN: string;
CLOUDFLARE_ACCOUNT_ID: string;
ENVIRONMENT: string;
VARS: string[];
COMMANDS: string[];
QUIET_MODE: boolean;
PACKAGE_MANAGER: string;
WRANGLER_OUTPUT_DIR: string;
GITHUB_TOKEN: string;
}>;
declare function main(config: WranglerActionConfig, packageManager: PackageManager): Promise<void>;
declare function installWrangler(config: WranglerActionConfig, packageManager: PackageManager): Promise<void>;
declare function authenticationSetup(config: WranglerActionConfig): void;
declare function execCommands(config: WranglerActionConfig, packageManager: PackageManager, commands: string[], cmdType: string): Promise<void>;
declare function uploadSecrets(config: WranglerActionConfig, packageManager: PackageManager): Promise<void>;
declare function wranglerCommands(config: WranglerActionConfig, packageManager: PackageManager): Promise<void>;
export { authenticationSetup, execCommands, info, installWrangler, main, uploadSecrets, wranglerCommands, };
+1
View File
@@ -0,0 +1 @@
export {};
+200
View File
@@ -0,0 +1,200 @@
import { z } from "zod";
export type OutputEntryPagesDeployment = z.infer<typeof OutputEntryPagesDeployment>;
declare const OutputEntryPagesDeployment: z.ZodObject<z.objectUtil.extendShape<{
version: z.ZodLiteral<1>;
type: z.ZodString;
}, {
type: z.ZodLiteral<"pages-deploy-detailed">;
pages_project: z.ZodNullable<z.ZodString>;
deployment_id: z.ZodNullable<z.ZodString>;
url: z.ZodOptional<z.ZodString>;
alias: z.ZodOptional<z.ZodString>;
environment: z.ZodEnum<["production", "preview"]>;
production_branch: z.ZodOptional<z.ZodString>;
deployment_trigger: z.ZodOptional<z.ZodObject<{
metadata: z.ZodObject<{
/** Commit hash of the deployment trigger metadata for the pages project */
commit_hash: z.ZodString;
}, "strip", z.ZodTypeAny, {
commit_hash: string;
}, {
commit_hash: string;
}>;
}, "strip", z.ZodTypeAny, {
metadata: {
commit_hash: string;
};
}, {
metadata: {
commit_hash: string;
};
}>>;
}>, "strip", z.ZodTypeAny, {
type: "pages-deploy-detailed";
environment: "production" | "preview";
version: 1;
pages_project: string | null;
deployment_id: string | null;
url?: string | undefined;
alias?: string | undefined;
production_branch?: string | undefined;
deployment_trigger?: {
metadata: {
commit_hash: string;
};
} | undefined;
}, {
type: "pages-deploy-detailed";
environment: "production" | "preview";
version: 1;
pages_project: string | null;
deployment_id: string | null;
url?: string | undefined;
alias?: string | undefined;
production_branch?: string | undefined;
deployment_trigger?: {
metadata: {
commit_hash: string;
};
} | undefined;
}>;
export type OutputEntryDeployment = z.infer<typeof OutputEntryDeployment>;
declare const OutputEntryDeployment: z.ZodObject<z.objectUtil.extendShape<{
version: z.ZodLiteral<1>;
type: z.ZodString;
}, {
type: z.ZodLiteral<"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.ZodOptional<z.ZodArray<z.ZodString, "many">>;
}>, "strip", z.ZodTypeAny, {
type: "deploy";
version: 1;
targets?: string[] | undefined;
}, {
type: "deploy";
version: 1;
targets?: string[] | undefined;
}>;
export type OutputEntryVersionUpload = z.infer<typeof OutputEntryVersionUpload>;
declare const OutputEntryVersionUpload: z.ZodObject<z.objectUtil.extendShape<{
version: z.ZodLiteral<1>;
type: z.ZodString;
}, {
type: z.ZodLiteral<"version-upload">;
/** The preview URL associated with this version upload */
preview_url: z.ZodOptional<z.ZodString>;
}>, "strip", z.ZodTypeAny, {
type: "version-upload";
version: 1;
preview_url?: string | undefined;
}, {
type: "version-upload";
version: 1;
preview_url?: string | undefined;
}>;
export type SupportedOutputEntry = z.infer<typeof SupportedOutputEntry>;
declare const SupportedOutputEntry: z.ZodDiscriminatedUnion<"type", [z.ZodObject<z.objectUtil.extendShape<{
version: z.ZodLiteral<1>;
type: z.ZodString;
}, {
type: z.ZodLiteral<"pages-deploy-detailed">;
pages_project: z.ZodNullable<z.ZodString>;
deployment_id: z.ZodNullable<z.ZodString>;
url: z.ZodOptional<z.ZodString>;
alias: z.ZodOptional<z.ZodString>;
environment: z.ZodEnum<["production", "preview"]>;
production_branch: z.ZodOptional<z.ZodString>;
deployment_trigger: z.ZodOptional<z.ZodObject<{
metadata: z.ZodObject<{
/** Commit hash of the deployment trigger metadata for the pages project */
commit_hash: z.ZodString;
}, "strip", z.ZodTypeAny, {
commit_hash: string;
}, {
commit_hash: string;
}>;
}, "strip", z.ZodTypeAny, {
metadata: {
commit_hash: string;
};
}, {
metadata: {
commit_hash: string;
};
}>>;
}>, "strip", z.ZodTypeAny, {
type: "pages-deploy-detailed";
environment: "production" | "preview";
version: 1;
pages_project: string | null;
deployment_id: string | null;
url?: string | undefined;
alias?: string | undefined;
production_branch?: string | undefined;
deployment_trigger?: {
metadata: {
commit_hash: string;
};
} | undefined;
}, {
type: "pages-deploy-detailed";
environment: "production" | "preview";
version: 1;
pages_project: string | null;
deployment_id: string | null;
url?: string | undefined;
alias?: string | undefined;
production_branch?: string | undefined;
deployment_trigger?: {
metadata: {
commit_hash: string;
};
} | undefined;
}>, z.ZodObject<z.objectUtil.extendShape<{
version: z.ZodLiteral<1>;
type: z.ZodString;
}, {
type: z.ZodLiteral<"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.ZodOptional<z.ZodArray<z.ZodString, "many">>;
}>, "strip", z.ZodTypeAny, {
type: "deploy";
version: 1;
targets?: string[] | undefined;
}, {
type: "deploy";
version: 1;
targets?: string[] | undefined;
}>, z.ZodObject<z.objectUtil.extendShape<{
version: z.ZodLiteral<1>;
type: z.ZodString;
}, {
type: z.ZodLiteral<"version-upload">;
/** The preview URL associated with this version upload */
preview_url: z.ZodOptional<z.ZodString>;
}>, "strip", z.ZodTypeAny, {
type: "version-upload";
version: 1;
preview_url?: string | undefined;
}, {
type: "version-upload";
version: 1;
preview_url?: string | undefined;
}>]>;
/**
* Parses file names in a directory to find wrangler artifact files
*
* @param artifactDirectory
* @returns All artifact files from the directory
*/
export declare function getWranglerArtifacts(artifactDirectory: string): Promise<string[]>;
/**
* Searches for a supported wrangler OutputEntry
*
* @param artifactDirectory
* @returns The first SupportedOutputEntry found within a wrangler artifact directory
*/
export declare function getOutputEntry(artifactDirectory: string): Promise<SupportedOutputEntry | null>;
export {};
+1
View File
@@ -0,0 +1 @@
export {};
+915 -1484
View File
File diff suppressed because it is too large Load Diff
+2 -6
View File
@@ -1,6 +1,6 @@
{
"name": "wrangler-action",
"version": "4.0.0",
"version": "3.13.1",
"description": "GitHub Action to use [Wrangler](https://developers.cloudflare.com/workers/cli-wrangler/).",
"author": "wrangler@cloudflare.com",
"license": "MIT OR Apache-2.0",
@@ -42,15 +42,11 @@
"@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"
"vitest": "^2.1.9"
}
}
+1 -1
View File
@@ -5,7 +5,7 @@ import { getPackageManager } from "./packageManagers";
import { checkWorkingDirectory } from "./utils";
import { main, WranglerActionConfig } from "./wranglerAction";
const DEFAULT_WRANGLER_VERSION = "4";
const DEFAULT_WRANGLER_VERSION = "3.90.0";
/**
* A configuration object that contains all the inputs & immutable state for the action.
+1 -1
View File
@@ -7,7 +7,7 @@ export function getTestConfig({
} = {}): WranglerActionConfig {
return Object.assign(
{
WRANGLER_VERSION: "4.72.0",
WRANGLER_VERSION: "3.81.0",
didUserProvideWranglerVersion: false,
secrets: [],
workingDirectory: "/src/test/fixtures",
-4
View File
@@ -42,10 +42,6 @@ describe("semverCompare", () => {
["3.1.0", "3.15.0", true],
["3.10.0", "3.1.0", false],
["3.20.0", "3.2.0", false],
["3.1.0", "4.0.0", true],
["2.20.0", "4.72.0", true],
["3.4.0", "4.72.0", true],
["3.60.0", "4.72.0", true],
["3.1.0", "latest", true],
["4.0.0", "latest", true],
])(
+5 -439
View File
@@ -1,59 +1,9 @@
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 { installWrangler } 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",
@@ -80,14 +30,10 @@ describe("installWrangler", () => {
};
});
const infoSpy = vi.spyOn(core, "info");
const resolvedVersion = await installWrangler(
testConfig,
testPackageManager,
);
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 () => {
@@ -105,14 +51,9 @@ describe("installWrangler", () => {
};
});
const infoSpy = vi.spyOn(core, "info");
const resolvedVersion = await installWrangler(
testConfig,
testPackageManager,
);
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: {
@@ -120,393 +61,18 @@ describe("installWrangler", () => {
didUserProvideWranglerVersion: true,
},
});
let callCount = 0;
vi.spyOn(exec, "getExecOutput").mockImplementation(async () => {
callCount++;
if (callCount === 1) {
// Pre-install check: different version installed
return {
exitCode: 0,
stderr: "",
stdout: ` ⛅️ wrangler 3.20.0 (update available 3.53.1)`,
};
}
// Post-install check: correct version now installed
return {
exitCode: 0,
stderr: "",
stdout: ` ⛅️ wrangler 3.48.0`,
stdout: ` ⛅️ wrangler 3.20.0 (update available 3.53.1)`,
};
});
vi.spyOn(exec, "exec").mockImplementation(async () => {
return 0;
});
const infoSpy = vi.spyOn(core, "info");
const resolvedVersion = await installWrangler(
testConfig,
testPackageManager,
);
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();
});
});
+40 -86
View File
@@ -7,14 +7,12 @@ import {
setOutput,
} from "@actions/core";
import { getExecOutput } from "@actions/exec";
import semverSatisfies from "semver/functions/satisfies";
import semverValid from "semver/functions/valid";
import semverEq from "semver/functions/eq";
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({
@@ -52,60 +50,32 @@ async function main(
try {
wranglerActionConfig.parse(config);
authenticationSetup(config);
const resolvedVersion = await installWrangler(config, packageManager);
const resolvedConfig = { ...config, WRANGLER_VERSION: resolvedVersion };
await installWrangler(config, packageManager);
await execCommands(
resolvedConfig,
config,
packageManager,
getMultilineInput("preCommands"),
"pre",
);
await uploadSecrets(resolvedConfig, packageManager);
await wranglerCommands(resolvedConfig, packageManager);
await uploadSecrets(config, packageManager);
await wranglerCommands(config, packageManager);
await execCommands(
resolvedConfig,
config,
packageManager,
getMultilineInput("postCommands"),
"post",
);
info(resolvedConfig, "🏁 Wrangler Action completed", true);
info(config, "🏁 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`,
@@ -114,25 +84,33 @@ async function installWrangler(
startGroup(config, "🔍 Checking for existing Wrangler installation");
let installedVersion = "";
let versionSatisfied = false;
let installedVersionSatisfiesRequirement = false;
try {
installedVersion = await resolveInstalledVersion(config, packageManager);
const { stdout } = await getExecOutput(
// We want to simply invoke wrangler to check if it's installed, but don't want to auto-install it at this stage
packageManager.execNoInstall,
["wrangler", "--version"],
{
cwd: config["workingDirectory"],
silent: config.QUIET_MODE,
},
);
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;
}
}
// There are two possible outputs from `wrangler --version`:
// ` ⛅️ wrangler 3.48.0 (update available 3.53.1)`
// and
// `3.48.0`
const versionMatch =
stdout.match(/wrangler (\d+\.\d+\.\d+)/) ??
stdout.match(/^(\d+\.\d+\.\d+)/m);
if (versionMatch) {
installedVersion = versionMatch[1];
}
if (config.didUserProvideWranglerVersion) {
installedVersionSatisfiesRequirement = semverEq(
installedVersion,
config["WRANGLER_VERSION"],
);
}
if (!config.didUserProvideWranglerVersion && installedVersion) {
info(
@@ -141,12 +119,15 @@ async function installWrangler(
true,
);
endGroup(config);
return installedVersion;
return;
}
if (config.didUserProvideWranglerVersion && versionSatisfied) {
if (
config.didUserProvideWranglerVersion &&
installedVersionSatisfiesRequirement
) {
info(config, `✅ Using Wrangler ${installedVersion}`, true);
endGroup(config);
return installedVersion;
return;
}
info(
config,
@@ -179,27 +160,6 @@ async function installWrangler(
} finally {
endGroup(config);
}
let resolvedVersion = "";
try {
resolvedVersion = await resolveInstalledVersion(config, packageManager);
} catch (err) {
debug(`Error resolving installed Wrangler version: ${err}`);
}
if (resolvedVersion) {
return resolvedVersion;
}
// Fall back to the raw version string if it's already valid semver.
// This preserves pre-existing behavior for exact version inputs.
if (isExactSemver(config["WRANGLER_VERSION"])) {
return config["WRANGLER_VERSION"];
}
throw new Error(
`Failed to determine installed Wrangler version after installing wrangler@${config["WRANGLER_VERSION"]}`,
);
}
function authenticationSetup(config: WranglerActionConfig) {
@@ -305,11 +265,7 @@ async function uploadSecrets(
);
}
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"];
}
const args = ["wrangler", "secret:bulk"];
if (environment) {
args.push("--env", environment);
@@ -412,9 +368,7 @@ export {
execCommands,
info,
installWrangler,
isExactSemver,
main,
parseWranglerVersion,
uploadSecrets,
wranglerCommands,
};