← All posts

Automated WordPress Plugin Deployment with GitHub Actions and WP-CLI

Automated WordPress Plugin Deployment with GitHub Actions and WP-CLI

Building robust WordPress applications often involves custom plugins and themes that require careful deployment. Manually FTPing files or even performing `git pull` operations directly on a server can be tedious, error-prone, and lack the necessary audit trail. This is where a proper CI/CD pipeline shines, allowing us to automate deployments directly from our version control system. Today, I want to walk through setting up a streamlined deployment pipeline for a custom WordPress plugin using GitHub Actions and WP-CLI.

Our goal is simple: whenever we push changes to the `main` branch of our custom plugin's GitHub repository, we want those changes to be automatically deployed to our staging server. This ensures our staging environment is always up-to-date with the latest code, ready for testing.

First, let's prepare our staging server. We'll need a user with SSH access that has write permissions to the `wp-content/plugins` directory of your WordPress installation. This user should also have `wp-cli` installed and accessible in its PATH. For security, it's best to create a dedicated deployment user on your server and configure it for key-based authentication, disabling password login. We'll generate an SSH key pair for this user; the public key goes into `~/.ssh/authorized_keys` on the server, and the private key will be stored securely in GitHub Secrets.

Now, let's craft the GitHub Actions workflow. Create a file named `.github/workflows/deploy.yml` in your plugin's repository.

```yaml name: Deploy Plugin to Staging

on: push: branches: - main

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

- name: Setup SSH uses: webfactory/[email protected] with: ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}

- name: Deploy plugin files via rsync run: | mkdir -p ~/.ssh ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts rsync -avz --exclude '.git/' --exclude '.github/' --exclude 'node_modules/' \ ./ ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:${{ secrets.REMOTE_PLUGIN_PATH }}

- name: Flush WordPress cache run: | ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "cd ${{ secrets.REMOTE_WP_ROOT_PATH }} && wp cache flush" ```

Let's break down this YAML. The `on: push` block triggers the workflow whenever changes are pushed to the `main` branch. The `deploy` job runs on `ubuntu-latest`.

1. **Checkout code**: This step simply checks out your plugin's repository onto the GitHub Actions runner. 2. **Setup SSH**: Here, we use a community action to set up the SSH agent with our private key. The `SSH_PRIVATE_KEY` secret will hold the private key content we generated earlier. 3. **Deploy plugin files via rsync**: This is the core deployment step. * `mkdir -p ~/.ssh` creates the SSH directory if it doesn't exist. * `ssh-keyscan` adds the host key of your staging server to the GitHub Actions runner's `known_hosts` file, preventing "Are you sure you want to continue connecting?" prompts during `rsync` and `ssh` commands. `SSH_HOST` is another secret storing your server's IP address or hostname. * `rsync` copies your plugin's entire directory (excluding `.git/`, `.github/`, and `node_modules/` to save bandwidth and prevent deploying unnecessary development files) to the `REMOTE_PLUGIN_PATH` on your server. `SSH_USER` holds the deployment username, and `REMOTE_PLUGIN_PATH` should be something like `/var/www/html/wp-content/plugins/your-plugin-slug`. 4. **Flush WordPress cache**: After updating plugin files, it's crucial to flush any object or persistent caches WordPress might be using. We SSH back into the server, navigate to the WordPress root (`REMOTE_WP_ROOT_PATH`), and execute `wp cache flush` using WP-CLI.

To secure your sensitive information, head to your GitHub repository's Settings > Secrets > Actions and add the following repository secrets: * `SSH_PRIVATE_KEY`: The *entire* private SSH key (including `-----BEGIN OPENSSH PRIVATE KEY-----` and `-----END OPENSSH PRIVATE KEY-----`). * `SSH_HOST`: Your staging server's hostname or IP address (e.g., `your-staging-server.com`). * `SSH_USER`: The deployment username on your staging server (e.g., `deployuser`). * `REMOTE_PLUGIN_PATH`: The absolute path to your plugin's directory on the staging server (e.g., `/var/www/html/wp-content/plugins/my-custom-plugin`). * `REMOTE_WP_ROOT_PATH`: The absolute path to your WordPress root directory on the staging server (e.g., `/var/www/html`).

With this setup, every push to your `main` branch will automatically trigger the workflow, deploy your plugin, and flush the cache, providing a reliable and efficient deployment process. This foundation can be extended with additional steps like running `composer install` for PHP dependencies or `npm build` for frontend assets before `rsync`, but for a pure backend plugin, this minimal setup is robust and effective.