1- # Manual maintenance hook for an externally reachable self-host stack . This is deliberately not scheduled while
1+ # Manual maintenance hook for externally reachable self-host stacks . This is deliberately not scheduled while
22# the review stack is running without colocated GitHub Actions runners.
3+ #
4+ # #4899: generalized from a single hardcoded instance to a fleet matrix. Each self-host instance is modeled
5+ # as its own GitHub Environment (Settings -> Environments), carrying that instance's own `SELF_HOST_URL`
6+ # environment variable and `INTERNAL_JOB_TOKEN` environment secret -- this is the supported way to give a
7+ # matrix job a different secret VALUE per entry while every job still reads the same secret NAME
8+ # (`secrets.INTERNAL_JOB_TOKEN`), since workflow expressions cannot index `secrets.*` by a computed key.
9+ #
10+ # Setup for N instances:
11+ # 1. Create one GitHub Environment per instance (any name, e.g. "primary", "edge-nl-01", "customer-acme").
12+ # 2. On each environment, set the `SELF_HOST_URL` variable and the `INTERNAL_JOB_TOKEN` secret for that
13+ # instance.
14+ # 3. Set the repo-level `SELF_HOST_ENVIRONMENTS` variable to a comma-separated list of those environment
15+ # names, e.g. `primary,edge-nl-01`.
16+ # A single-instance setup works exactly the same way -- one environment, one name in the list.
317name : self-host maintenance
418
519on :
@@ -13,30 +27,56 @@ concurrency:
1327 cancel-in-progress : false
1428
1529jobs :
30+ discover :
31+ if : ${{ vars.SELF_HOST_ENVIRONMENTS != '' }}
32+ runs-on : ubuntu-latest
33+ timeout-minutes : 5
34+ outputs :
35+ environments : ${{ steps.list.outputs.environments }}
36+ steps :
37+ - name : Parse SELF_HOST_ENVIRONMENTS into a JSON matrix list
38+ id : list
39+ env :
40+ SELF_HOST_ENVIRONMENTS : ${{ vars.SELF_HOST_ENVIRONMENTS }}
41+ run : |
42+ json=$(printf '%s' "$SELF_HOST_ENVIRONMENTS" | tr ',' '\n' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e '/^$/d' | jq -R . | jq -sc .)
43+ echo "environments=$json" >> "$GITHUB_OUTPUT"
44+ echo "Discovered instances: $json"
45+
1646 maintenance :
17- if : ${{ vars.SELF_HOST_URL != '' }}
47+ needs : discover
48+ if : ${{ needs.discover.outputs.environments != '' && needs.discover.outputs.environments != '[]' }}
1849 runs-on : ubuntu-latest
1950 timeout-minutes : 30
51+ strategy :
52+ fail-fast : false
53+ matrix :
54+ environment : ${{ fromJson(needs.discover.outputs.environments) }}
55+ environment : ${{ matrix.environment }}
2056 env :
2157 SELF_HOST_URL : ${{ vars.SELF_HOST_URL }}
2258 INTERNAL_JOB_TOKEN : ${{ secrets.INTERNAL_JOB_TOKEN }}
2359 steps :
24- - name : Self-host health
60+ - name : Self-host health (${{ matrix.environment }})
2561 run : |
62+ if [ -z "$SELF_HOST_URL" ]; then
63+ echo "✗ SELF_HOST_URL not set on the '${{ matrix.environment }}' environment — skipping."
64+ exit 1
65+ fi
2666 if curl -fsS "$SELF_HOST_URL/ready" >/dev/null; then
27- echo "✓ self-host ready"
67+ echo "✓ self-host ready (${{ matrix.environment }}) "
2868 else
29- echo "✗ self-host not ready"
69+ echo "✗ self-host not ready (${{ matrix.environment }}) "
3070 exit 1
3171 fi
3272
33- - name : Refresh the RAG index (fan out over configured repos)
73+ - name : Refresh the RAG index (fan out over configured repos) (${{ matrix.environment }})
3474 run : |
3575 if [ -z "$INTERNAL_JOB_TOKEN" ]; then
36- echo "INTERNAL_JOB_TOKEN not set — skipping RAG refresh (set it as a repo secret to enable) ."
76+ echo "INTERNAL_JOB_TOKEN not set on the '${{ matrix.environment }}' environment — skipping RAG refresh ."
3777 exit 0
3878 fi
3979 code=$(curl -s -o /dev/null -w '%{http_code}' -X POST "$SELF_HOST_URL/v1/internal/jobs/rag-index" \
4080 -H "authorization: Bearer $INTERNAL_JOB_TOKEN" -H "content-type: application/json" -d '{}')
41- echo "RAG re-index request → HTTP $code"
81+ echo "RAG re-index request (${{ matrix.environment }}) → HTTP $code"
4282 [ "$code" = "202" ] || [ "$code" = "200" ]
0 commit comments