Skip to content

Instantly share code, notes, and snippets.

@aitormendez
Last active June 6, 2025 15:01
Show Gist options
  • Save aitormendez/c0c68510e2c91c784deb07ae55b0e8f6 to your computer and use it in GitHub Desktop.
Save aitormendez/c0c68510e2c91c784deb07ae55b0e8f6 to your computer and use it in GitHub Desktop.
Complete Guide: Automatic Deployment of Sage 11 with Trellis, Bedrock, and GitHub Actions

Complete Guide: Automatic Deployment of Sage 11 with Trellis, Bedrock, and GitHub Actions

Table of Contents

  1. Prerequisites
  2. Using npm across the project
  3. Manually creating workflow files
  4. Generating SSH keys for GitHub Actions with Trellis CLI
  5. Configuring secrets in GitHub
  6. File: .github/workflows/deploy-staging.yml
  7. Adding SSH keys to the global Vault
  8. Modifying the deploy role tasks to write the key on the server
  9. Adding a Makefile for convenience commands
  10. Best practices
  11. Final result

This guide outlines, step by step, how to configure a continuous deployment workflow for a project based on the Roots stack (Trellis + Bedrock + Sage 11) using GitHub Actions. It includes secure SSH key handling for staging and production environments.


1. Prerequisites

  • A Git repository organized following the Roots structure:

    project-name/
    ├── site/                # WordPress via Bedrock
    │   └── web/app/themes/sage
    ├── trellis/             # Ansible + server config
    └── .github/             # deployment workflows
    
    
  • GitHub CLI installed and authenticated: gh auth login

  • A VPS or droplet provisioned with Trellis (Ubuntu, DigitalOcean, etc.)

  • A deploy SSH key generated and added to the GitHub repository as a "read-only deploy key"


2. Using npm across the project

While the project might have originally used yarn, it is recommended to fully migrate to npm, for the following reasons:

  • @wordpress/scripts and other modern WordPress tools are designed and tested with npm.
  • Sage 11 has removed explicit references to yarn in its documentation.
  • Yarn 2+ (Berry) introduces a Plug'n'Play system incompatible with many tools.

Steps to migrate the Sage theme to npm

cd site/web/app/themes/sage
rm yarn.lock
npm install

Then update the workflow and Makefile commands accordingly.


3. Manually creating workflow files

Instead of cloning external templates like trellis-github-deployment, this guide defines custom GitHub Actions workflows from scratch.

Steps:

  1. Create the following folder structure:
mkdir -p .github/workflows

  1. Inside .github/workflows/, create two files:
  • deploy-staging.yml
  • deploy-production.yml
  1. Use the examples provided in this guide as a base and customize them as needed:
  • Sage theme path
  • Build commands
  • Deployment target (staging or production)

This approach keeps workflows clean, minimal, and fully tailored to the project.


4. Generating SSH keys for GitHub Actions with Trellis CLI

From the trellis directory:

trellis key generate

This command will:

  • Generate a private SSH key in ~/.ssh/trellis_<slug>_ed25519

  • Create the corresponding public key in trellis/public_keys/

  • Add the deploy key to the GitHub repo (if you have access)

  • Automatically create these GitHub secrets:

    • TRELLIS_DEPLOY_SSH_PRIVATE_KEY
    • TRELLIS_DEPLOY_SSH_KNOWN_HOSTS

You can also upload the key to the server:

trellis provision --tags=users staging

Each project should have its own key. Trellis handles naming to avoid SSH conflicts.


5. Configuring secrets in GitHub

In your GitHub repository: Settings → Secrets and variables → Actions → New repository secret

Add the following secrets if they weren’t created automatically by trellis key generate:

  • ANSIBLE_VAULT_PASSWORD: content of trellis/.vault_pass
  • TRELLIS_DEPLOY_SSH_PRIVATE_KEY: your private SSH key
  • TRELLIS_DEPLOY_SSH_KNOWN_HOSTS: output of ssh-keyscan -H github.com
  • GITHUB_TOKEN: this is provided by default in GitHub Actions, no need to add manually

💡 Note: Secret values are hidden once saved. If unsure, re-uploading them is safe.


6. File: .github/workflows/deploy-staging.yml

name: 🚀 Deploy to Staging

on:
  workflow_dispatch:

jobs:
  deploy:
    name: Deploy to Staging
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repo
        uses: actions/checkout@v3

      - name: Install SSH key and known_hosts
        uses: shimataro/ssh-key-action@v2
        with:
          key: ${{ secrets.TRELLIS_DEPLOY_SSH_PRIVATE_KEY }}
          known_hosts: ${{ secrets.TRELLIS_DEPLOY_SSH_KNOWN_HOSTS }}
          if_key_exists: replace

      - name: Setup Trellis CLI
        uses: roots/setup-trellis-cli@v1
        with:
          ansible-vault-password: ${{ secrets.ANSIBLE_VAULT_PASSWORD }}
          repo-token: ${{ secrets.GITHUB_TOKEN }}

      - name: Build Sage 11 Theme
        run: |
          cd site/web/app/themes/sage
          npm install
          npm run build

      - name: Install Sage PHP dependencies
        run: |
          cd site/web/app/themes/sage
          composer install --no-dev --no-interaction --optimize-autoloader

      - name: Deploy to staging
        run: trellis deploy staging

Create a similar file for deploy-production.yml, replacing staging with production.


7. Adding SSH keys to the global Vault

Edit:

ansible-vault edit trellis/group_vars/all/vault.yml

Add:

vault_github_deploy_key: |
  -----BEGIN OPENSSH PRIVATE KEY-----
  your-key-here...
  -----END OPENSSH PRIVATE KEY-----


8. Modifying the deploy role tasks to write the key on the server

Edit trellis/roles/deploy/tasks/main.yml:

Insert right before - import_tasks: initialize.yml:

- name: Ensure .ssh directory exists
  file:
    path: "/home/{{ web_user }}/.ssh"
    state: directory
    owner: "{{ web_user }}"
    group: "{{ web_group }}"
    mode: "0700"

- name: Write GitHub deploy key from Vault
  copy:
    content: "{{ vault_github_deploy_key }}"
    dest: "/home/{{ web_user }}/.ssh/github_deploy_key"
    owner: "{{ web_user }}"
    group: "{{ web_group }}"
    mode: "0600"

- name: Configure SSH to use GitHub deploy key
  copy:
    content: |
      Host github.com
        IdentityFile /home/{{ web_user }}/.ssh/github_deploy_key
        IdentitiesOnly yes
    dest: "/home/{{ web_user }}/.ssh/config"
    owner: "{{ web_user }}"
    group: "{{ web_group }}"
    mode: "0644"


9. Adding a Makefile for convenience commands

# Variables
BRANCH=main

deploy-staging:
	gh workflow run deploy-staging.yml --ref $(BRANCH)

deploy-production:
	gh workflow run deploy-production.yml --ref $(BRANCH)

build-theme:
	cd site/web/app/themes/sage && npm install && npm run build


10. Best practices

  • Never store private keys in plain files (use Vault)
  • Avoid agent-forwarding with GitHub Actions
  • Prefer npm for best compatibility with WordPress ecosystem
  • Always ensure staging is running before deploying

11. Final result

When running:

make deploy-staging

GitHub Actions will:

  • Clone the repo
  • Build the Sage 11 theme
  • Install PHP dependencies
  • Upload the SSH key to the server
  • Deploy the site to staging via Trellis

All of this happens securely, reproducibly, and automatically.

@cfaria
Copy link

cfaria commented Jun 5, 2025

Hi @aitormendez, great doc! But I have some questions:

  1. Why do you copy your private key to the server? If you use TRELLIS_DEPLOY_SSH_PRIVATE_KEY action secret then you only need the public key associated to that private key in the authorized_keys file in the server for the action to successfully connect to the server via SSH.
  2. Why do you build Sage 11 and Composer install in the action when you can do that via hooks with Trellis? In fact, I think those are installing in the action server, not the final one...

Maybe I'm doing things the wrong way, but I think you are overdoing some tasks... 😄

@aitormendez
Copy link
Author

Hola Carlos! Gracias por revisar la metodología, estoy seguro que tus comentarios serán acertados. Ahora estoy con un jaleo vital considerable, pero lo revisaré tan pronto como pueda. Un abrazo.

@cfaria

@cfaria
Copy link

cfaria commented Jun 6, 2025

Jajaja, claro @aitormendez , si sólo lo comentaba por ayudar en realidad.
Oye, tenemos que hacer un meet algún día, que tenemos poca gente que hable español y hay veces que me gustaría saber cómo trabajan otros desarrolladores con todas las herramientas de Roots. Te llevo siguiendo la pista hace tiempo por todo el ecosistema Roots ;D.
Vamos hablando!

@aitormendez
Copy link
Author

@cfaria te he mandado un email a carlos at blavetstudio.com, mira por si acaso el spam ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment