feat(workflows): add mcp-server-deploy reusable workflow - #11
Merged
Conversation
Canonical deploy workflow for wyre-technology/*-mcp repositories.
Companion to mcp-server-ci.yml, finishing the per-repo workflow
factorization (CI was already shared; deploy was still 8x copy-paste,
which is how we ended up with autotask-mcp silently shipping every
release into an orphaned ACA for 3+ weeks).
What this workflow does:
- Logs into Azure via OIDC
- Deploys the caller's image to gwp-<vendor-slug> by IMMUTABLE digest
- Hard-fails if the digest input is empty or not sha256:-prefixed
- Sets IMAGE_VERSION env var on the new revision tying it back to
release version + git SHA + workflow run ID for forensics
Failure patterns this fixes for the fleet:
1. Deploying to mcpgw-prod-<vendor> instead of gwp-<vendor>. The
gateway routes vendor traffic per VENDOR_URL_<VENDOR> env, all of
which point to gwp-<vendor>. The mcpgw-prod-* ACAs are orphaned
legacy from a prior naming convention. Releases shipped to them
are silent no-ops.
2. Deploying by :latest tag. :latest is mutable and can resolve to a
stale digest through GHCR edge caches or ACA's image-pull cache,
making deploys "succeed" while actually rolling onto the prior
image (observed 2026-05-13 with autotask-mcp PRs #95 and #96).
Caller contract (release.yml):
- docker job must expose `outputs.digest:
${{ steps.<push-step>.outputs.digest }}`
- deploy job replaces its inline body with `uses:
wyre-technology/.github/.github/workflows/mcp-server-deploy.yml@<sha>`
passing vendor-slug, image-name, digest, version. Secrets propagate
via `secrets: inherit` or explicit forwarding.
Will be rolled out to autotask-mcp first as the reference call site,
then to halopsa-mcp, datto-rmm-mcp, itglue-mcp, huntress-mcp,
liongard-mcp, domotz-mcp, cipp-mcp in follow-up PRs.
See related skills:
- ci-docker-semantic-release-version-race
- mcp-gateway-troubleshooting
This was referenced May 13, 2026
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Companion to `mcp-server-ci.yml`, finishing the per-repo workflow factorization. CI was already shared; deploy was still 8x copy-paste, which is how we ended up with autotask-mcp silently shipping every release into an orphaned ACA for 3+ weeks.
What this workflow does
Failure patterns this fixes for the fleet
1. Wrong ACA
Every `-mcp` release deploys to `mcpgw-prod-`. The gateway routes vendor traffic per `VENDOR_URL_` env vars — all of which point to `gwp-`. The `mcpgw-prod-` ACAs are orphaned legacy from a prior naming convention. Releases shipped to them are silent no-ops.
Discovered today (2026-05-13): autotask's `gwp-autotask` had last been manually updated on 2026-04-21. Three weeks of releases went to `mcpgw-prod-autotask` and were never reachable by the gateway.
2. Mutable `:latest` tag
Even with the correct ACA, deploying by `:latest` is racy. The tag can resolve to a stale digest through GHCR edge caches or ACA's image-pull cache. The deploy command "succeeds" but the new revision actually rolls onto the prior image.
This was observed twice today on autotask-mcp PRs #95 (TicketHistory) and #96 (attachment fix): release pipeline reported success on every step, GHCR `:latest` correctly pointed at the new digest, ACA reported the new revision active — but the running container was the prior image. Verified by manual digest-pin override.
Caller contract
In each `*-mcp/release.yml`:
`docker` job must expose its push digest as a job output:
```yaml
docker:
...
outputs:
digest: ${{ steps.push.outputs.digest }}
steps:
- id: push
uses: docker/build-push-action@...
```
`deploy` job becomes a thin caller:
```yaml
deploy:
needs: [release, docker]
uses: wyre-technology/.github/.github/workflows/mcp-server-deploy.yml@
with:
vendor-slug: autotask
image-name: ghcr.io/wyre-technology/autotask-mcp
digest: ${{ needs.docker.outputs.digest }}
version: ${{ needs.release.outputs.version }}
secrets: inherit
```
Rollout plan
Will be rolled out to autotask-mcp first as the reference call site (its current inline fix from PR #97 gets refactored to use this), then in follow-up PRs to:
After full rollout, the `mcpgw-prod-` ACAs become safe to delete — they're orphaned now and won't be touched by any release pipeline. Tracking that cleanup separately.
Test plan