116 lines
4.9 KiB
YAML
116 lines
4.9 KiB
YAML
name: deploy
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths:
|
|
# No build/config/terraform/** — this workflow only deploys to Unraid,
|
|
# and `terraform apply` is run by hand. Add it if that ever changes.
|
|
- "src/**"
|
|
- "build/config/ansible/**"
|
|
|
|
jobs:
|
|
deploy:
|
|
# Requires a self-hosted Forgejo Actions runner on your LAN (labelled
|
|
# `unraid-deploy`) with: network access to Vault and to the Unraid hosts'
|
|
# SSH + Postgres ports, and Python + pip available. See README.md
|
|
# "Bootstrapping" — this workflow can only run once that runner exists,
|
|
# which is why the first deploy has to happen by hand. The SSH key
|
|
# `ansible_user` (root) authenticates with is fetched from Vault below,
|
|
# not stored as a runner or repo secret — see README.md "SSH access".
|
|
runs-on: unraid-deploy
|
|
env:
|
|
VAULT_ADDR: ${{ secrets.VAULT_ADDR }}
|
|
VAULT_AUTH_METHOD: approle
|
|
VAULT_ROLE_ID: ${{ secrets.VAULT_ROLE_ID }}
|
|
VAULT_SECRET_ID: ${{ secrets.VAULT_SECRET_ID }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0 # need history to diff — a shallow clone can't compute this
|
|
|
|
- name: Install Ansible and Python dependencies
|
|
run: |
|
|
python3 -m pip install --upgrade pip
|
|
python3 -m pip install ansible hvac psycopg2-binary
|
|
|
|
- name: Install Ansible collections
|
|
working-directory: build/config/ansible
|
|
run: ansible-galaxy collection install -r requirements.yml
|
|
|
|
- name: Fetch SSH deploy key from Vault
|
|
run: |
|
|
set -eu
|
|
python3 - <<'PY'
|
|
import os
|
|
import hvac
|
|
|
|
client = hvac.Client(url=os.environ["VAULT_ADDR"])
|
|
client.auth.approle.login(
|
|
role_id=os.environ["VAULT_ROLE_ID"],
|
|
secret_id=os.environ["VAULT_SECRET_ID"],
|
|
)
|
|
secret = client.secrets.kv.v2.read_secret_version(
|
|
path="homelab/ci/ssh", mount_point="kv"
|
|
)["data"]["data"]
|
|
|
|
key_path = os.path.join(os.environ["RUNNER_TEMP"], "unraid_ansible_key")
|
|
with open(key_path, "w") as f:
|
|
f.write(secret["PRIVATE_KEY"].rstrip() + "\n")
|
|
os.chmod(key_path, 0o600)
|
|
PY
|
|
echo "ANSIBLE_PRIVATE_KEY_FILE=$RUNNER_TEMP/unraid_ansible_key" >> "$GITHUB_ENV"
|
|
|
|
- name: Determine changed stacks
|
|
id: changed
|
|
run: |
|
|
set -eu
|
|
# Three possible outcomes, and they are not the same thing:
|
|
# all=true deploy every stack on every host
|
|
# only_stacks=a,b deploy just those
|
|
# skip=true nothing here affects the Unraid deployment
|
|
# An empty only_stacks must never be read as "deploy everything" —
|
|
# that is exactly what a Proxmox-only or Terraform-only push
|
|
# produces.
|
|
#
|
|
# A change under build/config/ansible/ can affect how every stack
|
|
# is deployed (the role, inventory, playbook), so don't try to
|
|
# narrow it. Same if the diff can't be computed at all (force-push,
|
|
# or a first push where `before` is all-zeros): fall back to a full
|
|
# deploy rather than silently deploying nothing.
|
|
if ! changed_files=$(git diff --name-only "${{ github.event.before }}" "${{ github.sha }}" 2>/dev/null); then
|
|
echo "all=true" >> "$GITHUB_OUTPUT"
|
|
exit 0
|
|
fi
|
|
if echo "$changed_files" | grep -q '^build/config/ansible/'; then
|
|
echo "all=true" >> "$GITHUB_OUTPUT"
|
|
exit 0
|
|
fi
|
|
# Only changes that affect the Unraid deployment count, since that
|
|
# is all this job runs. A stack's common/ and ansible/unraid/ do;
|
|
# its terraform/ and ansible/proxmox/ don't.
|
|
stacks=$(echo "$changed_files" \
|
|
| { grep '^src/' || true; } \
|
|
| { grep -Ev '/(terraform|ansible/proxmox)/' || true; } \
|
|
| awk -F/ '{ print ($2 == "shared") ? $3 : $2 }' \
|
|
| sort -u | paste -sd, -)
|
|
if [ -z "$stacks" ]; then
|
|
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "only_stacks=$stacks" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
# Unraid only. Deploying to Proxmox from CI would need the runner to
|
|
# reach the Proxmox API and the guests, and `terraform apply` is
|
|
# deliberately kept manual — creating and destroying LXCs on a push is
|
|
# a bigger blast radius than restarting a Compose stack. Run
|
|
# playbooks/proxmox.yml by hand for now.
|
|
- name: Run deploy playbook
|
|
if: steps.changed.outputs.skip != 'true'
|
|
working-directory: build/config/ansible
|
|
run: |
|
|
if [ "${{ steps.changed.outputs.all }}" = "true" ]; then
|
|
ansible-playbook playbooks/unraid.yml
|
|
else
|
|
ansible-playbook playbooks/unraid.yml -e only_stacks="${{ steps.changed.outputs.only_stacks }}"
|
|
fi
|