Skip to content

Instantly share code, notes, and snippets.

@ryukinix
Last active June 24, 2026 06:50
Show Gist options
  • Select an option

  • Save ryukinix/01934ed78ea5d3ff275b52681b39a9df to your computer and use it in GitHub Desktop.

Select an option

Save ryukinix/01934ed78ea5d3ff275b52681b39a9df to your computer and use it in GitHub Desktop.
cl-bbs GitHub Action deployment plan with Cloudflare Tunnels

Implementation Plan: Continuous Deployment (Production & QA)

Objective

Create GitHub Action workflows to automatically deploy to specific environments depending on the trigger context, utilizing Cloudflare Tunnels for SSH access in the runner using and-fm/cloudflared-ssh-action:

  1. Production (deploy.yml): Deploys the master branch to the production server.
  2. QA (deploy-qa.yml): Deploys a Pull Request branch to a dedicated QA instance using a specific QA Docker tag (qa) after CI checks (linting and testing) pass successfully.

1. Prerequisites and Secrets Configuration

The following GitHub Secrets map directly to the requirements in both workflows (Settings -> Secrets and variables -> Actions -> New repository secret).

Required Secrets

Secret Name Description Source/Generation
SSH_PRIVATE_KEY Private key to authenticate with the remote server. Generated locally using ssh-keygen. Expected to be permitted on the target server.
TUNNEL_SERVICE_TOKEN_ID Cloudflare Access Service Token Client ID. Generate via Cloudflare Zero Trust (Access -> Service Auth).
TUNNEL_SERVICE_TOKEN_SECRET Cloudflare Access Service Token Client Secret. Generate via Cloudflare Zero Trust (Access -> Service Auth). Save immediately.
DEPLOY_HOST The hostname of the server (e.g., ssh.example.com). Server administrator.
DEPLOY_USER The username used to log in via SSH (e.g., lerax or root). Documented deployment account user.
DOCKERHUB_TOKEN Token to push the Docker image. Docker Hub -> Account settings -> Security -> New Access Token. Needed because publish pushing ryukinix/cl-bbs mandates auth in Actions.
DOCKERHUB_USERNAME Username for Docker Hub target. Example: ryukinix

2. Makefile Enhancements (Completed locally)

The Makefile has been updated locally to support QA target pushes. We added publish-qa to tag as :qa instead of :latest/version, and deploy-qa explicitly applying cl-bbs-qa.

publish-qa: docker-build
	docker tag $(DOCKER_IMG) ryukinix/$(DOCKER_IMG):qa
	docker push ryukinix/$(DOCKER_IMG):qa

deploy-qa: publish-qa
	ssh starfox -t deploy apply cl-bbs-qa

3. Workflow Implementations

A. Production Workflow (.github/workflows/deploy.yml)

This runs specifically on merges/direct pushes to master.

name: Deploy to Production

on:
  push:
    branches:
      - master
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Source
        uses: actions/checkout@v4

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build and Publish Image
        run: make publish

      - name: Trigger Remote Deployment
        uses: and-fm/cloudflared-ssh-action@v3
        with:
          host: ${{ secrets.DEPLOY_HOST }}
          username: ${{ secrets.DEPLOY_USER }}
          private_key_value: ${{ secrets.SSH_PRIVATE_KEY }}
          service_token_id: ${{ secrets.TUNNEL_SERVICE_TOKEN_ID }}
          service_token_secret: ${{ secrets.TUNNEL_SERVICE_TOKEN_SECRET }}
          commands: deploy apply cl-bbs

B. QA Pull Request Workflow (.github/workflows/deploy-qa.yml)

This workflow waits for both CI and Lint actions to complete utilizing GitHub's workflow_run cross-workflow triggering. To ensure the correct PR codebase is built, we instruct the checkout action to use the SHA from the triggering workflow.

name: Deploy to QA

on:
  workflow_run:
    workflows: ["Lint", "CI Lisp Check"]
    types:
      - completed

jobs:
  deploy-qa:
    # Only run on pull requests and if BOTH prior actions were successful
    if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout PR Source
        uses: actions/checkout@v4
        with:
          ref: ${{ github.event.workflow_run.head_commit.id }}
          repository: ${{ github.event.workflow_run.head_repository.full_name }}

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build and Publish QA Image
        run: make publish-qa

      - name: Trigger QA Remote Deployment
        uses: and-fm/cloudflared-ssh-action@v3
        with:
          host: ${{ secrets.DEPLOY_HOST }}
          username: ${{ secrets.DEPLOY_USER }}
          private_key_value: ${{ secrets.SSH_PRIVATE_KEY }}
          service_token_id: ${{ secrets.TUNNEL_SERVICE_TOKEN_ID }}
          service_token_secret: ${{ secrets.TUNNEL_SERVICE_TOKEN_SECRET }}
          commands: deploy apply cl-bbs-qa

4. Execution Steps (For the Developer)

  1. Review the generated Makefile modifications introducing make publish-qa and make deploy-qa.
  2. Push DockerHub Tokens (DOCKERHUB_TOKEN, DOCKERHUB_USERNAME) to GitHub Actions Secrets.
  3. Commit these workflow yaml specs to .github/workflows/deploy.yml and .github/workflows/deploy-qa.yml.
  4. Create a PR, verify lint/check run successfully, and check if QA Deploy spins up reliably against the PR context target.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment