Last active
September 1, 2026 15:54
-
-
Save waptik/c9052d4922fbb1e4c38a8236127802b5 to your computer and use it in GitHub Desktop.
GitHub Actions CI/CD deployment workflow and GitHub secrets stack using Alchemy v2 & Doppler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # ============================================================================== | |
| # GitHub Actions CI/CD Deployment Workflow for Alchemy v2 & Doppler | |
| # ============================================================================== | |
| # | |
| # PURPOSE: | |
| # Automated CI/CD pipeline for deploying applications using Alchemy v2 on Cloudflare, | |
| # with Doppler as the centralized runtime secrets manager. | |
| # | |
| # FEATURES: | |
| # - Automated preview deployments for PRs (stage: pr-<PR_NUMBER>) | |
| # - Production deployments on merge/push to `main` (stage: prod) | |
| # - Automatic preview teardown/cleanup when PR is closed | |
| # - Doppler CLI integration to inject runtime secrets per environment | |
| # - Concurrency grouping by stage to prevent race conditions | |
| # - Non-interactive (`--yes`) and zero-downtime adoption (`--adopt`) flags | |
| # | |
| # PREREQUISITES: | |
| # 1. Doppler Project & Configs: | |
| # - Set up configs in Doppler (e.g. `production` and `dev`). | |
| # 2. GitHub Actions Secrets / Variables: | |
| # - Deploy the companion `stacks-github.ts` stack to sync `DOPPLER_TOKEN`, | |
| # `CLOUDFLARE_API_TOKEN`, and `CLOUDFLARE_ACCOUNT_ID` automatically. | |
| # | |
| # PLACEHOLDERS TO REPLACE: | |
| # - `project`: Your Doppler project name | |
| # - `@repo/location`: Your package name/filter (e.g. `@myorg/infra`) | |
| # ============================================================================== | |
| name: Deploy Application | |
| on: | |
| push: | |
| branches: | |
| - main | |
| pull_request: | |
| types: | |
| - opened | |
| - reopened | |
| - synchronize | |
| - closed | |
| concurrency: | |
| group: deploy-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.number) || (github.ref == 'refs/heads/main' && 'prod' || github.ref_name) }} | |
| cancel-in-progress: false | |
| env: | |
| STAGE: ${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.number) || (github.ref == 'refs/heads/main' && 'prod' || github.ref_name) }} | |
| jobs: | |
| deploy: | |
| name: Deploy | |
| if: ${{ github.event.action != 'closed' }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| run_install: false | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Setup Doppler CLI | |
| uses: dopplerhq/cli-action@v3 | |
| - name: Deploy with Alchemy | |
| run: doppler run -c ${{ env.STAGE == 'prod' && 'production' || 'dev' }} --project project -- pnpm --filter @repo/location exec alchemy deploy --stage "$STAGE" --adopt --yes | |
| env: | |
| DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN }} | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| PULL_REQUEST: ${{ github.event.number }} | |
| GITHUB_SHA: ${{ github.sha }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| cleanup: | |
| name: Cleanup | |
| runs-on: ubuntu-latest | |
| if: ${{ github.event_name == 'pull_request' && github.event.action == 'closed' }} | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| run_install: false | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile --ignore-scripts | |
| - name: Setup Doppler CLI | |
| uses: dopplerhq/cli-action@v3 | |
| # Allowlist check: cleanup must only ever destroy pr-N stages | |
| - name: Safety Check | |
| run: |- | |
| case "${{ env.STAGE }}" in | |
| pr-[0-9]*) ;; | |
| *) | |
| echo "ERROR: cleanup only destroys pr-* stages, got '${{ env.STAGE }}'" | |
| exit 1 | |
| ;; | |
| esac | |
| - name: Destroy Preview Environment | |
| run: doppler run -c dev --project project -- pnpm --filter @repo/location exec alchemy destroy --stage "$STAGE" --yes | |
| env: | |
| DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN }} | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| PULL_REQUEST: ${{ github.event.number }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * ============================================================================== | |
| * Alchemy v2 Stack: GitHub Actions Secrets & Variables Provisioner | |
| * ============================================================================== | |
| * | |
| * PURPOSE: | |
| * Synchronizes CI/CD secrets (DOPPLER_TOKEN, CLOUDFLARE_API_TOKEN) and variables | |
| * (CLOUDFLARE_ACCOUNT_ID) directly into your GitHub repository settings as code. | |
| * | |
| * WORKFLOW & SETUP INSTRUCTIONS: | |
| * 1. Authenticate with Alchemy (configures Cloudflare & GitHub credentials): | |
| * $ pnpm alchemy login --profile <profile> --configure | |
| * | |
| * 2. Ensure your GITHUB_TOKEN has `secrets:write`, `variables:write`, and `actions:write` permissions. | |
| * | |
| * 3. Preview stack changes (Dry Run): | |
| * $ doppler run -c <config> --project <project> -- alchemy plan path/to/stacks/github.ts --profile <profile> | |
| * | |
| * 4. Deploy the stack to GitHub: | |
| * $ doppler run -c <config> --project <project> -- alchemy deploy path/to/stacks/github.ts --profile <profile> | |
| * | |
| * PLACEHOLDERS TO REPLACE: | |
| * - `owner`: Your GitHub organization or username | |
| * - `repo`: Your GitHub repository name | |
| * - `ProjectGitHubSecrets`: Name of your Alchemy stack | |
| * ============================================================================== | |
| */ | |
| import * as Alchemy from "alchemy"; | |
| import * as Cloudflare from "alchemy/Cloudflare"; | |
| import * as GitHub from "alchemy/GitHub"; | |
| import * as Config from "effect/Config"; | |
| import * as Effect from "effect/Effect"; | |
| import * as Layer from "effect/Layer"; | |
| const REPO = { owner: "owner", repository: "repo" } as const; | |
| export default Alchemy.Stack( | |
| "ProjectGitHubSecrets", | |
| { | |
| providers: Layer.mergeAll( | |
| Cloudflare.providers(), | |
| GitHub.providers(), | |
| ), | |
| state: Cloudflare.state(), | |
| }, | |
| Effect.gen(function* () { | |
| const DOPPLER_TOKEN = yield* Config.redacted("DOPPLER_TOKEN"); | |
| const CLOUDFLARE_API_TOKEN = yield* Config.redacted("CLOUDFLARE_API_TOKEN"); | |
| const CLOUDFLARE_ACCOUNT_ID = yield* Config.string("CLOUDFLARE_ACCOUNT_ID"); | |
| // Sync repository secrets for GitHub Actions CI/CD | |
| yield* GitHub.Secrets({ | |
| owner: REPO.owner, | |
| repository: REPO.repository, | |
| secrets: { | |
| DOPPLER_TOKEN, | |
| CLOUDFLARE_API_TOKEN, | |
| }, | |
| }); | |
| yield* GitHub.Variables({ | |
| owner: REPO.owner, | |
| repository: REPO.repository, | |
| variables: { | |
| CLOUDFLARE_ACCOUNT_ID, | |
| }, | |
| }); | |
| return { repo: `${REPO.owner}/${REPO.repository}` }; | |
| }), | |
| ); |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Setup & Deployment Guide
This gist provides a complete CI/CD deployment pipeline using Alchemy v2 for Cloudflare infrastructure and Doppler as the centralized secrets manager.
Step 1: Authenticate with Alchemy
Before deploying the GitHub stack, configure your Cloudflare and GitHub credentials:
Step 2: Deploy the GitHub Secrets Stack (
stacks-github.ts)The
stacks-github.tsfile is an Alchemy stack that synchronizes your repository secrets (DOPPLER_TOKEN,CLOUDFLARE_API_TOKEN) and variables (CLOUDFLARE_ACCOUNT_ID) directly into GitHub Actions as code.Update Placeholders:
ownerandrepowith your GitHub organization/username and repository.ProjectGitHubSecretsto your desired stack name.Preview Changes (Dry Run):
Deploy Secrets to GitHub:
Step 3: Configure GitHub Actions Workflow
Save
github-workflows-deploy-alchemy-v2.ymlas.github/workflows/deploy.ymlin your repository.Placeholders to replace:
--project project: Set your Doppler project name.--filter @repo/location: Set your monorepo infra package name (e.g.--filter @myorg/infra).Key Workflow Features & Safety Protections
pr-<PR_NUMBER>on every pull request.prodstage when changes merge intomain.alchemy destroy --stage "$STAGE"when a PR is closed.caseallowlist (pr-[0-9]*) to prevent accidental destruction of production or base environments.--yesto suppress CLI interactive prompts and--adoptfor zero-downtime resource adoption.STAGEexpression to eliminate race conditions.