Skip to content
Snippets Groups Projects
Unverified Commit b63ea172 authored by Alexander Lesnenko's avatar Alexander Lesnenko Committed by GitHub
Browse files

replace backport label with a more flexible backport command (#17497)

* replace backport label with a more flexible backport command

* fix backport bot command
parent 3d5b0524
No related branches found
No related tags found
No related merge requests found
# Creates a pull request with the latest release branch as a target with a cherry-picked commit if an associated pull request has `backport` label
name: AutoBackport
on:
push:
branches:
- master
jobs:
pr_info:
name: Check if the commit should be backported
runs-on: ubuntu-latest
outputs:
title: ${{ fromJson(steps.collect_pr_info.outputs.result).title }}
number: ${{ fromJson(steps.collect_pr_info.outputs.result).pullRequestNumber }}
author: ${{ fromJson(steps.collect_pr_info.outputs.result).author }}
should_backport: ${{ fromJson(steps.collect_pr_info.outputs.result).hasBackportLabel }}
steps:
- uses: actions/github-script@v4
id: collect_pr_info
with:
script: |
const commitMessage = context.payload.commits[0].message;
const pullRequestNumbers = Array.from(commitMessage.matchAll(/\(#(.*?)\)/g))
if (pullRequestNumbers.length === 0) {
return;
}
if (pullRequestNumbers > 1) {
throw "Multiple PRs are associated with this commit";
}
const pullRequestNumber = pullRequestNumbers[0][1];
const { data } = await github.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pullRequestNumber
});
const hasBackportLabel = data.labels.some((label) => label.name === 'backport');
const { title, user } = data
console.log(`PR #${pullRequestNumber}: "${title}" hasBackportLabel=${hasBackportLabel}`)
return {
author: user.login,
pullRequestNumber,
title: data.title,
hasBackportLabel
}
get_latest_release_branch:
name: Get latest release branch
runs-on: ubuntu-latest
outputs:
branch_name: ${{ steps.get_branch_name.outputs.result }}
steps:
- uses: actions/github-script@v4
id: get_branch_name
with:
result-encoding: string
script: |
const releaseBranches = await github.git.listMatchingRefs({
owner: context.repo.owner,
repo: context.repo.repo,
ref: "heads/release-x.",
});
const getVersionFromBranch = branch => {
const match = branch.match(/release-x\.(.*?)\.x/);
return match && parseInt(match[1])
};
const latestReleaseBranch = releaseBranches.data
.filter(branch => getVersionFromBranch(branch.ref) !== null)
.reduce((prev, current) => getVersionFromBranch(prev.ref) > getVersionFromBranch(current.ref) ? prev : current);
const latestReleaseBranchName = latestReleaseBranch.ref.replace(/^refs\/heads\//, "");
console.log(`Latest release branch: ${latestReleaseBranchName}`)
return latestReleaseBranchName;
create_backport_pull_request:
runs-on: ubuntu-latest
name: Create a backport PR with the commit
needs: [pr_info, get_latest_release_branch]
if: ${{ needs.pr_info.outputs.should_backport == 'true' }}
env:
RELEASE_BRANCH: ${{ needs.get_latest_release_branch.outputs.branch_name }}
ORIGINAL_PR_TITLE: ${{ needs.pr_info.outputs.title }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v2
name: Checkout
with:
fetch-depth: 0
- run: |
git config --global user.email "metabase-github-automation@metabase.com"
git config --global user.name "$GITHUB_ACTOR"
PR_BRANCH="backport-$GITHUB_SHA"
git fetch --all
git checkout -b "${PR_BRANCH}" origin/"${RELEASE_BRANCH}"
git cherry-pick "${GITHUB_SHA}"
git push -u origin "${PR_BRANCH}"
hub pull-request -b "${RELEASE_BRANCH}" -h "${PR_BRANCH}" -l "auto-created-backport" -a "${GITHUB_ACTOR}" -m "backported \"${ORIGINAL_PR_TITLE}\""
notify_when_failed:
runs-on: ubuntu-latest
name: Notify about failure
needs: [pr_info, create_backport_pull_request]
if: ${{ failure() }}
steps:
- uses: actions/github-script@v4
with:
script: |
github.issues.createComment({
issue_number: ${{ needs.pr_info.outputs.number }},
owner: context.repo.owner,
repo: context.repo.repo,
body: '@${{ needs.pr_info.outputs.author }} could not automatically create a backport PR 😩'
})
# Cherry-picks commits from current branch to a specified one in a command "@metabase-bot backport release-x.40.x"
name: Backport
on:
issue_comment:
types: [created]
jobs:
create_pull_request:
name: Creates a pull request
if: contains(github.event.comment.body, '@metabase-bot backport')
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v4
id: branch_info
with:
script: |
// Example: @metabase-bot backport release-x.40.x
const [_botName, _command, targetBranch] = context.payload.comment.body.split(" ");
console.log(`Target branch is ${targetBranch}`);
const { data: originalPullRequest } = await github.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.issue.number,
});
const { data: commits } = await github.pulls.listCommits({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.issue.number,
});
const targetRef = await github.git.getRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `heads/${targetBranch}`,
});
const backportBranch = `backport-${originalPullRequest.head.ref}`
try {
await github.git.getRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `heads/${backportBranch}`,
});
} catch(e) {
if (e.status === 404) {
await github.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `refs/heads/${backportBranch}`,
sha: targetRef.data.object.sha,
});
}
}
return {
backportBranch,
targetBranch,
originalPullRequest,
startSha: commits[0].sha,
endSha: commits[commits.length - 1].sha
}
- uses: actions/checkout@v2
name: Cherry-pick commits and create PR
with:
fetch-depth: 0
- run: |
git config --global user.email "metabase-github-automation@metabase.com"
git config --global user.name "$GITHUB_ACTOR"
git fetch --all
git checkout "${BACKPORT_BRANCH}"
git reset --hard origin/${TARGET_BRANCH}
if [[ -z $(git ls-remote --heads origin ${ORIGINAL_HEAD_REF}) ]]; then
echo "PR has been merged, searching for a squashed commit in the base branch"
echo "searching for a commit in a ${ORIGINAL_BASE_REF} that contains pull request number ${ORIGINAL_PULL_REQUEST_NUMBER}"
SQUASHED_COMMIT=$(env -i git log ${ORIGINAL_BASE_REF} --grep="(#${ORIGINAL_PULL_REQUEST_NUMBER})" --format="%H")
echo "found commit ${SQUASHED_COMMIT}"
git cherry-pick ${SQUASHED_COMMIT}
else
echo "PR has not been merged, copying all commits"
git cherry-pick ${ORIGINAL_BASE_SHA}..${ORIGINAL_HEAD_SHA}
fi
git push origin "${BACKPORT_BRANCH}" --force-with-lease
if [[ $(hub pr list -b "${TARGET_BRANCH}" -h "${BACKPORT_BRANCH}" -s "open") ]]; then
echo "PR already exists"
else
hub pull-request -b "${TARGET_BRANCH}" -h "${BACKPORT_BRANCH}" -l "auto-backported" -a "${GITHUB_ACTOR}" -F- <<<"🤖 backported \"${ORIGINAL_TITLE}\"
#${ORIGINAL_PULL_REQUEST_NUMBER}"
echo "New PR has been created"
fi
env:
ORIGINAL_TITLE: ${{ fromJson(steps.branch_info.outputs.result).originalPullRequest.title }}
ORIGINAL_BASE_REF: ${{ fromJson(steps.branch_info.outputs.result).originalPullRequest.base.ref }}
ORIGINAL_BASE_SHA: ${{ fromJson(steps.branch_info.outputs.result).originalPullRequest.base.sha }}
ORIGINAL_HEAD_REF: ${{ fromJson(steps.branch_info.outputs.result).originalPullRequest.head.ref }}
ORIGINAL_HEAD_SHA: ${{ fromJson(steps.branch_info.outputs.result).originalPullRequest.head.sha }}
ORIGINAL_PULL_REQUEST_NUMBER: ${{ fromJson(steps.branch_info.outputs.result).originalPullRequest.number }}
TARGET_BRANCH: ${{ fromJson(steps.branch_info.outputs.result).targetBranch }}
BACKPORT_BRANCH: ${{ fromJson(steps.branch_info.outputs.result).backportBranch }}
START_SHA: ${{ fromJson(steps.branch_info.outputs.result).startSha }}
END_SHA: ${{ fromJson(steps.branch_info.outputs.result).endSha }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
notify_when_failed:
runs-on: ubuntu-latest
name: Notify about failure
needs: create_pull_request
if: ${{ failure() }}
steps:
- uses: actions/github-script@v4
with:
script: |
const { GITHUB_SERVER_URL, GITHUB_REPOSITORY, GITHUB_RUN_ID} = process.env;
const runUrl = `${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}`
const author = context.payload.comment.user.login;
github.issues.createComment({
issue_number: context.payload.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `@${author} could not automatically a backport PR 😩 [[Logs]](${runUrl})`
})
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment