Starts the snapshot deploy of dependent repositories right after a snapshot deploy, so their snapshots and
Docker images contain the new artifacts. When mycore deploys a new snapshot of 2025.12.x, mir is rebuilt
on 2025.12.x minutes later, instead of picking the change up with its next own push.
The upstream project calls it after its deploy, in the same file as maven-deploy.yml:
name: Maven Snapshot Deploy
on:
push:
branches:
- main
- '[0-9][0-9][0-9][0-9].[0-9][0-9].x'
workflow_dispatch:
permissions:
contents: read
jobs:
deploy:
concurrency:
group: snapshot-deploy-${{ github.repository }}-${{ github.ref_name }}
cancel-in-progress: true
uses: MyCoRe-Org/github-workflows/.github/workflows/maven-deploy.yml@v1
with:
java-version: '25'
secrets:
MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
downstream:
needs: deploy
if: github.event_name == 'push'
uses: MyCoRe-Org/github-workflows/.github/workflows/dispatch-downstream.yml@v1
with:
targets: |
MyCoRe-Org/mir maven-snapshot.yml
secrets:
DISPATCH_TOKEN: ${{ secrets.DISPATCH_TOKEN }}The dependents need no change for this beyond what maven-deploy.yml asks for anyway,
see Requirements for targets.
| Input | Required | Default | Description |
|---|---|---|---|
targets |
yes | Targets, one per line | |
branch |
no | branch of the calling run | Branch for targets without an explicit branch |
| Secret | Description |
|---|---|
DISPATCH_TOKEN |
Token with actions: write on the targets |
One target per line as <owner/repository> <workflow> [<branch or pattern>]:
# <owner/repository> <workflow> [<branch or pattern>]
MyCoRe-Org/mir maven-snapshot.yml
MyCoRe-Org/<repository> maven-snapshot.yml main
<workflow>is the file name of the deploy workflow in.github/workflowsof the target.- Without
<branch>, the target is built on the branch that was pushed upstream, so2025.12.xfollows2025.12.x. Set a branch only for a dependent that always builds the same branch, whichever upstream branch was pushed. - Instead of a branch, a shell pattern like
[0-9][0-9][0-9][0-9].[0-9][0-9].xstands for all branches matching it that contain the workflow file, the other matching branches are skipped. Git does not allow*,?and[in branch names, so a pattern cannot be confused with a branch. Never use*alone: feature branches created frommaincontain the workflow as well, and their deploy would overwrite the snapshot ofmain. - Empty lines and lines starting with
#are ignored, Windows line endings do no harm.
flowchart LR
push["push to mycore 2025.12.x"] --> deploy["mycore: maven-deploy"]
deploy --> dispatch["mycore: dispatch-downstream"]
dispatch -->|"workflow_dispatch on 2025.12.x"| mir["mir: maven-snapshot.yml"]
dispatch -.->|"no branch 2025.12.x, skipped"| plugin["plugin without 2025.12.x"]
The job runs the action workflow-dispatch with DISPATCH_TOKEN.
For every target it
- checks that the token can read the repository, otherwise it reports an error and continues with the next target,
- checks that the branch exists in the target. A missing branch is a notice, not an error, as not every dependent has every version branch. A pattern is resolved to the matching branches that contain the workflow file,
- starts the workflow on that branch with
workflow_dispatch, like the "Run workflow" button would, and logs the link to the started run.
All targets are tried, the job fails at the end if any of them reported an error. The job summary lists the
started runs with their links. They appear in the Actions tab of the targets with the event workflow_dispatch
and the account of the token as actor.
This workflow does not wait for the started runs. The snapshot refresh does,
it uses the same action with wait: 'true'. The action waits at most wait-minutes, by default 300 minutes,
runs not finished by then count as failed. That ends the job before the limit of 360 minutes of GitHub,
which would cancel it without a failure to report.
- The deploy workflow has the trigger
workflow_dispatch. - The workflow file exists on the default branch of the target, GitHub only accepts dispatches for those, and on the dispatched branch, as GitHub runs the file as it is on that branch, with that branch's Java version.
- A
concurrencygroup per branch withcancel-in-progress, as in the example. A series of upstream pushes then leads to one build of the target instead of a queue. DISPATCH_TOKENcovers the repository.
Only pushes dispatch, see if: github.event_name == 'push' in the example. Runs started by a dispatch, by hand or by
the weekly snapshot refresh do not dispatch further. Without that condition
- the snapshot refresh, which dispatches every target directly, would also start the dependents of every target, so they would build twice,
- dispatches would chain through the whole dependency tree, and a dependency cycle would never stop.
A project that depends on the upstream only through a dependent, e.g. a plugin of mir, is listed in the
targets of the upstream as well, if it should be rebuilt on upstream pushes.
DISPATCH_TOKEN is an organization secret holding a fine-grained personal access token, e.g. of a bot account,
with resource owner MyCoRe-Org and the repository permission "Actions: Read and write" on all targets.
It starts the runs and reads their results. Patterns also read the branches and the workflow file, which needs
"Contents: Read" for private repositories, public ones are readable anyway.
GITHUB_TOKEN does not work, it is only valid for the repository of the run.
- Fine-grained tokens expire. Renew the token in time, an expired token makes every target fail with "cannot access".
- The organization must allow fine-grained tokens, and may require approving them.
- A GitHub App would need a step creating its installation token in the job. The workflows take the token as a secret and have no such step.
| Message | Meaning |
|---|---|
dispatched <workflow> in <repository>@<branch> <link> |
The run was started |
notice <repository> has no branch <branch>, skipped |
Expected for dependents without that version branch |
<repository>@<branch> has no <workflow>, skipped |
The branch matches the pattern but has no deploy workflow, e.g. an old version branch |
notice no branch of <repository> matching <pattern> has <workflow> |
Nothing to deploy for that line: a typo in the pattern, or the project does not have the workflow yet |
error the token is empty, check that DISPATCH_TOKEN is shared with <repository> and passed on by the caller |
The organization secret is not shared with the calling repository, or the caller does not pass it |
error cannot access <repository>: … |
Wrong repository name, or the token cannot read it: expired, or the repository is not selected for the token |
error cannot check <repository>@<branch>: … |
The branch check failed for another reason than a missing branch, e.g. a GitHub outage |
error cannot list the branches of <repository>: …, cannot check <workflow> in <repository>@<branch>: … |
Resolving a pattern failed, e.g. because of a GitHub outage |
error dispatch of <workflow> in <repository>@<branch> failed: … |
Wrong file name, no workflow_dispatch trigger, the file is missing on the default or on that branch, or the token lacks "Actions: Read and write" |
When waiting for the runs, as the snapshot refresh does:
| Message | Meaning |
|---|---|
<workflow> in <repository>@<branch> succeeded |
The deploy succeeded |
error <workflow> in <repository>@<branch> ended with <conclusion>, <link> |
The deploy failed, the link leads to its log |
warning <workflow> in <repository>@<branch> was cancelled, <link> |
Another run of the same concurrency group replaced it, e.g. after a push at the same time, that run deploys instead |
warning no run id returned for <workflow> in <repository>@<branch>, not waiting for it |
GitHub did not return the run, its result is not checked |
cannot check <link> yet: … |
Reading the status failed, it is tried again in the next round |
error <workflow> in <repository>@<branch> did not finish within <minutes> minutes, <link> |
The run is still queued or running, or was deleted. It is not waited for any longer |