chore(deps): Bump the actions group across 1 directory with 7 updates… #55
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # instant.dev/provisioner — Auto-deploy on push to master | |
| # | |
| # Why this exists: | |
| # On 2026-05-15, a worker code fix shipped to master but was never | |
| # deployed — an operator had to manually `docker buildx build && | |
| # kubectl set image`. A user received the same broken expiry email | |
| # twice as a result. This workflow eliminates that gap for every | |
| # backend service, including this one. | |
| # | |
| # Build context note: | |
| # The Dockerfile expects to be invoked from the parent of provisioner/, | |
| # with sibling common/ and proto/ directories present. In CI we mirror | |
| # that by checking out: | |
| # . (workspace root) | |
| # ├── provisioner/ (this repo) | |
| # ├── common/ (sibling repo) | |
| # └── proto/ (sibling repo) | |
| # then `docker buildx build -f provisioner/Dockerfile .` from root. | |
| # | |
| # The Dockerfile has two named final stages (debug, prod). The default | |
| # when no --target is passed is the LAST named stage — `prod` — exactly | |
| # what we want here. | |
| # | |
| # Required repo secret: | |
| # KUBECONFIG_B64 — base64-encoded kubeconfig with permission to | |
| # `kubectl set image deployment/instant-provisioner -n instant-infra`. | |
| # | |
| # GHCR auth uses the per-job GITHUB_TOKEN with `packages: write`. | |
| name: Deploy | |
| on: | |
| push: | |
| branches: [master] | |
| # CI-minute savings (2026-05-21): skip Deploy on docs-only commits. | |
| paths-ignore: | |
| - '**.md' | |
| - 'docs/**' | |
| - 'CLAUDE.md' | |
| - '.gitignore' | |
| - 'LICENSE' | |
| - 'BUGBASH-*/**' | |
| workflow_dispatch: | |
| concurrency: | |
| # CI-minute savings (2026-05-21): cancel prior in-flight Deploy when a | |
| # newer commit lands on the same branch. | |
| group: deploy-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| packages: write | |
| env: | |
| IMAGE_REPO: ghcr.io/instanode-dev/instant-provisioner | |
| K8S_NAMESPACE: instant-infra | |
| K8S_DEPLOYMENT: instant-provisioner | |
| K8S_CONTAINER: provisioner | |
| jobs: | |
| deploy: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout provisioner (this repo) into ./provisioner | |
| uses: actions/checkout@v6 | |
| with: | |
| path: provisioner | |
| - name: Checkout common sibling into ./common | |
| uses: actions/checkout@v6 | |
| with: | |
| repository: ${{ vars.COMMON_REPO || format('{0}/common', github.repository_owner) }} | |
| # 2026-05-15: GITHUB_TOKEN is scoped to THIS repo only and 404s | |
| # on private sibling repos. REPO_ACCESS_TOKEN is a fine-grained | |
| # PAT with read access to InstaNode-dev/{common,proto}. | |
| token: ${{ secrets.REPO_ACCESS_TOKEN }} | |
| path: common | |
| - name: Checkout proto sibling into ./proto | |
| uses: actions/checkout@v6 | |
| with: | |
| repository: ${{ vars.PROTO_REPO || format('{0}/proto', github.repository_owner) }} | |
| token: ${{ secrets.REPO_ACCESS_TOKEN }} | |
| path: proto | |
| - name: Compute build metadata | |
| id: meta | |
| run: | | |
| SHORT_SHA="${GITHUB_SHA:0:7}" | |
| BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)" | |
| VERSION="master-${SHORT_SHA}" | |
| echo "short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT" | |
| echo "build_time=${BUILD_TIME}" >> "$GITHUB_OUTPUT" | |
| echo "version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "Built ${VERSION} (${BUILD_TIME})" | |
| - name: Set up Go (for unit tests + go.mod replace directives) | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: '1.24' | |
| - name: Run unit tests (short, no integration deps) | |
| # go.mod uses `replace instant.dev/common => ../common` and | |
| # `replace instant.dev/proto => ../proto`. When `go test` runs | |
| # inside ./provisioner, the relative paths resolve to ./common | |
| # and ./proto in the workspace root — already correct. | |
| working-directory: provisioner | |
| run: go test ./... -short -count=1 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build and push image | |
| # Build context = workspace root so Dockerfile's | |
| # `COPY proto/`, `COPY common/`, `COPY provisioner/` all resolve. | |
| run: | | |
| docker buildx build \ | |
| --platform linux/amd64 \ | |
| -f provisioner/Dockerfile \ | |
| --build-arg GIT_SHA="${{ steps.meta.outputs.short_sha }}" \ | |
| --build-arg BUILD_TIME="${{ steps.meta.outputs.build_time }}" \ | |
| --build-arg VERSION="${{ steps.meta.outputs.version }}" \ | |
| -t "${IMAGE_REPO}:${{ steps.meta.outputs.version }}" \ | |
| -t "${IMAGE_REPO}:latest" \ | |
| --push \ | |
| . | |
| - name: Set up kubectl | |
| uses: azure/setup-kubectl@v5 | |
| with: | |
| version: 'latest' | |
| - name: Configure kubeconfig from KUBECONFIG_B64 secret | |
| env: | |
| KUBECONFIG_B64: ${{ secrets.KUBECONFIG_B64 }} | |
| run: | | |
| if [ -z "${KUBECONFIG_B64}" ]; then | |
| echo "::error::KUBECONFIG_B64 repo secret is not set. Add it under Settings → Secrets → Actions." | |
| exit 1 | |
| fi | |
| mkdir -p "$HOME/.kube" | |
| echo "$KUBECONFIG_B64" | base64 -d > "$HOME/.kube/config" | |
| chmod 600 "$HOME/.kube/config" | |
| kubectl version --client=true | |
| - name: Roll out new image | |
| run: | | |
| IMAGE="${IMAGE_REPO}:${{ steps.meta.outputs.version }}" | |
| echo "Setting ${K8S_DEPLOYMENT}.${K8S_CONTAINER} to ${IMAGE}" | |
| kubectl set image \ | |
| "deployment/${K8S_DEPLOYMENT}" \ | |
| "${K8S_CONTAINER}=${IMAGE}" \ | |
| -n "${K8S_NAMESPACE}" | |
| kubectl rollout status \ | |
| "deployment/${K8S_DEPLOYMENT}" \ | |
| -n "${K8S_NAMESPACE}" \ | |
| --timeout=180s | |
| - name: Verify rolled-out image tag matches built SHA (rule-14 gate) | |
| # CLAUDE.md rule 14 build-SHA gate. The provisioner has no public | |
| # ingress (gRPC on :50051 + a /healthz sidecar on :8092, both | |
| # cluster-internal), so there is no external URL to curl. | |
| # | |
| # An earlier revision tried `kubectl port-forward` of :8092 from the | |
| # runner — that proved flaky (apiserver-proxied port-forward against | |
| # a distroless pod behind the task-#115 NetworkPolicies returns empty | |
| # bodies intermittently) and was a false deploy failure even though | |
| # the pod was healthy. | |
| # | |
| # The deterministic, network-free gate: the image tag IS the SHA | |
| # (`master-<short_sha>`). Assert the live deployment references the | |
| # exact tag we built; `kubectl rollout status` above already | |
| # confirmed the new pod passed its readinessProbe — which is itself | |
| # an httpGet of /healthz:8092, so a Ready pod IS a /healthz-verified | |
| # pod. Tag match + Ready = SHA verified in prod. | |
| run: | | |
| ROLLED=$(kubectl get deployment "${K8S_DEPLOYMENT}" -n "${K8S_NAMESPACE}" \ | |
| -o jsonpath="{.spec.template.spec.containers[?(@.name=='${K8S_CONTAINER}')].image}") | |
| EXPECTED="${IMAGE_REPO}:${{ steps.meta.outputs.version }}" | |
| echo "Live image: ${ROLLED}" | |
| echo "Expected: ${EXPECTED}" | |
| if [ "${ROLLED}" != "${EXPECTED}" ]; then | |
| echo "::error::Rolled image (${ROLLED}) != expected (${EXPECTED})" | |
| exit 1 | |
| fi | |
| echo "Confirmed ${K8S_DEPLOYMENT} is running ${EXPECTED} (SHA ${{ steps.meta.outputs.short_sha }})" |