mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-09 14:02:56 +09:00
Finishes the runner decommission tracked in #2937. PR builds, the main cache warmer, nightly, promote-rc and pull-requests-release already run on the ephemeral pool; this moves the stragglers so no workflow depends on the persistent runner anymore: - tags.yaml prepare-release: large ephemeral shape (same as the cache warmer), flux CLI bootstrap, and read-only OCIR access so release builds warm-start from the shared mode=max build cache while still pushing images to GHCR. Without the cache wiring every tag build on an ephemeral runner would be 100% cold. - tags.yaml generate-changelog / update-website-docs, backport.yaml, update-releasenotes.yaml: GitHub-hosted runners — these jobs only need git and the GitHub API, no docker or repo toolchain. - pull-requests.yaml: the debug label no longer reroutes jobs to the self-hosted runner (that path would queue forever once the runner is gone). The label keeps gating the SSH breakpoint on e2e failure, which works from ephemeral runners too. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
147 lines
6.1 KiB
YAML
147 lines
6.1 KiB
YAML
name: Automatic Backport
|
||
|
||
on:
|
||
pull_request_target:
|
||
types: [closed, labeled] # fires when PR is closed (merged) or labeled
|
||
|
||
concurrency:
|
||
group: backport-${{ github.workflow }}-${{ github.event.pull_request.number }}
|
||
cancel-in-progress: true
|
||
|
||
# Top-level token defaults to read-only; jobs request the minimum extra scopes.
|
||
permissions:
|
||
contents: read
|
||
|
||
jobs:
|
||
# Determine which backports are needed
|
||
prepare:
|
||
if: |
|
||
github.event.pull_request.merged == true &&
|
||
github.event.pull_request.base.ref == 'main' &&
|
||
(
|
||
contains(github.event.pull_request.labels.*.name, 'backport') ||
|
||
contains(github.event.pull_request.labels.*.name, 'backport-previous') ||
|
||
(github.event.action == 'labeled' && (github.event.label.name == 'backport' || github.event.label.name == 'backport-previous'))
|
||
)
|
||
# GitHub API calls only — no docker, no repo toolchain: GitHub-hosted.
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
contents: read
|
||
outputs:
|
||
backport_current: ${{ steps.labels.outputs.backport }}
|
||
backport_previous: ${{ steps.labels.outputs.backport_previous }}
|
||
current_branch: ${{ steps.branches.outputs.current_branch }}
|
||
previous_branch: ${{ steps.branches.outputs.previous_branch }}
|
||
steps:
|
||
- name: Check which labels are present
|
||
id: labels
|
||
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
|
||
with:
|
||
script: |
|
||
const pr = context.payload.pull_request;
|
||
const labels = pr.labels.map(l => l.name);
|
||
const isBackport = labels.includes('backport');
|
||
const isBackportPrevious = labels.includes('backport-previous');
|
||
|
||
core.setOutput('backport', isBackport ? 'true' : 'false');
|
||
core.setOutput('backport_previous', isBackportPrevious ? 'true' : 'false');
|
||
|
||
console.log(`backport label: ${isBackport}, backport-previous label: ${isBackportPrevious}`);
|
||
|
||
- name: Determine target branches
|
||
id: branches
|
||
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
|
||
with:
|
||
script: |
|
||
// Get latest release
|
||
let latestRelease;
|
||
try {
|
||
latestRelease = await github.rest.repos.getLatestRelease({
|
||
owner: context.repo.owner,
|
||
repo: context.repo.repo
|
||
});
|
||
} catch (e) {
|
||
core.setFailed('No existing releases found; cannot determine backport target.');
|
||
return;
|
||
}
|
||
|
||
const [maj, min] = latestRelease.data.tag_name.replace(/^v/, '').split('.');
|
||
const currentBranch = `release-${maj}.${min}`;
|
||
const prevMin = parseInt(min) - 1;
|
||
const previousBranch = prevMin >= 0 ? `release-${maj}.${prevMin}` : '';
|
||
|
||
core.setOutput('current_branch', currentBranch);
|
||
core.setOutput('previous_branch', previousBranch);
|
||
|
||
console.log(`Current branch: ${currentBranch}, Previous branch: ${previousBranch || 'N/A'}`);
|
||
|
||
// Verify previous branch exists if we need it
|
||
if (previousBranch && '${{ steps.labels.outputs.backport_previous }}' === 'true') {
|
||
try {
|
||
await github.rest.repos.getBranch({
|
||
owner: context.repo.owner,
|
||
repo: context.repo.repo,
|
||
branch: previousBranch
|
||
});
|
||
console.log(`Previous branch ${previousBranch} exists`);
|
||
} catch (e) {
|
||
core.setFailed(`Previous branch ${previousBranch} does not exist.`);
|
||
return;
|
||
}
|
||
}
|
||
|
||
backport:
|
||
needs: prepare
|
||
if: |
|
||
github.event.pull_request.merged == true &&
|
||
github.event.pull_request.base.ref == 'main' &&
|
||
(needs.prepare.outputs.backport_current == 'true' || needs.prepare.outputs.backport_previous == 'true')
|
||
# git cherry-picks via backport-action — no docker, no repo toolchain: GitHub-hosted.
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
contents: write
|
||
pull-requests: write
|
||
strategy:
|
||
matrix:
|
||
backport_type: [current, previous]
|
||
steps:
|
||
# 1. Determine target branch based on matrix
|
||
- name: Set target branch
|
||
id: target
|
||
if: |
|
||
(matrix.backport_type == 'current' && needs.prepare.outputs.backport_current == 'true') ||
|
||
(matrix.backport_type == 'previous' && needs.prepare.outputs.backport_previous == 'true')
|
||
run: |
|
||
if [ "${{ matrix.backport_type }}" == "current" ]; then
|
||
echo "branch=${{ needs.prepare.outputs.current_branch }}" >> $GITHUB_OUTPUT
|
||
echo "Target branch: ${{ needs.prepare.outputs.current_branch }}"
|
||
else
|
||
echo "branch=${{ needs.prepare.outputs.previous_branch }}" >> $GITHUB_OUTPUT
|
||
echo "Target branch: ${{ needs.prepare.outputs.previous_branch }}"
|
||
fi
|
||
|
||
# 2. Checkout (required by backport‑action)
|
||
- name: Checkout repository
|
||
if: steps.target.outcome == 'success'
|
||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||
|
||
# 3. Create the back‑port pull request
|
||
- name: Create back‑port PR
|
||
id: backport
|
||
if: steps.target.outcome == 'success'
|
||
uses: korthout/backport-action@0193454f0c5947491d348f33a275c119f30eb736 # v3.2.1
|
||
with:
|
||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||
label_pattern: '' # don't read labels for targets
|
||
target_branches: ${{ steps.target.outputs.branch }}
|
||
merge_commits: skip
|
||
# conflict_resolution lives inside the experimental JSON input, not as a
|
||
# top-level key. Passed top-level it is silently ignored and the action
|
||
# falls back to the experimental default ("fail"), which on a conflict
|
||
# creates no PR and only comments. Nesting it here makes a conflicting
|
||
# backport open a draft PR with the conflict committed for manual fixup.
|
||
experimental: |
|
||
{
|
||
"conflict_resolution": "draft_commit_conflicts"
|
||
}
|