From: Marvin Schenkel Date: Sun, 6 Jul 2025 06:04:17 +0000 (+0200) Subject: Add backport pipeline (#2268) X-Git-Url: https://git.kitaultman.com/?a=commitdiff_plain;h=54431f40b4f60d48a6772a07e97a78ec3fd086b8;p=music-assistant-server.git Add backport pipeline (#2268) * Vibe coded a backport pipeline * Remove unused triggers * Potential fix for code scanning alert no. 23: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- diff --git a/.github/workflows/backport-to-stable.yml b/.github/workflows/backport-to-stable.yml new file mode 100644 index 00000000..def896ed --- /dev/null +++ b/.github/workflows/backport-to-stable.yml @@ -0,0 +1,146 @@ +name: Backport to stable +permissions: + contents: read + pull-requests: write + +on: + push: + branches: + - dev + +jobs: + backport: + name: Backport PRs with 'backport-to-stable' label to stable + runs-on: ubuntu-latest + if: github.event.commits[0].distinct == true + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Needed for full git history + + - name: Get merged PR info + id: prinfo + uses: actions/github-script@v7 + with: + script: | + const pr = await github.rest.pulls.list({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'closed', + base: 'dev', + sort: 'updated', + direction: 'desc', + per_page: 10 + }); + const merged = pr.data.find(p => p.merge_commit_sha === context.payload.head_commit.id); + if (!merged) return core.setFailed('No merged PR found for this commit.'); + core.setOutput('pr_number', merged.number); + core.setOutput('pr_title', merged.title); + core.setOutput('pr_labels', merged.labels.map(l => l.name).join(',')); + core.setOutput('merge_commit_sha', merged.merge_commit_sha); + + - name: Check for backport-to-stable label + id: checklabel + run: | + echo "PR labels: ${{ steps.prinfo.outputs.pr_labels }}" + if [[ "${{ steps.prinfo.outputs.pr_labels }}" != *"backport-to-stable"* ]]; then + echo "No backport-to-stable label, skipping." + exit 0 + fi + + - name: Set up Git user + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Create or update backport branch + id: create_or_update_backport_branch + run: | + git fetch origin stable --tags + latest_tag=$(git tag --merged origin/stable --sort=-v:refname | head -1) + if [[ -z "$latest_tag" ]]; then + echo "No tags found on stable branch" >&2 + exit 1 + fi + version="$latest_tag" + IFS='.' read -r major minor patch <<< "$version" + next_patch=$((patch + 1)) + next_version="$major.$minor.$next_patch" + branch_name="backport/$next_version" + git fetch origin $branch_name || true + if git show-ref --verify --quiet refs/remotes/origin/$branch_name; then + git checkout -B $branch_name origin/$branch_name + else + git checkout -b $branch_name origin/stable + fi + echo "branch_name=$branch_name" >> $GITHUB_OUTPUT + + - name: Cherry-pick commit + run: | + git cherry-pick ${{ steps.prinfo.outputs.merge_commit_sha }} || { + echo 'Cherry-pick failed, please resolve conflicts manually.' + exit 1 + } + + - name: Push backport branch + run: | + git push origin ${{ steps.create_or_update_backport_branch.outputs.branch_name }}:${{ steps.create_or_update_backport_branch.outputs.branch_name }} --force + + - name: Create or update backport PR with cherry-picked commits + uses: actions/github-script@v7 + with: + script: | + const { pr_number } = process.env; + const next_patch_version = process.env.next_patch_version; + const branch = process.env.branch_name; + const cherry_commit = process.env.cherry_commit; + const prs = await github.rest.pulls.list({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + head: `${context.repo.owner}:${branch}`, + base: 'stable' + }); + const commit_url = `https://github.com/${context.repo.owner}/${context.repo.repo}/commit/${cherry_commit}`; + const commit_item = `- [${cherry_commit.substring(0,7)}](${commit_url})`; + if (prs.data.length === 0) { + // Create new PR with initial commit in body + await github.rest.pulls.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: `[Backport to stable] ${next_patch_version}`, + head: branch, + base: 'stable', + body: `Automated backport PR stable release ${next_patch_version} with cherry-picked commits:\n${commit_item}` + }); + } else { + // Update PR body to append new commit if not already present + const pr = prs.data[0]; + let body = pr.body || ''; + if (!body.includes(commit_item)) { + // Try to find the start of the list + const listMatch = body.match(/(cherry-picked commits:\n)([\s\S]*)/); + if (listMatch) { + // Append to existing list + const before = listMatch[1]; + const list = listMatch[2].trim(); + const newList = list + '\n' + commit_item; + body = body.replace(/(cherry-picked commits:\n)([\s\S]*)/, before + newList); + } else { + // Add new list + body = body.trim() + `\n\nCherry-picked commits:\n${commit_item}`; + } + await github.rest.pulls.update({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pr.number, + body + }); + } + } + env: + pr_number: ${{ steps.prinfo.outputs.pr_number }} + next_patch_version: ${{ steps.nextver.outputs.next_patch_version }} + branch_name: ${{ steps.create_or_update_backport_branch.outputs.branch_name }} + cherry_commit: ${{ steps.prinfo.outputs.merge_commit_sha }}