# Builds and publishes the grafana-mcp (Grafana MCP server) container image # to the Gitea registry. This repo deliberately contains no build process # that depends on Hermes' container — Gitea Actions builds and pushes it # standalone. # # Triggers: # - push to main (e.g. bumping the Dockerfile default version ARG) # - weekly schedule, Mon 06:00 UTC — resolves the newest upstream release # and builds it if not yet published, ~7h BEFORE the cluster0 # image-update cron (Mon 13:00 UTC) so the Monday pass picks it up like # any other app. # - workflow_dispatch with an explicit version input name: build on: push: branches: [main] schedule: - cron: '0 6 * * 1' workflow_dispatch: inputs: version: description: 'grafana/mcp-grafana GitHub release (empty = latest)' required: false default: '' suffix: description: 'Image tag suffix for rebuilds of the same upstream version (e.g. -1)' required: false default: '' env: REGISTRY: gitea.williammiceli.systems IMAGE: gitea.williammiceli.systems/hermes/grafana-mcp jobs: build: runs-on: ubuntu-latest # cluster0 Gitea runner (registers this label; has docker + network) # Minimal explicit scopes: read code (checkout), write packages (registry push). permissions: contents: read packages: write steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 1 - name: Resolve upstream version id: ver run: | if [ -n "${{ github.event.inputs.version }}" ]; then VER="${{ github.event.inputs.version }}" else VER=$(curl -fsSL https://api.github.com/repos/grafana/mcp-grafana/releases/latest \ | python3 -c "import sys,json;print(json.load(sys.stdin)['tag_name'])") fi [ -n "$VER" ] || { echo "could not resolve upstream version"; exit 1; } echo "version=$VER" >> "$GITHUB_OUTPUT" # Image tag strips a leading 'v' from the release tag (v1.2.3 -> v1.2.3), # so the tag is always v regardless of upstream's tag style. # An optional workflow_dispatch suffix (-1) allows rebuilds of the same # upstream version (e.g. image fixes) without overwriting the original tag. echo "tag=${VER#v}${{ github.event.inputs.suffix }}" >> "$GITHUB_OUTPUT" echo "Resolved grafana/mcp-grafana version: $VER" - name: Skip if already published id: skip run: | if docker manifest inspect "$IMAGE:v${{ steps.ver.outputs.tag }}" >/dev/null 2>&1; then echo "Image $IMAGE:v${{ steps.ver.outputs.tag }} already exists — nothing to do." echo "skip=true" >> "$GITHUB_OUTPUT" else echo "skip=false" >> "$GITHUB_OUTPUT" fi - name: Configure registry credentials # Dedicated scoped PAT (repo Actions secret REGISTRY_TOKEN) — Gitea's # container registry rejects scope requests from the Actions job token # (upstream issue #23642). Write config.json directly; no login ping. run: | mkdir -p "$HOME/.docker" AUTH=$(printf 'Hermes:%s' "${{ secrets.REGISTRY_TOKEN }}" | base64 -w0) printf '{"auths":{"%s":{"auth":"%s"}}}\n' "$REGISTRY" "$AUTH" > "$HOME/.docker/config.json" chmod 600 "$HOME/.docker/config.json" echo "docker config.json written for $REGISTRY" - name: Diagnose registry auth (runs only if a later step fails) if: failure() # Prints only metadata (lengths, status codes) — never the token itself. run: | T="${{ secrets.REGISTRY_TOKEN }}" echo "REGISTRY_TOKEN length: ${#T} (0 = secret empty or missing)" echo "--- /v2/ ping with basic auth ---" curl -s -o /dev/null -w "HTTP %{http_code}\n" -u "Hermes:$T" \ "https://gitea.williammiceli.systems/v2/" || true echo "--- bearer-token flow (what docker push does after a 401 challenge) ---" BT=$(curl -s -u "Hermes:$T" -G "https://gitea.williammiceli.systems/v2/token" \ --data-urlencode "service=container_registry" \ --data-urlencode "scope=repository:hermes/grafana-mcp:pull,push" \ | python3 -c "import sys,json;print(json.load(sys.stdin).get('token',''))") echo "bearer token length: ${#BT}" curl -s -o /dev/null -w "bearer tags/list: HTTP %{http_code}\n" \ -H "Authorization: Bearer $BT" \ "https://gitea.williammiceli.systems/v2/hermes/grafana-mcp/tags/list" || true - name: Build if: steps.skip.outputs.skip != 'true' run: | VER="${{ steps.ver.outputs.version }}" TAG="${{ steps.ver.outputs.tag }}" docker build --build-arg MCP_GRAFANA_VERSION="$VER" \ -t "$IMAGE:v$TAG" -t "$IMAGE:latest" . - name: Push if: steps.skip.outputs.skip != 'true' run: | TAG="${{ steps.ver.outputs.tag }}" docker push "$IMAGE:v$TAG" docker push "$IMAGE:latest"