# Deploying a GitHub repository to cPanel with GitHub Actions

This guide configures a secure one-way deployment path:

```text
Developer → GitHub (main) → GitHub Actions → cPanel Git repository (master)
```

It is suitable for cPanel servers that provide **Git Version Control** and SSH access. GitHub is the source of truth; do not edit the cPanel repository directly after this setup is complete.

## 1. Prerequisites

You need:

- Admin or write access to the GitHub repository.
- A cPanel account with **Git Version Control** and **SSH Access** enabled.
- The cPanel account username, SSH host name, and SSH port.
- The exact cPanel Git clone URL. It looks like this:

  ```text
  ssh://<CPANEL_USER>@<SSH_HOST>:<SSH_PORT>/home/<CPANEL_USER>/<REPOSITORY_PATH>
  ```

Keep the repository outside `public_html`. For example:

```text
/home/<CPANEL_USER>/repositories/my-site
```

## 2. Create the cPanel-managed Git repository

1. In cPanel, open **Git Version Control**.
2. Select **Create Repository**.
3. Turn **off** “Clone a Repository”. Create an empty cPanel-managed repository instead.
4. Set a repository path outside the web root, then create it.
5. Copy the SSH clone URL that cPanel displays. It becomes the `CPANEL_REPO_URL` GitHub secret.

cPanel manages this repository and installs its Git hook. GitHub Actions will push into it; cPanel can later deploy from it if a valid `.cpanel.yml` is intentionally added.

## 3. Create an SSH deployment key

Run this on a trusted developer computer:

```bash
ssh-keygen -t ed25519 -C "github-cpanel-deploy" -f cpanel-github-deploy
```

For an unattended GitHub Actions key, leave the passphrase empty when prompted.

This creates two files:

- `cpanel-github-deploy` — **private key**. Keep it secret.
- `cpanel-github-deploy.pub` — public key.

In cPanel, open **SSH Access → Manage SSH Keys → Import Key**. Import the contents of the `.pub` file, then select **Authorize**. The key must show as authorized.

## 4. Add GitHub repository secrets

In GitHub, open **Settings → Secrets and variables → Actions → New repository secret**. Add these repository secrets:

| Secret | Value |
| --- | --- |
| `CPANEL_SSH_KEY` | The complete private-key file, including the `BEGIN` and `END` lines. |
| `CPANEL_REPO_URL` | The exact cPanel clone URL copied in step 2. |
| `CPANEL_KNOWN_HOSTS` | The SSH host key output from the command below. |

Create `CPANEL_KNOWN_HOSTS` on a trusted computer:

```bash
ssh-keyscan -p <SSH_PORT> -H <SSH_HOST>
```

Copy every returned key line into the secret. The port is important: a key collected for port 22 will not match a cPanel server using a custom SSH port.

Before trusting a new key, compare its fingerprint with the value supplied by the hosting provider or server administrator.

## 5. Add the GitHub Actions workflow

This repository already contains `.github/workflows/deploy.yml`; do not create a second deployment workflow. Commit it to `main` and watch **GitHub → Actions → Deploy to cPanel** for the first run.

The workflow uses full Git history, pushes the repository to cPanel, and—only when the showcase theme or plugin changes—builds Composer production dependencies in GitHub Actions and deploys those two components with rsync.

The workflow maps the GitHub `main` branch to the cPanel repository’s `master` branch. If the cPanel repository uses another branch, update `HEAD:master` accordingly.

## 6. Verify the connection

After a successful Action:

1. Open cPanel → **Git Version Control**.
2. Open the managed repository.
3. Confirm that its latest commit SHA matches the GitHub commit that triggered the workflow.
4. Push a small non-production test change to GitHub `main` and verify that a new successful Action runs.

At this point the repository is synchronised. Nothing is copied to `public_html` unless a deployment configuration is added.

## 7. Optional: deploy files into the live web root

### Showcase Catalogue theme

The repository's existing `.github/workflows/deploy.yml` runs Composer in GitHub Actions with `--no-dev`, then streams each built component through `tar` over SSH. The cPanel server stages, swaps, and cleans the built `showcase-catalogue` theme (including `vendor/`) and companion plugin without requiring Composer or rsync on cPanel. It derives the SSH connection and WordPress root from the existing `CPANEL_REPO_URL` secret. Composer and component deployment run only when the showcase theme or plugin changes; normal Git synchronization still runs for every push to `main`.

No new secrets are required: retain `CPANEL_SSH_KEY`, `CPANEL_KNOWN_HOSTS`, and `CPANEL_REPO_URL`. The workflow treats the absolute repository path inside `CPANEL_REPO_URL` as the WordPress root—`wpexamples` in this installation—then deploys to its theme and plugin directories. Protect the GitHub `production` environment with the desired reviewers. Each component is extracted into a sibling staging directory and then swapped into place, so stale files are removed from the two managed component directories.

Only add `.cpanel.yml` when the exact production path and files are known. Do not deploy WordPress core, `wp-config.php`, uploads, caches, or secrets by default.

Example for a repository containing only a custom theme directory named `theme`:

```yaml
---
deployment:
  tasks:
    - export DEPLOYPATH=/home/<CPANEL_USER>/public_html/wp-content/themes/<THEME_NAME>
    - /bin/mkdir -p $DEPLOYPATH
    - /bin/cp -R theme/. $DEPLOYPATH
```

Commit `.cpanel.yml` at the repository root. On the next accepted cPanel push, its deployment tasks run. Review the target path carefully before committing it.

## Troubleshooting

| Error | Cause and resolution |
| --- | --- |
| `Permission denied (publickey)` | The cPanel public key was not authorized, or `CPANEL_SSH_KEY` does not contain its matching private key. Re-import and authorize the correct `.pub` key. |
| `Host key verification failed` | Replace `CPANEL_KNOWN_HOSTS` with fresh output from `ssh-keyscan -p <SSH_PORT> -H <SSH_HOST>`. Ensure the host and port exactly match `CPANEL_REPO_URL`. |
| `shallow update not allowed` | Ensure the checkout step uses `fetch-depth: 0`. |
| `non-fast-forward` or remote rejection after the shallow-clone fix | GitHub and cPanel have different commit histories. Back up the cPanel repository and decide which history is authoritative; do not force-push production history without an explicit recovery plan. |
| No live site changes after a successful Action | This is expected without `.cpanel.yml`. The Action synchronises Git repositories only. |

## Security and maintenance

- Use dedicated deployment keys, not a developer’s personal SSH key.
- Store the private key only as a GitHub Actions secret and an encrypted recovery backup.
- Rotate the key by adding a new authorized public key, updating `CPANEL_SSH_KEY`, testing, then removing the old key.
- Keep production secrets out of Git and out of `.cpanel.yml`.
- Restrict production deployment to reviewed commits on `main`.
