Well, it's perfectly possible to put one file, a plain text file, called "releases" in it's own source code repository, and it contains nothing but:
repoA:1234567890abcdef:v1.2.3
repoA:34567890abcdef12:v1.2.2
It points out some recorded information about what changeset/commit in a repository was called something, for marketing reasons. Every change to that repository will emit an artifact with a changeset an version number for other pipelines to pick up and use as input.
Another option is to create all the pipelines dynamically:
A third option is to simply have a first stage that verifies that whatever change appears in the source code repository is in fact a tag. If it's not, the job "fails" and stops any following stages.
A fourth option is to have two repositories:
* a Main repository where humans push source code and tags to. This repository is monitored by a Pipeline that only does one thing: it looks at all incoming changesets, and if one of those contains a release tag, it will push that changeset (and all upstream changesets in the graph) from the Main repository to the Release repository.
* a Release repository is where pushes are made automatically, triggered by the above job when a new tag is found in the Main repository. Your release pipelines/jobs watch this Release repository and is only run/triggered when a push (with a new tag) has been made to this Release repository.
I'm sure there are more patterns and solutions.
/ Fredrik