name: Deploy Hugo Blog to k3s on: push: branches: [main] workflow_dispatch: env: REGISTRY: git.luochen570.cn IMAGE: luochen570/hugo-blog K8S_NAMESPACE: hugo K8S_DEPLOYMENT: hugo-blog K8S_CONTAINER: nginx K8S_INGRESS: hugo-blog TEST_HOST: hugo.test.luochen570.cn PROD_HOST: hugo.luochen570.cn DOCKER_HOST: unix:///run/user/1000/docker.sock jobs: build-and-deploy: runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout uses: actions/checkout@v4 with: submodules: recursive - name: Setup Hugo run: | set -euo pipefail HUGO_VERSION="0.161.1" HUGO_DEB="hugo_extended_${HUGO_VERSION}_linux-amd64.deb" HUGO_URL="https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/${HUGO_DEB}" apt-get update apt-get install -y --no-install-recommends ca-certificates curl download_hugo() { local proxy_name="$1" shift echo "Trying Hugo download via ${proxy_name}" rm -f "/tmp/${HUGO_DEB}" env "$@" curl -fL --connect-timeout 20 --retry 5 --retry-all-errors --retry-delay 5 \ -o "/tmp/${HUGO_DEB}" "$HUGO_URL" } download_hugo "direct" \ HTTP_PROXY= HTTPS_PROXY= ALL_PROXY= http_proxy= https_proxy= all_proxy= \ || download_hugo "cluster mihomo" \ HTTP_PROXY=http://mihomo.mihomo.svc.cluster.local:7890 HTTPS_PROXY=http://mihomo.mihomo.svc.cluster.local:7890 ALL_PROXY=socks5://mihomo.mihomo.svc.cluster.local:7890 \ http_proxy=http://mihomo.mihomo.svc.cluster.local:7890 https_proxy=http://mihomo.mihomo.svc.cluster.local:7890 all_proxy=socks5://mihomo.mihomo.svc.cluster.local:7890 \ NO_PROXY=localhost,127.0.0.1,.svc,.cluster.local,gitea-http.gitea.svc.cluster.local,git.luochen570.cn,mihomo.mihomo.svc.cluster.local dpkg -i "/tmp/${HUGO_DEB}" hugo version | grep "v${HUGO_VERSION}" - name: Hugo Build run: | set -euo pipefail rm -rf public hugo --minify test -s public/index.html if grep -q "Welcome to nginx" public/index.html; then echo "public/index.html is nginx default page, aborting" >&2 exit 1 fi grep -q "洛尘的技术笔记" public/index.html - name: Build and Push Docker Image env: REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} run: | set -euo pipefail # k3s dind-rootless runner: job containers do not have the runner's # Unix socket. Prefer the exposed dind TCP API when available, while # keeping the Unix socket as a fallback for host-style execution. unset DOCKER_TLS_VERIFY DOCKER_CERT_PATH DOCKER_CONTEXT # Avoid proxy pollution in the container/rootless Docker path; proxy envs # can break image pulls through the slirp4netns namespace. unset HTTP_PROXY HTTPS_PROXY ALL_PROXY http_proxy https_proxy all_proxy if docker -H unix:///run/user/1000/docker.sock version >/dev/null 2>&1; then export DOCKER_HOST="unix:///run/user/1000/docker.sock" elif docker -H tcp://127.0.0.1:2375 version >/dev/null 2>&1; then export DOCKER_HOST="tcp://127.0.0.1:2375" elif docker -H tcp://localhost:2375 version >/dev/null 2>&1; then export DOCKER_HOST="tcp://localhost:2375" else echo "No usable Docker endpoint found" >&2 exit 1 fi if [ -z "${REGISTRY_TOKEN:-}" ]; then echo "REGISTRY_TOKEN secret is required" >&2 exit 1 fi IMAGE_TAG="${GITHUB_SHA}" docker version echo "$REGISTRY_TOKEN" | docker login "$REGISTRY" -u luochen570 --password-stdin docker build \ -t "$REGISTRY/$IMAGE:$IMAGE_TAG" \ -t "$REGISTRY/$IMAGE:latest" \ . docker push "$REGISTRY/$IMAGE:$IMAGE_TAG" docker push "$REGISTRY/$IMAGE:latest" - name: Deploy to k3s env: KUBECONFIG_B64: ${{ secrets.KUBECONFIG_B64 }} run: | set -euo pipefail if [ -z "${KUBECONFIG_B64:-}" ]; then echo "KUBECONFIG_B64 secret is required" >&2 exit 1 fi KUBECONFIG_FILE="$(mktemp)" trap 'rm -f "$KUBECONFIG_FILE" /tmp/kubectl' EXIT echo "$KUBECONFIG_B64" | base64 -d > "$KUBECONFIG_FILE" chmod 600 "$KUBECONFIG_FILE" KUBECTL_VERSION="v1.32.0" download_kubectl() { local proxy_name="$1" shift echo "Trying kubectl download via ${proxy_name}" rm -f /tmp/kubectl env "$@" curl -fL --connect-timeout 20 --retry 5 --retry-all-errors --retry-delay 5 \ -o /tmp/kubectl "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl" } download_kubectl "direct" \ HTTP_PROXY= HTTPS_PROXY= ALL_PROXY= http_proxy= https_proxy= all_proxy= \ || download_kubectl "cluster mihomo" \ HTTP_PROXY=http://mihomo.mihomo.svc.cluster.local:7890 HTTPS_PROXY=http://mihomo.mihomo.svc.cluster.local:7890 ALL_PROXY=socks5://mihomo.mihomo.svc.cluster.local:7890 \ http_proxy=http://mihomo.mihomo.svc.cluster.local:7890 https_proxy=http://mihomo.mihomo.svc.cluster.local:7890 all_proxy=socks5://mihomo.mihomo.svc.cluster.local:7890 \ NO_PROXY=localhost,127.0.0.1,.svc,.cluster.local,gitea-http.gitea.svc.cluster.local,git.luochen570.cn,mihomo.mihomo.svc.cluster.local chmod +x /tmp/kubectl if /tmp/kubectl --kubeconfig "$KUBECONFIG_FILE" \ auth can-i get ingresses -n "$K8S_NAMESPACE" >/dev/null 2>&1; then ingress_hosts="$(/tmp/kubectl --kubeconfig "$KUBECONFIG_FILE" \ get ingress "$K8S_INGRESS" -n "$K8S_NAMESPACE" \ --ignore-not-found \ -o jsonpath='{range .spec.rules[*]}{.host}{"\n"}{end}{range .spec.tls[*].hosts[*]}{.}{"\n"}{end}')" printf '%s\n' "$ingress_hosts" | grep -Fx "$PROD_HOST" >/dev/null if printf '%s\n' "$ingress_hosts" | grep -Fx "$TEST_HOST" >/dev/null; then echo "Ingress also contains test host $TEST_HOST; continuing because production host $PROD_HOST is present" fi else echo "Kubeconfig cannot read ingresses; relying on K8S_NAMESPACE/K8S_DEPLOYMENT scope: $K8S_NAMESPACE/$K8S_DEPLOYMENT" fi IMAGE_REF="$REGISTRY/$IMAGE:${GITHUB_SHA}" /tmp/kubectl --kubeconfig "$KUBECONFIG_FILE" \ set image "deployment/$K8S_DEPLOYMENT" -n "$K8S_NAMESPACE" \ "$K8S_CONTAINER=$IMAGE_REF" /tmp/kubectl --kubeconfig "$KUBECONFIG_FILE" \ annotate "deployment/$K8S_DEPLOYMENT" -n "$K8S_NAMESPACE" \ "hugo.luochen570.cn/git-sha=${GITHUB_SHA}" \ "hugo.luochen570.cn/image=$IMAGE_REF" \ --overwrite /tmp/kubectl --kubeconfig "$KUBECONFIG_FILE" \ rollout status "deployment/$K8S_DEPLOYMENT" -n "$K8S_NAMESPACE" --timeout=120s