[POC - do not merge] TT-18139: release pipelines as reusable workflows - #159
konrad-sol wants to merge 12 commits into
Conversation
Phase 1a of the reusable-workflow spike. Proves the workflow_call boundary only - it does not run goreleaser or build the real image. Exercises OIDC, secrets: inherit, vars resolution against the caller, caller checkout with submodules, and outputs crossing the boundary. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Renames releng-tyk-analytics.yml to release-tyk-analytics.yml. The matrix is gone: every managed repo/branch has exactly one leg today (el7-pgo-build is not enabled anywhere), so it was a single-element data carrier. golang_cross is now a required input instead - it is genuinely per-branch (1.24-bullseye on release-5.3, 1.26-bullseye on master/5.8+). The second matrix leg and the fail_leg input existed only to test re-run granularity, which is confirmed working, so both are removed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e only input
Verified across all 26 generated release.yml files:
rpmvers - 1 distinct value across every repo and branch
debvers - 1 distinct value
goreleaser - 1 distinct value (ci/goreleaser/goreleaser.yml)
cgo - 2 values, but split by product, never within one
(1 for tyk/tyk-analytics/portal/ai-studio, 0 for the rest)
All four are therefore constants for tyk-analytics and move to job env.
golang_cross stays an input because it is genuinely per-branch.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Verbatim port of the 576-line goreleaser job from tyk-analytics'
generated release.yml. The only changes:
- needs/if on dep-guard move to the caller
- single-leg matrix replaced by the golang_cross input plus constant env
- caller workflow-level env reproduced here, since it does not propagate
(DOCKER_BUILD_SUMMARY and DOCKER_BUILD_RECORD_UPLOAD are read from the
environment by docker/build-push-action, never referenced by name)
- dropped the dead publish-leg gate
On that last point: 15 steps carried `matrix.golang_cross == <buildenv>`,
which gromit renders per-branch ('1.24-bullseye' on release-5.3). It exists
to pick the publishing leg when el7-pgo-build adds a second matrix leg, and
no repo enables that - so it is always true. Carrying it as a second input
would mean two inputs that must always agree, and if they ever drifted all
15 publishing steps would skip silently and the job would go green having
pushed nothing. Dropped instead: 8 sole conditions removed, 7 compound ones
reduced to their remaining clause. Behaviour is identical on every branch.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
master's gromit sync b3dbeb753 changed timeout-minutes: 30 -> 45 in the goreleaser job. Applied centrally here instead of in 26 generated files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…flow Moves test-controller-ui, ui-tests, test-controller-api, api-tests, test-controller-distros and upgrade-tests into a second reusable workflow. Two files rather than one because release-tests must stay in tyk-analytics: it calls the repo-local swagger-contract-tests.yml, which cannot be invoked from here. A caller can only depend on a called workflow as a whole, so a single file would push release-tests behind the tests and smoke tests, costing ~2.5 min per run. Splitting at the build boundary keeps it parallel. aggregator-ci-test and report_logs stay in the caller - each depends on a job that lives there (dep-guard and release-tests respectively). Image tags and BASE_REF/VARIATION arrive as inputs: neither job outputs nor caller workflow-level env cross a workflow_call boundary. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Replaces the build/tests pair with one callable workflow, plus the two product workflows moved over from tyk-analytics as nested helpers: release-tyk-analytics.yml <- entry point, 11 jobs release-tyk-analytics-release-tests.yml <- nested release-tyk-analytics-swagger.yml <- nested The two-file split existed only because release-tests had to stay in the caller (it called a repo-local workflow) and needed to depend on the build alone. With it moved here, the caller makes a single call and there is no dependency point to preserve, so the DAG lives entirely inside one file. Rebuilt from tyk-analytics' master release.yml rather than stitching the two files together, so it stays a verbatim port. Same three edits as before: triggers become workflow_call, the single-leg matrix becomes the golang_cross input plus constant env, and the dead publish-leg gate is dropped (8 conditions removed, 7 reduced). Churn check that justified the move: release-tests.yml and swagger-contract-tests.yml have had 2 commits each in 18 months. The test content itself - ci/smoke-tests, ci/tests/schema/specs, the Taskfiles - stays in tyk-analytics and is still reached by checkout. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds three entry points and the ecr-login action: release-tyk.yml 10 jobs, verbatim port of tyk's release.yml release-tyk-release-tests.yml 4 jobs, moved from tyk release-portal.yml 5 jobs, verbatim port .github/actions/ecr-login moved from tyk, actions now SHA-pinned Same three edits as tyk-analytics in each: triggers become workflow_call, the single-leg matrix becomes the golang_cross input plus constant env, and the dead publish-leg gate is dropped (tyk: 12 removed / 6 reduced; portal: 9 removed / 6 reduced). ecr-login moves because tyk's release-tests.yml uses it four times and a `./` path resolves inside whichever repo owns the workflow. It is generic - several repos already inline the same two steps. Note portal builds on 1.26-bookworm, not bullseye, so its gate literal differed - further evidence that carrying that literal as an input would be a footgun rather than a feature. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A `./` path means different things for workflows and for actions:
uses: ./.github/workflows/x.yml -> resolves against the repo that OWNS
the workflow file (github-actions)
uses: ./.github/actions/x -> resolves against the checked-out
WORKSPACE, i.e. the caller's repo
So the nested workflow calls work, but the ecr-login action reference broke:
Can't find 'action.yml' under '/home/runner/work/tyk/tyk/.github/actions/ecr-login'
Fixed by referencing it as TykTechnologies/github-actions/.github/actions/
ecr-login@<ref>, which resolves regardless of what is checked out.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
One workflow for the four repos whose pipelines differ only by data plus two
flags: tyk-pump, tyk-sink, portal and tyk-identity-broker. Derived from
tyk-pump, which is the superset (it has both the fips build and api tests).
build_fips false for tyk-identity-broker, true for the rest
run_api_tests true for tyk-pump and tyk-sink only
+ 9 data inputs (repo name, golang_cross, cgo, platforms_std, the two
Docker Hub repos, Cloudsmith repo, OCI title and description)
release-portal.yml is removed - portal is a consumer of this instead.
Two bugs caught while generalising, neither possible in a verbatim port:
- Platforms differ between the fips and std image within a repo. FIPS is
always amd64+arm64, never s390x; std varies. Collapsing them into one
input would have started building s390x FIPS images.
- The first substitution only matched the CI push form
(${{ ... || '...' }}) and missed the tag-push form (a plain
`platforms: linux/...`), so tyk-sink, portal and tyk-identity-broker
would have silently inherited tyk-pump's platform list and lost s390x
from their released images - with a green pipeline.
This file is the only one of the four that can be wrong while still passing
CI, so it needs artifact comparison against all four repos, not just a green
run.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
zizmor findings
Full details are in the workflow run. |
|
This Proof of Concept (POC) refactors the release pipelines for multiple Tyk products into centralized, reusable GitHub workflows. The primary goal is to eliminate widespread duplication of release logic across numerous repositories, simplifying maintenance by making this Files Changed AnalysisThis PR adds 7 new files, totaling 3,238 lines of code, with no deletions. The changes establish a new, centralized framework for release pipelines.
Architecture & Impact Assessment
graph TD
subgraph "Product Repositories (Callers)"
A[tyk] --> W_TYK
B[tyk-analytics] --> W_TA
C[tyk-pump, tyk-sink, etc.] --> W_COMMON
end
subgraph "github-actions Repo (Reusable Workflows)"
W_TYK(release-tyk.yml)
W_TA(release-tyk-analytics.yml)
W_COMMON(release-common.yml)
end
W_TYK --> T_TYK(release-tyk-release-tests.yml)
W_TA --> T_TA(release-tyk-analytics-release-tests.yml)
W_TYK & W_TA & W_COMMON --> A_ECR(ecr-login action)
Scope Discovery & Context ExpansionThis PR represents a fundamental modernization of the CI/CD and release engineering strategy. The impact extends to developer workflows, release processes, and internal tooling. To fully understand the context, the next steps would be to investigate:
Metadata
Powered by Visor from Probelabs Last updated: 2026-09-09T09:48:28.490Z | Triggered by: pr_updated | Commit: b7a8854 💡 TIP: You can chat with Visor using |
Security Issues (12)
Architecture Issues (2)
Performance Issues (2)
Quality Issues (6)
Powered by Visor from Probelabs Last updated: 2026-09-09T09:48:07.758Z | Triggered by: pr_updated | Commit: b7a8854 💡 TIP: You can chat with Visor using |
The fips gate injection assumed any existing `if:` sat immediately after `- name:`. On "Docker metadata for fips tag push" it sat after `id:`, so the step ended up with two `if:` keys and GitHub refused to load the workflow - the run shows up named after the file path with no jobs. Caught by actionlint: key "if" is duplicated in element of "steps" section Worth noting: `task lint` in this repo runs yamllint only, which parses that file happily. actionlint is what catches Actions-schema errors, and it would have caught this before three PRs were opened. Also confirmed benign: actionlint flags needs.goreleaser.outputs.ee_tags as undeclared, but that is pre-existing - the generated release.yml files reference it 2x (tyk-analytics) and 1x (tyk-pump) and declare it zero times. It evaluates to empty and the || fallback handles it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
package_name and upgrade_repo do different jobs in upgrade-tests.yml: upgrade_repo is the Packagecloud repo added as an apt/yum source, package_name is what gets installed from it. They match for tyk-pump, portal and tyk-identity-broker, so deriving both from repo_name looked fine - but tyk-sink publishes as tyk-mdcb-stable, because the product is MDCB publicly and tyk-sink internally. Worth noting how this would have failed: upgrade-tests.yml ends both the repo-setup and install lines with `|| echo "... but continuing"`, so a wrong upgrade_repo does not fail. It silently falls through to a fresh install and the test passes while never testing an upgrade at all. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Do not merge. POC for TT-18139.
Moves the gromit-generated release pipelines into reusable workflows here, so a
pipeline change stops fanning out to ~24 repo/branch combinations.
What this adds
release-tyk-analytics.yml+ 2 nestedrelease-tyk.yml+ 1 nestedrelease-common.yml.github/actions/ecr-loginFour entry points, one per pipeline shape. tyk and tyk-analytics need their own
because each has a repo-local
release-tests.yml; the other four differ only bydata plus two flags (
build_fips,run_api_tests).Caller side
release.ymlin each product repo becomes ~30 lines:Verbatim ports
Each entry point is a line-for-line copy of the generated
release.yml, withonly three edits:
on:triggers becomeworkflow_call, the single-leg matrixbecomes a
golang_crossinput plus constant env, and the dead publish-leg gateis dropped.
release-common.ymladditionally parameterises the data axes.Findings from the POC
subclaim resolves to the caller, so no AWStrust-policy changes.
varsandactions/checkoutresolve to the caller, so per-repo runnersand
ci/files keep working.granularity is preserved.
./means different things for workflows and actions../.github/workflows/x.ymlresolves against the repo owning the workflow;./.github/actions/xresolves against the checked-out workspace. That brokeecr-loginuntil it was referenced repo-qualified.so a re-run will not pick up a hotfix.
env:does not propagate, andenvis unavailablein a calling job's
with:.so a thin caller legitimately fails it.
with a component/edition matrix gromit cannot express. Its two config entries
(
ai-studioandmidsommar, the same repo via rename) render files nobody uses.Not done
gromit templates still generate the old full files; emitting thin callers is the
follow-up. Required status checks become
release / aggregator-ci-testand needrenaming per repo.
🤖 Generated with Claude Code