Proof: The rendered Kubernetes manifest now includes a Traefik TLS ingress for operator-dashboard and production basic auth, deploy workflow/bootstrap preserve the dashboard password as a secret, and static plus full node tests pass. Assumptions: doran.133011.xyz is the intended public host because unrip.doran.133011.xyz and dashboard.doran.133011.xyz do not currently resolve. Still fake: the public dashboard is not deployed or externally verified yet because the cluster host, Forgejo, and Kubernetes API timed out from this machine during this turn.
188 lines
7.2 KiB
YAML
188 lines
7.2 KiB
YAML
name: deploy
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
deploy:
|
|
runs-on: linux-amd64
|
|
env:
|
|
IMAGE_TAG: ${{ github.sha }}
|
|
REGISTRY_HOST: ${{ vars.REGISTRY_HOST }}
|
|
PROJECT_NAME: ${{ vars.PROJECT_NAME || 'unrip' }}
|
|
PROJECT_NAMESPACE: ${{ vars.PROJECT_NAMESPACE || vars.PROJECT_NAME || 'unrip' }}
|
|
PROJECT_REGISTRY_SECRET_NAME: ${{ vars.PROJECT_REGISTRY_SECRET_NAME || format('{0}-registry-creds', vars.PROJECT_NAME || 'unrip') }}
|
|
REPO_CLONE_URL: ${{ github.server_url }}/${{ github.repository }}.git
|
|
steps:
|
|
- name: Install tooling
|
|
run: |
|
|
if command -v git >/dev/null 2>&1 && command -v kubectl >/dev/null 2>&1 && command -v python3 >/dev/null 2>&1; then
|
|
exit 0
|
|
fi
|
|
|
|
if command -v apk >/dev/null 2>&1; then
|
|
apk add --no-cache git kubectl python3
|
|
exit 0
|
|
fi
|
|
|
|
if command -v apt-get >/dev/null 2>&1; then
|
|
apt-get update
|
|
apt-get install -y git curl ca-certificates python3
|
|
curl -fsSLo /usr/local/bin/kubectl "https://dl.k8s.io/release/$(curl -fsSL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
|
|
chmod +x /usr/local/bin/kubectl
|
|
exit 0
|
|
fi
|
|
|
|
echo "missing git/kubectl/python3 and no supported package manager found" >&2
|
|
exit 1
|
|
|
|
- name: Prepare workspace
|
|
run: |
|
|
workspace_root="${RUNNER_TEMP:-/tmp}"
|
|
workspace_dir="$(mktemp -d "${workspace_root%/}/unrip-deploy-XXXXXX")"
|
|
echo "WORKSPACE_DIR=$workspace_dir" >> "$GITHUB_ENV"
|
|
echo "runner workspace: $workspace_dir"
|
|
|
|
- name: Load kubeconfig
|
|
run: |
|
|
mkdir -p "$HOME/.kube"
|
|
printf '%s' '${{ secrets.KUBECONFIG_B64 }}' | base64 -d > "$HOME/.kube/config"
|
|
kubectl get ns
|
|
|
|
- name: Checkout repo
|
|
env:
|
|
REPO_TOKEN: ${{ github.token }}
|
|
run: |
|
|
git -c credential.username=oauth2 -c http.extraHeader="Authorization: Bearer ${REPO_TOKEN}" clone --depth=1 "${REPO_CLONE_URL}" "$WORKSPACE_DIR"
|
|
cd "$WORKSPACE_DIR"
|
|
current_sha="$(git rev-parse HEAD)"
|
|
if [ "$current_sha" != "$GITHUB_SHA" ]; then
|
|
git -c credential.username=oauth2 -c http.extraHeader="Authorization: Bearer ${REPO_TOKEN}" fetch --depth=1 origin "${GITHUB_SHA}"
|
|
git checkout --detach "${GITHUB_SHA}"
|
|
else
|
|
git checkout --detach "$current_sha"
|
|
fi
|
|
git rev-parse HEAD
|
|
|
|
- name: Resolve deployment settings
|
|
run: |
|
|
IMAGE="$REGISTRY_HOST/$PROJECT_NAME:$IMAGE_TAG"
|
|
BUILD_JOB="image-build-$(printf '%s' "$GITHUB_SHA" | cut -c1-12)"
|
|
{
|
|
echo "IMAGE=$IMAGE"
|
|
echo "BUILD_JOB=$BUILD_JOB"
|
|
} >> "$GITHUB_ENV"
|
|
|
|
- name: Ensure namespace exists
|
|
run: |
|
|
kubectl apply -f "$WORKSPACE_DIR/deploy/k8s/base/namespace.yaml"
|
|
|
|
- name: Upsert runtime secrets
|
|
env:
|
|
OPERATOR_DASHBOARD_AUTH_PASSWORD: ${{ secrets.OPERATOR_DASHBOARD_AUTH_PASSWORD }}
|
|
run: |
|
|
if [ -z "$OPERATOR_DASHBOARD_AUTH_PASSWORD" ]; then
|
|
echo "missing required repo action secret OPERATOR_DASHBOARD_AUTH_PASSWORD" >&2
|
|
exit 1
|
|
fi
|
|
|
|
patch_file="$(mktemp)"
|
|
cleanup() {
|
|
rm -f "$patch_file"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
python3 - "$OPERATOR_DASHBOARD_AUTH_PASSWORD" >"$patch_file" <<'PY'
|
|
import json
|
|
import sys
|
|
|
|
print(json.dumps({
|
|
"stringData": {
|
|
"OPERATOR_DASHBOARD_AUTH_PASSWORD": sys.argv[1],
|
|
},
|
|
}))
|
|
PY
|
|
|
|
kubectl -n "$PROJECT_NAMESPACE" patch secret "${PROJECT_NAME}-secrets" \
|
|
--type merge \
|
|
--patch-file "$patch_file"
|
|
|
|
- name: Build and push image in-cluster
|
|
env:
|
|
REPO_TOKEN: ${{ github.token }}
|
|
run: |
|
|
kubectl -n "$PROJECT_NAMESPACE" delete job "$BUILD_JOB" --ignore-not-found=true
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: batch/v1
|
|
kind: Job
|
|
metadata:
|
|
name: ${BUILD_JOB}
|
|
namespace: ${PROJECT_NAMESPACE}
|
|
spec:
|
|
backoffLimit: 0
|
|
ttlSecondsAfterFinished: 3600
|
|
template:
|
|
spec:
|
|
restartPolicy: Never
|
|
volumes:
|
|
- name: workspace
|
|
emptyDir: {}
|
|
- name: registry-creds
|
|
secret:
|
|
secretName: ${PROJECT_REGISTRY_SECRET_NAME}
|
|
items:
|
|
- key: .dockerconfigjson
|
|
path: config.json
|
|
initContainers:
|
|
- name: checkout
|
|
image: alpine/git:2.47.2
|
|
env:
|
|
- name: REPO_TOKEN
|
|
value: ${REPO_TOKEN}
|
|
- name: REPO_CLONE_URL
|
|
value: ${REPO_CLONE_URL}
|
|
- name: GITHUB_SHA
|
|
value: ${GITHUB_SHA}
|
|
command: ["/bin/sh", "-lc"]
|
|
args:
|
|
- >-
|
|
git -c credential.username=oauth2 -c http.extraHeader="Authorization: Bearer ${REPO_TOKEN}" clone --depth=1 "${REPO_CLONE_URL}" /workspace &&
|
|
cd /workspace &&
|
|
git -c credential.username=oauth2 -c http.extraHeader="Authorization: Bearer ${REPO_TOKEN}" fetch --depth=1 origin "${GITHUB_SHA}" &&
|
|
git checkout --detach "${GITHUB_SHA}"
|
|
volumeMounts:
|
|
- name: workspace
|
|
mountPath: /workspace
|
|
containers:
|
|
- name: kaniko
|
|
image: gcr.io/kaniko-project/executor:v1.23.2-debug
|
|
args:
|
|
- --context=/workspace
|
|
- --dockerfile=/workspace/Dockerfile
|
|
- --destination=${IMAGE}
|
|
- --cache=false
|
|
volumeMounts:
|
|
- name: workspace
|
|
mountPath: /workspace
|
|
- name: registry-creds
|
|
mountPath: /kaniko/.docker
|
|
EOF
|
|
kubectl -n "$PROJECT_NAMESPACE" wait --for=condition=Complete --timeout=20m "job/$BUILD_JOB"
|
|
kubectl -n "$PROJECT_NAMESPACE" logs "job/$BUILD_JOB"
|
|
|
|
- name: Apply release manifests and wait for rollout
|
|
run: |
|
|
# Apply the rendered image after the build so no deployment ever falls back to bootstrap placeholders.
|
|
kubectl kustomize "$WORKSPACE_DIR/deploy/k8s/base" \
|
|
| python3 "$WORKSPACE_DIR/scripts/deploy/render_release_manifest.py" --image "$IMAGE" \
|
|
| kubectl apply -f -
|
|
|
|
python3 "$WORKSPACE_DIR/scripts/deploy/repo_deployments.py" --format lines \
|
|
| while IFS= read -r deployment; do
|
|
[ -n "$deployment" ] || continue
|
|
kubectl -n "$PROJECT_NAMESPACE" set image "deployment/$deployment" app="$IMAGE"
|
|
kubectl -n "$PROJECT_NAMESPACE" rollout status "deployment/$deployment" --timeout=180s
|
|
done
|