diff --git a/gitea-action/README.md b/gitea-action/README.md index bc0816a..e9e75aa 100644 --- a/gitea-action/README.md +++ b/gitea-action/README.md @@ -14,7 +14,10 @@ jobs: steps: - uses: gitea.example.com/shcizo/package-updater/gitea-action@v1 with: - endpoint: https://updater.example.com/update + endpoint: | + https://updater-swarm.example.com/update + https://updater-web01.example.com/update + https://updater-web02.example.com/update image: registry.example.com/${{ gitea.repository }} tag: ${{ gitea.sha }} token: ${{ secrets.UPDATER_TOKEN }} @@ -22,15 +25,24 @@ jobs: `UPDATER_TOKEN` should be set as an organisation-level secret in Gitea so all repos share it. +All instances in the fleet must share the same bearer token, since one `token` +input is sent to every endpoint. + ## Inputs | Name | Required | Default | Description | |---|---|---|---| -| `endpoint` | yes | — | Full URL to `/update` | +| `endpoint` | yes | — | Full URL to `/update`. Several may be given, one per line, to update a fleet. | | `image` | yes | — | Image reference without tag | | `tag` | no | `""` | Tag that was just pushed (logged for audit) | | `token` | yes | — | Bearer token configured in package-updater | ## Failure modes -The step exits non-zero if `package-updater` returns HTTP 4xx or 5xx. This is intentional — the workflow surfaces the deploy failure to whoever pushed. +Every endpoint is attempted, even when an earlier one fails — otherwise one dead +server would leave the rest of the fleet un-updated, and the CI log would not show +which hosts actually succeeded. + +The step exits non-zero if any endpoint returned 4xx/5xx or was unreachable. The +log lists each endpoint with its HTTP status and response body, so a partial +deploy is visible at a glance. diff --git a/gitea-action/action.yml b/gitea-action/action.yml index 19148e8..0ec1826 100644 --- a/gitea-action/action.yml +++ b/gitea-action/action.yml @@ -1,8 +1,8 @@ name: "Deploy via package-updater" -description: "Notifies package-updater to pull & restart a Docker Compose service" +description: "Notifies one or more package-updater instances to pull & restart a service" inputs: endpoint: - description: "Full URL to /update (e.g. https://updater.example.com/update)" + description: "Full URL to /update. Give several, one per line, to update a fleet." required: true image: description: "Image reference without tag (e.g. registry.example.com/myapp)" @@ -20,18 +20,60 @@ runs: - name: Trigger update shell: bash env: + ENDPOINTS: ${{ inputs.endpoint }} + IMAGE: ${{ inputs.image }} + TAG: ${{ inputs.tag }} TOKEN: ${{ inputs.token }} run: | - set -euo pipefail - response=$(curl -sS -w "\n%{http_code}" \ - -X POST "${{ inputs.endpoint }}" \ - -H "Authorization: Bearer $TOKEN" \ - -H "Content-Type: application/json" \ - -d "{\"image\":\"${{ inputs.image }}\",\"tag\":\"${{ inputs.tag }}\"}") - body=$(echo "$response" | head -n -1) - code=$(echo "$response" | tail -n 1) - echo "HTTP $code" - echo "$body" | jq . - if [ "$code" -ge 400 ]; then + # No `set -e`: a failing endpoint must not abort the loop, or one dead + # server leaves the rest of the fleet un-updated and hides which hosts + # actually succeeded. + set -uo pipefail + + payload=$(jq -nc --arg image "$IMAGE" --arg tag "$TAG" \ + '{image: $image, tag: $tag}') + + attempted=0 + failed=0 + + while IFS= read -r endpoint; do + # URLs never contain whitespace, so stripping all of it safely + # handles indentation, blank lines and CRLF line endings. + endpoint=$(printf '%s' "$endpoint" | tr -d '[:space:]') + [ -z "$endpoint" ] && continue + + attempted=$((attempted + 1)) + echo "--- $endpoint" + + if ! response=$(curl -sS -w "\n%{http_code}" \ + -X POST "$endpoint" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d "$payload"); then + echo "unreachable" + failed=$((failed + 1)) + continue + fi + + # Split on the last newline: curl's -w appended the status code + # there. Pure bash — `head -n -1` is GNU-only and fails on + # BSD/macOS, so the fan-out could not be tested locally. + code="${response##*$'\n'}" + body="${response%$'\n'*}" + echo "HTTP $code" + printf '%s' "$body" | jq . || printf '%s\n' "$body" + + if [ "$code" -ge 400 ]; then + failed=$((failed + 1)) + fi + done <<< "$ENDPOINTS" + + if [ "$attempted" -eq 0 ]; then + echo "no endpoints given" exit 1 fi + if [ "$failed" -gt 0 ]; then + echo "$failed of $attempted endpoint(s) failed" + exit 1 + fi + echo "all $attempted endpoint(s) updated"