Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion .github/actions/generate-workflows-for-branch/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ outputs:
changed:
description: "'true' if workflow files were changed and pushed (or would be, on a dry run), 'false' otherwise"
value: ${{ steps.commit.outputs.changed }}
files:
description: >
JSON array of the files that were changed and pushed (or would be, on a dry run),
e.g. [{"status":"M","path":".github/workflows/ci.yml"}]. Empty array when nothing changed.
value: ${{ steps.commit.outputs.files }}

runs:
using: composite
Expand Down Expand Up @@ -159,7 +164,15 @@ runs:
if git diff --staged --quiet; then
echo "No changes to commit for ${{ inputs.repo }}@${{ inputs.branch }}."
echo "changed=false" >> "$GITHUB_OUTPUT"
elif [[ "$DRY_RUN" == "true" ]]; then
echo "files=[]" >> "$GITHUB_OUTPUT"
exit 0
fi

files=$(git diff --staged --name-status | jq -R -s -c \
'split("\n") | map(select(length > 0) | split("\t") | {status: .[0][0:1], path: .[-1]})')
echo "files=${files}" >> "$GITHUB_OUTPUT"

if [[ "$DRY_RUN" == "true" ]]; then
git diff --staged --stat
echo "Dry run: would push changes to ${{ inputs.repo }}@${{ inputs.branch }}."
echo "changed=true" >> "$GITHUB_OUTPUT"
Expand Down
17 changes: 3 additions & 14 deletions .github/workflows/run-github-actions-workflow-generator.README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Runs the [github-actions-workflow-generator](https://github.com/spring-io/github

## What It Does

For every repository and branch tracked in [`config/projects.json`](../config/projects.json):
For every repository and `.x` branch tracked in [`config/projects.json`](../config/projects.json):

1. Copies `.github/actions/release-train-build/action.yml` and `.github/actions/release-train-test/action.yml` into the target repo from this repo (see [Release Train Actions](#release-train-actions)).
2. Copies `release-train-settings.xml` to the root of the target repo.
Expand All @@ -15,6 +15,8 @@ The primary Java version passed to the generator is determined per-branch from `
- `8` if JDK 8 is listed in the branch's `jdkVersions`
- `17` otherwise

`release/*` and `*-internal` branches are never processed, even when listed in `projects.json`. They are rebased onto the `.x` branches and pick up the regenerated workflow files that way.

## Triggering the Workflow

The workflow is triggered manually via **Actions → Run GitHub Actions Workflow Generator → Run workflow**.
Expand All @@ -25,7 +27,6 @@ The workflow is triggered manually via **Actions → Run GitHub Actions Workflow
|---|---|---|---|
| `generator-version` | No | Latest release | Version of `github-actions-workflow-generator` to use (e.g. `0.0.5`). |
| `sha` | No | Triggering commit | Commit SHA of this repo to copy release-train action files from. Useful for rolling out a specific version of the build/test logic. |
| `spring-release` | No | _(empty)_ | Spring release train version (e.g. `2026.1`). When set, also processes `release/[version]` branches found in the `spring-io/release-train` README for that version. See [Spring Release Branches](#spring-release-branches). |
| `projects` | No | _(empty — all projects)_ | Comma-separated list of Spring Cloud project names to limit the run to (e.g. `spring-cloud-build,spring-cloud-config`). When empty, every project in `projects.json` is processed. |
| `dry_run` | No | `true` | When true, reports which repos and branches would be updated and pushes nothing. Uncheck to actually commit and push. |
| `token` | No | `GH_ACTIONS_REPO_TOKEN` | GitHub token with write access to all target repos. Falls back to the `GH_ACTIONS_REPO_TOKEN` organisation secret. |
Expand Down Expand Up @@ -70,18 +71,6 @@ Useful when you want to roll out a specific version of the build/test action log
sha: 348109524f9790dc1e20d48043fb1ef4765373b8
```

### Include active release-train branches

```
spring-release: 2026.1
```

## Spring Release Branches

When `spring-release` is set, the workflow fetches `README.adoc` from the `spring-io/release-train` repository at the specified ref (e.g. `2026.1`) and scans it for Spring Cloud entries. For each entry it finds a `release/[version]` branch in the corresponding commercial repository and runs the same generator steps against it.

The primary JDK for a release branch is determined by deriving the parent branch from the version number (e.g. `release/3.1.15` → parent `3.1.x`) and looking up that branch's `jdkVersions` in `projects.json`.

## Release Train Actions

Each target repo receives a copy of the release-train action files directly from this repo. The source file for each action is chosen using a three-level lookup (first match wins):
Expand Down
76 changes: 18 additions & 58 deletions .github/workflows/run-github-actions-workflow-generator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ on:
required: false
type: string
default: ''
spring-release:
description: 'Spring release train version (e.g. 2026.1). When set, also processes release/[version] branches found in the spring-io/release-train README.adoc for that version.'
required: false
type: string
default: ''
projects:
description: 'Comma-separated list of Spring Cloud project names to run against (e.g. spring-cloud-build,spring-cloud-config). When empty, all projects in projects.json are processed.'
required: false
Expand Down Expand Up @@ -74,7 +69,6 @@ jobs:
id: build-matrix
env:
GH_TOKEN: ${{ inputs.token || secrets.GH_ACTIONS_REPO_TOKEN }}
SPRING_RELEASE: ${{ inputs.spring-release }}
PROJECTS_FILTER: ${{ inputs.projects }}
run: |
node - << 'JSEOF'
Expand Down Expand Up @@ -112,11 +106,11 @@ jobs:
const entries = [];
const seen = new Set();

function addEntry(repo, branch, typeKey, projectKey, jdkLookupBranch) {
function addEntry(repo, branch, typeKey, projectKey) {
const key = `${repo}@${branch}`;
if (seen.has(key)) return;
seen.add(key);
const jdks = getJdks(projectKey, typeKey, jdkLookupBranch || branch);
const jdks = getJdks(projectKey, typeKey, branch);
entries.push({ repo, branch, primary_jdk: primaryJdk(jdks) });
}

Expand All @@ -131,58 +125,14 @@ jobs:
? `spring-cloud/${projectKey}-commercial`
: `spring-cloud/${projectKey}`;
for (const branch of (typeCfg.branches || {}).scheduled || []) {
// release/* and *-internal branches are rebased onto the .x branches and
// pick up the regenerated workflows that way, so never generate them directly.
if (branch.startsWith('release/') || branch.endsWith('-internal')) continue;
addEntry(repo, branch, typeKey, projectKey);
}
}
}

// Optionally add release/[version] branches from the Spring release train README
const springRelease = (process.env.SPRING_RELEASE || '').trim();
if (springRelease) {
let readmeB64;
try {
readmeB64 = execFileSync('gh', [
'api', 'repos/spring-io/release-train/contents/README.adoc',
'-X', 'GET', '-f', `ref=${springRelease}`, '--jq', '.content'
], { encoding: 'utf8' }).trim();
} catch (err) {
console.error(`Warning: could not fetch README.adoc for spring release ${springRelease}: ${err.message}`);
}
if (readmeB64) {
const content = Buffer.from(readmeB64, 'base64').toString('utf8');
let currentRepo = null;
let currentProjectKey = null;
for (const line of content.split('\n')) {
if (/^== /.test(line)) {
if (!line.includes('Spring Cloud')) {
currentRepo = null;
currentProjectKey = null;
}
continue;
}
const releasingMatch = line.match(
/\*\*Releasing from:\*\* https:\/\/github\.com\/spring-cloud\/([^\[]+)\[/
);
if (releasingMatch) {
const repoName = releasingMatch[1].trim().replace(/\/$/, '');
currentRepo = `spring-cloud/${repoName}`;
currentProjectKey = repoName.replace(/-commercial$/, '');
continue;
}
if (currentRepo) {
const versionMatch = line.match(/=== .+ (\d+\.\d+(?:\.\d+(?:\.\d+)?)?)\s*$/);
if (versionMatch) {
const version = versionMatch[1];
const parts = version.split('.');
const parentBranch = parts.slice(0, -1).join('.') + '.x';
if (projectsFilter.size > 0 && !projectsFilter.has(currentProjectKey)) continue;
addEntry(currentRepo, `release/${version}`, 'commercial', currentProjectKey, parentBranch);
}
}
}
}
}

const matrix = { include: entries };
fs.appendFileSync(process.env.GITHUB_OUTPUT, `matrix=${JSON.stringify(matrix)}\n`);
console.log(`Matrix built: ${entries.length} entries`);
Expand Down Expand Up @@ -220,11 +170,17 @@ jobs:
REPO: ${{ matrix.repo }}
BRANCH: ${{ matrix.branch }}
CHANGED: ${{ steps.generate.outputs.changed }}
FILES: ${{ steps.generate.outputs.files }}
run: |
safe="${REPO//\//-}-${BRANCH//\//-}"
safe="${safe//./-}"
echo "safe-name=${safe}" >> "$GITHUB_OUTPUT"
echo '{"repo":"'"$REPO"'","branch":"'"$BRANCH"'","changed":'"${CHANGED:-false}"'}' \
jq -n -c \
--arg repo "$REPO" \
--arg branch "$BRANCH" \
--argjson changed "${CHANGED:-false}" \
--argjson files "${FILES:-[]}" \
'{repo: $repo, branch: $branch, changed: $changed, files: $files}' \
> "result-${safe}.json"

- name: Upload result
Expand Down Expand Up @@ -274,9 +230,13 @@ jobs:

if (updated.length > 0) {
md += `### ${dryRun ? 'Would update' : 'Updated'} (${updated.length})\n\n`;
md += '| Repository | Branch |\n|---|---|\n';
const statusLabels = { A: 'added', M: 'modified', D: 'deleted', R: 'renamed', C: 'copied', T: 'type changed' };
md += '| Repository | Branch | Files |\n|---|---|---|\n';
for (const r of updated) {
md += `| \`${r.repo}\` | \`${r.branch}\` |\n`;
const fileList = (r.files || [])
.map(f => `\`${f.path}\` (${statusLabels[f.status] || f.status})`)
.join('<br>') || '_unknown_';
md += `| \`${r.repo}\` | \`${r.branch}\` | ${fileList} |\n`;
}
md += '\n';
} else {
Expand Down
Loading