1node.si
← Registry

cd-github-actions-git-deploy

cleared

Designs GitHub Actions CD that mirrors build output into a dedicated Git deployment repository, commits, and pushes so a server-side Git hook runs deploy. Supports Linux (rsync) and Windows (robocopy /MIR). Emphasizes optional deploy steps in the same job as the build so free accounts avoid upload-artifact storage; documents a separate-job pattern when needed. Use when implementing GitHub Actions CD, git push deploy, post-receive hooks, or mirroring build output into a deploy repo. Covers hosted and self-hosted Linux runners and optional compact single-step deploy scripts.

files.jsonSKILL.md
---
name: cd-github-actions-git-deploy
description: >-
  Designs GitHub Actions CD that mirrors build output into a dedicated Git
  deployment repository, commits, and pushes so a server-side Git hook runs
  deploy. Supports Linux (rsync) and Windows (robocopy /MIR). Emphasizes
  optional deploy steps in the same job as the build so free accounts avoid
  upload-artifact storage; documents a separate-job pattern when needed. Use
  when implementing GitHub Actions CD, git push deploy, post-receive hooks,
  or mirroring build output into a deploy repo. Covers hosted and self-hosted
  Linux runners and optional compact single-step deploy scripts.
---

# GitHub Actions CD: Git push deploy

## Pattern

1. **Build** produces deployable files (folder or ZIP).
2. **Prepare tree for sync**: either use the **build output directory in the same job** (no artifact upload), or **download** an artifact / **unzip** in a follow-up job.
3. **Clone** the **deployment Git repo** into an isolated directory—commonly **`${{ runner.temp }}/deploy/${{ github.run_number }}`** per run, or a **`deployment/`** subfolder under **`github.workspace`**—so **`actions/checkout`** of the **source** repo is not overwritten.
4. **Mirror** files into that clone **without touching `.git`** (rsync on Linux, `robocopy /MIR` on Windows). A typical source tree is **`${{ github.workspace }}/Artifacts/`** (rename to match your build).
5. **`git add` / `git commit` / `git push`** to the configured branch.
6. **Server** runs a **post-receive / post-update hook** (or CI on that repo) to roll out the new tree.

Store clone/push credentials in **GitHub Actions secrets** (e.g. **`DEPLOYMENT_URL`**, **`DEPLOYMENT_GIT_URL`**, or per-app names like **`GIT_DEPLOY_URL_APP_PRODUCTION`**—full HTTPS URL with PAT). Avoid printing URLs or tokens in logs.

**Deploy directory:** besides **`runner.temp`**, bash exposes **`$RUNNER_TEMP`** (same path). Include **`${{ github.run_attempt }}`** in the folder name if retries must not collide with a failed run’s tree.

## Same job vs separate job (storage on free accounts)

| Layout | When to use | Artifacts |
|--------|-------------|-----------|
| **Deploy steps continue the build job** | Default for **free** plans: avoid **`actions/upload-artifact`** for deploy output so you do not consume **Actions artifact storage** | None required; sync from build output path (e.g. **`Artifacts/`**, `./publish`, `./dist`) |
| **Separate `deploy` job** | Different runner, approvals, or clearer separation | **`upload-artifact` / `download-artifact`** (or reuse workflow patterns); **costs storage** on free accounts |

You can still use **one job** even if an intermediate step creates a **ZIP** for consistency—extract it in a later **step** in the same job instead of uploading it.

## Required inputs (secrets and variables)

| Input | Role |
|--------|------|
| `DEPLOYMENT_URL` or `DEPLOYMENT_GIT_URL` (secret) | HTTPS clone URL (often includes PAT) or SSH remote URL |
| `DEPLOY_GIT_BRANCH` | Branch to push (repo variable or env); legacy repos may use **`master`** |
| Git identity | **`git config --global`** in a **Git setup** step (examples: **GitHub Actions** / **actions@github.com**), or **`DEPLOY_GIT_USER_*`** secrets—same idea as Azure’s git setup task |
| Build output path | Directory of loose files to mirror (e.g. **`Artifacts/`**), or path after download/unzip (multi-job) |

## Linux runner (rsync)

- **rsync** from the build output (e.g. **`${{ github.workspace }}/Artifacts/`**) into the deploy clone root; flags such as **`-azh --progress --delete`** match common deployment scripts.
- Exclude **`.git`** and dot-git files: `--exclude '.git' --exclude '.git*'`.

## Windows runner (robocopy)

- **`robocopy`** with **`/MIR`**, **`/XD .git`**, optional **`/XF`** for `.gitignore` / `.gitattributes`; normalize exit code: **`if ($LASTEXITCODE -lt 8) { $global:LASTEXITCODE = 0 }`** in PowerShell.
- Source path example: **`${{ github.workspace }}\Artifacts`** → destination **`.`** in the deploy directory (often under **`runner.temp`**).

## Operational notes

- **Empty commits**: guard with `git diff --cached --quiet` / `git status --porcelain` before `git commit`.
- **Strict shell**: **`set -euo pipefail`** at the top of bash deploy scripts catches clone/rsync failures early.
- **Line endings**: **`git config --local core.safecrlf false`** in the deploy clone avoids CRLF noise when the hook repo expects LF.
- **Non-fast-forward**: `git pull --rebase origin <branch>` before push when the deploy repo can move from other writers.
- **`git push -f`**: some hook repos use **force push** to a single branch; document the risk (history rewrite) and prefer fast-forward + merge policy when multiple writers exist.
- **Shallow clone**: `git clone --depth 1` for large deploy repos.
- **`actions/checkout`**: checks out the **application** repo; deploy clone must **not** replace that tree—use **`runner.temp`** or a dedicated subfolder.
- **Compact script**: a **single** bash step that clones, rsyncs, commits, and pushes is acceptable on **self-hosted** runners when logs stay readable; the multi-step layout in [reference.md](reference.md) remains easier to debug.

## Full workflow examples

See [reference.md](reference.md) for YAML: **same-job** (no deploy artifact) and **multi-job** (upload/download + optional unzip). Examples use **separate steps** for git setup, clone, sync, and push—aligned with the **Azure DevOps** task breakdown in **`cd-azure-devops-git-deploy`**. A **compact self-hosted** variant is also listed.