commit a174f823f3db10d64337009db8e59d6463128e2d Author: Anders Olsson Date: Sat May 23 22:37:57 2026 +0200 feat: initial composite action Co-Authored-By: Claude Opus 4.7 (1M context) diff --git a/README.md b/README.md new file mode 100644 index 0000000..95fad2b --- /dev/null +++ b/README.md @@ -0,0 +1,89 @@ +# notify-image-updater + +Composite action that POSTs a CloudEvents v1.0 payload to the in-cluster +`argocd-image-updater` webhook so it reconciles the matching `ImageUpdater` +CR immediately instead of waiting for the next 30s poll tick. + +Pair it with `docker/build-push-action` (or any step that publishes an +image and exposes a `digest` output). + +## Usage + +```yaml +jobs: + build: + runs-on: aceofba-cluster + container: + image: ghcr.io/catthehacker/ubuntu:act-22.04 + env: + IMAGE_UPDATER_WEBHOOK_SECRET: ${{ secrets.IMAGE_UPDATER_WEBHOOK_SECRET }} + steps: + - uses: actions/checkout@v4 + + - uses: https://git.aceofba.se/infra/setup-buildx@v1 + + - uses: docker/login-action@v3 + with: + registry: git.aceofba.se + username: ${{ gitea.actor }} + password: ${{ secrets.GITEA_TOKEN }} + + - id: meta + uses: docker/metadata-action@v5 + with: + images: git.aceofba.se/${{ gitea.repository }} + tags: | + type=sha,prefix=,format=short + type=raw,value=latest,enable={{is_default_branch}} + + - id: build + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - uses: https://git.aceofba.se/infra/notify-image-updater@v1 + with: + image: git.aceofba.se/${{ gitea.repository }} + tag: ${{ steps.meta.outputs.version }} + digest: ${{ steps.build.outputs.digest }} +``` + +## Inputs + +| Name | Required | Default | Description | +|------|----------|---------|-------------| +| `image` | yes | – | Fully-prefixed image repository, e.g. `git.aceofba.se/owner/repo`. Must match `data.repositoryName` matching in argocd-image-updater's registry config. | +| `tag` | yes | – | Tag that was just pushed. | +| `digest` | yes | – | Image digest. Use `${{ steps..outputs.digest }}` from `docker/build-push-action`. | +| `secret` | no | `${{ env.IMAGE_UPDATER_WEBHOOK_SECRET }}` | CloudEvents shared secret. Read by default from the `IMAGE_UPDATER_WEBHOOK_SECRET` env var (set it from a repo or org Gitea Actions secret). | +| `webhook-url` | no | `http://argocd-image-updater.argocd.svc.cluster.local:8080/webhook` | Webhook endpoint. Only override for non-default clusters. | + +## Setting up the secret + +Add an org-level Gitea Actions secret named `IMAGE_UPDATER_WEBHOOK_SECRET` +with the value from +`infra/clusters` → `base/argo-cd-image-updater/manifests/webhook-secret.yaml` +(`stringData.CLOUDEVENTS_WEBHOOK_SECRET`). Repos that build images then +just expose it via `env:` at the job level. + +## Why + +The controller polls every 30s by default — fine for most cases but slow +when iterating. With a webhook hit at the end of the build, the +ImageUpdater controller reconciles immediately, finds the new tag, and +pushes the manifest bump. Combined with a Gitea→Argo CD repo webhook, the +end-to-end CI-to-deploy latency drops from minutes to seconds. + +## Notes + +- The cluster's argocd-image-updater service is ClusterIP-only; this + action only works from in-cluster runners (which is what + `aceofba-cluster` is). +- A non-2xx response fails the step. Argo CD reconciliation latency + itself is not part of this action — once the manifest commit lands, + Argo CD picks it up via its own webhook from Gitea. diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..f525de6 --- /dev/null +++ b/action.yml @@ -0,0 +1,62 @@ +name: notify-image-updater +description: | + POSTs a CloudEvents v1.0 envelope to the in-cluster argocd-image-updater + webhook so the controller reconciles the matching ImageUpdater CR + immediately instead of waiting for the next 30s poll. Pair this with the + docker/build-push-action step that publishes the image. + +inputs: + image: + description: 'Fully-prefixed image repository, e.g. git.aceofba.se//.' + required: true + tag: + description: 'Tag that was just pushed.' + required: true + digest: + description: 'Image digest from docker/build-push-action (steps..outputs.digest).' + required: true + secret: + description: 'CloudEvents shared secret. Defaults to the IMAGE_UPDATER_WEBHOOK_SECRET repo/org secret.' + required: false + default: ${{ env.IMAGE_UPDATER_WEBHOOK_SECRET }} + webhook-url: + description: 'Webhook endpoint. Override only when running against a non-default cluster.' + required: false + default: 'http://argocd-image-updater.argocd.svc.cluster.local:8080/webhook' + +runs: + using: composite + steps: + - name: Notify argocd-image-updater + shell: bash + env: + WEBHOOK_URL: ${{ inputs.webhook-url }} + WEBHOOK_SECRET: ${{ inputs.secret }} + IMAGE: ${{ inputs.image }} + TAG: ${{ inputs.tag }} + DIGEST: ${{ inputs.digest }} + run: | + set -euo pipefail + if [[ -z "${WEBHOOK_SECRET}" ]]; then + echo "::error::IMAGE_UPDATER_WEBHOOK_SECRET is empty. Set it as a repo/org secret or pass 'secret:' to this action." >&2 + exit 1 + fi + curl -fsS -X POST \ + "${WEBHOOK_URL}?type=cloudevents&secret=${WEBHOOK_SECRET}" \ + -H 'Content-Type: application/json' \ + -d @- <