Skip to content

Update release-bot.yaml #83

Update release-bot.yaml

Update release-bot.yaml #83

Workflow file for this run

name: Cherry-pick to Release Branch (with GitHub App auth)
on:
issue_comment:
types: [created]
workflow_call:
inputs:
app-id:
description: "GitHub App ID (optional). If set with private-key, the workflow will auth as the App."
required: false
type: string
default: ""
private-key:
description: "GitHub App private key (PEM, multiline)."
required: false
type: string
default: ""
installation-id:
description: "GitHub App installation ID (optional). If omitted, installation for the repo owner is used."
required: false
type: string
default: ""
permissions:
contents: write
pull-requests: write
issues: write
jobs:
cherry_pick:
runs-on: ubuntu-latest
steps:
# 1) Only run when someone comments `/release to <branch>` on a PR
- name: Check for release command
id: check_command
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { issue, comment } = context.payload;
if (!issue || !issue.pull_request || !comment) {
core.setOutput('release_valid','false');
return;
}
const m = (comment.body || '').trim().match(/^\/release to\s+([^\s]+)\s*$/i);
if (!m) {
core.setOutput('release_valid','false');
return;
}
core.setOutput('release_valid','true');
core.setOutput('release_branch', m[1]);
core.setOutput('pr_number', issue.number);
- name: Skip if not a valid command
if: steps.check_command.outputs.release_valid == 'false'
run: echo "Skipping: no '/release to <branch>' command found."

Check failure on line 57 in .github/workflows/release-bot.yaml

View workflow run for this annotation

GitHub Actions / .github/workflows/release-bot.yaml

Invalid workflow file

You have an error in your yaml syntax on line 57
# 2) Checkout repo with full history; do NOT persist the default token
- name: Checkout repository
if: steps.check_command.outputs.release_valid == 'true'
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
# 3) Optional: Authenticate as a GitHub App (token with the app identity)
- name: Create GitHub App token (by installation id)
id: app_token_id
if: ${{ github.event_name == 'workflow_call' && inputs.app-id != '' && inputs.private-key != '' && inputs.installation-id != '' }}
uses: actions/create-github-app-token@v1
with:
app-id: ${{ inputs['app-id'] }}
private-key: ${{ inputs['private-key'] }}
installation-id: ${{ inputs['installation-id'] }}
- name: Create GitHub App token (by owner)
id: app_token_owner
if: ${{ github.event_name == 'workflow_call' && inputs.app-id != '' && inputs.private-key != '' && inputs.installation-id == '' }}
uses: actions/create-github-app-token@v1
with:
app-id: ${{ inputs['app-id'] }}
private-key: ${{ inputs['private-key'] }}
owner: ${{ github.repository_owner }}
- name: Select auth token & prepare git remote
if: steps.check_command.outputs.release_valid == 'true'
run: |
set -euo pipefail
TOKEN="${{ steps.app_token_id.outputs.token }}"
if [ -z "$TOKEN" ]; then
TOKEN="${{ steps.app_token_owner.outputs.token }}"
fi
if [ -z "$TOKEN" ]; then
TOKEN="${{ secrets.GITHUB_TOKEN }}"
fi
# Export for following steps (gh CLI prefers GITHUB_TOKEN/GH_TOKEN)
echo "GITHUB_TOKEN=$TOKEN" >> "$GITHUB_ENV"
echo "GH_TOKEN=$TOKEN" >> "$GITHUB_ENV"
# Ensure pushes/pulls use our chosen token (avoid any extraheader from checkout)
git config --local --unset-all http.https://github.com/.extraheader || true
git remote set-url origin "https://x-access-token:${TOKEN}@github.com/${{ github.repository }}.git"
git remote -v
shell: bash
- name: Configure git user
if: steps.check_command.outputs.release_valid == 'true'
run: |
git config --global user.email "bot@tyk.io"
git config --global user.name "Tyk Bot"
# 4) Locate the *merge* commit that landed on the base branch when the PR was merged
- name: Find merge commit on base branch
id: pr_details
if: steps.check_command.outputs.release_valid == 'true'
env:
GH_TOKEN: ${{ env.GH_TOKEN }}
run: |
set -euo pipefail
PR_NUMBER='${{ steps.check_command.outputs.pr_number }}'
OWNER_REPO='${{ github.repository }}'
PR_JSON="$(gh api "repos/${OWNER_REPO}/pulls/${PR_NUMBER}")"
PR_TITLE="$(jq -r '.title' <<<"$PR_JSON")"
BASE_REF="$(jq -r '.base.ref' <<<"$PR_JSON")"
MERGED="$(jq -r '.merged' <<<"$PR_JSON")"
MERGE_SHA="$(jq -r '.merge_commit_sha // empty' <<<"$PR_JSON")"
if [ "${MERGED}" != "true" ]; then
echo "PR #${PR_NUMBER} is not merged; cannot use merge commit." >&2
exit 1
fi
# Ensure base branch is present locally
git fetch origin "${BASE_REF}" --quiet
# Fallbacks for different merge strategies/messages
if [ -z "${MERGE_SHA}" ] || [ "${MERGE_SHA}" = "null" ]; then
MERGE_SHA="$(git log "origin/${BASE_REF}" --merges --grep="Merge pull request #${PR_NUMBER}" -n 1 --pretty=format:%H || true)"
fi
if [ -z "${MERGE_SHA}" ]; then
# Squash merge default message ends with "(#<PR_NUMBER>)"
MERGE_SHA="$(git log "origin/${BASE_REF}" --grep="(#${PR_NUMBER})" -n 1 --pretty=format:%H || true)"
fi
if [ -z "${MERGE_SHA}" ]; then
echo "Could not find merge commit for PR #${PR_NUMBER} on ${BASE_REF}. Was it 'rebase and merge'?" >&2
exit 1
fi
{
echo "COMMIT_SHA=${MERGE_SHA}"
echo "PR_TITLE=${PR_TITLE}"
echo "BASE_REF=${BASE_REF}"
} >> "$GITHUB_OUTPUT"
# 5) Cherry-pick that merge commit onto the requested release branch and open a PR
- name: Cherry-pick merge commit to target release
id: cherry_pick
if: steps.check_command.outputs.release_valid == 'true'
env:
GH_TOKEN: ${{ env.GH_TOKEN }}
GITHUB_TOKEN: ${{ env.GITHUB_TOKEN }}
GITHUB_REPO: ${{ github.repository }}
GITHUB_BRANCH: ${{ steps.check_command.outputs.release_branch }}
GITHUB_CHERRY_PICK_COMMIT: ${{ steps.pr_details.outputs.COMMIT_SHA }}
PR_TITLE: ${{ steps.pr_details.outputs.PR_TITLE }}
BASE_REF: ${{ steps.pr_details.outputs.BASE_REF }}
run: |
set -euo pipefail
# Ensure both base and target branches are present/up-to-date
git fetch origin "${BASE_REF}" --quiet
git fetch origin "${GITHUB_BRANCH}" --quiet
git checkout -B "${GITHUB_BRANCH}" "origin/${GITHUB_BRANCH}"
# Create a consistent branch name (optionally include JIRA key)
JIRA_ID="$(echo "${PR_TITLE}" | grep -oE '[A-Z]{1,10}-[0-9]{1,10}' | head -n 1 || true)"
BRANCH_NAME="merge/${GITHUB_BRANCH}/${GITHUB_CHERRY_PICK_COMMIT}"
if [ -n "${JIRA_ID}" ]; then
BRANCH_NAME="${BRANCH_NAME}/${JIRA_ID}"
fi
# Remove any stale branches locally/remotely
git branch -D "${BRANCH_NAME}" 2>/dev/null || true
git push origin --delete "${BRANCH_NAME}" 2>/dev/null || true
# Create working branch from the release branch
git switch -c "${BRANCH_NAME}"
# Determine if selected commit is a merge (has >1 parent)
PARENTS_LINE="$(git rev-list --parents -n 1 "${GITHUB_CHERRY_PICK_COMMIT}")"
WORDS_COUNT="$(wc -w <<<"${PARENTS_LINE}")"
MERGE_FAILED=0
if [ "${WORDS_COUNT}" -gt 2 ]; then
# Merge commit -> use -m 1
git cherry-pick -x -m 1 "${GITHUB_CHERRY_PICK_COMMIT}" || MERGE_FAILED=$?
else
# Normal / squash commit
git cherry-pick -x "${GITHUB_CHERRY_PICK_COMMIT}" || MERGE_FAILED=$?
fi
# If conflicts happened, attempt to continue so we still open a (draft) PR
if [ "${MERGE_FAILED}" -ne 0 ]; then
git add -A || true
git -c core.editor=true cherry-pick --continue --no-edit || true
fi
git push origin "${BRANCH_NAME}" --force
# Prepare PR metadata from the cherry-picked commit
MESSAGE="$(git show -s --format=%B "${GITHUB_CHERRY_PICK_COMMIT}")"
TITLE="$(echo "${MESSAGE}" | head -n 1)"
# Reuse existing PR for this head if present; else create one (draft on conflicts)
PR_URL="$(gh pr view --repo "${GITHUB_REPO}" --head "${BRANCH_NAME}" --json url -q .url 2>/dev/null || true)"
if [ -z "${PR_URL}" ]; then
if [ "${MERGE_FAILED}" -ne 0 ]; then
PR_URL="$(gh pr create --draft \
--repo "${GITHUB_REPO}" \
--base "${GITHUB_BRANCH}" \
--head "${BRANCH_NAME}" \
--title "Merging to ${GITHUB_BRANCH}: ${TITLE}" \
--body "${MESSAGE}")"
else
PR_URL="$(gh pr create \
--repo "${GITHUB_REPO}" \
--base "${GITHUB_BRANCH}" \
--head "${BRANCH_NAME}" \
--title "Merging to ${GITHUB_BRANCH}: ${TITLE}" \
--body "${MESSAGE}")"
fi
fi
{
echo "PR_URL=${PR_URL}"
echo "MERGE_FAILED=${MERGE_FAILED}"
echo "BRANCH_NAME=${BRANCH_NAME}"
} >> "$GITHUB_OUTPUT"
# 6) Comment back on the original PR with the result (use App token if we have it)
- name: Comment back on original PR
if: always() && steps.check_command.outputs.release_valid == 'true'
uses: actions/github-script@v6
with:
github-token: ${{ env.GITHUB_TOKEN }}
script: |
const prUrl = '${{ steps.cherry_pick.outputs.PR_URL }}';
const mergeFailed = '${{ steps.cherry_pick.outputs.MERGE_FAILED }}' === '1';
let body;
if ('${{ job.status }}' === 'success') {
body = mergeFailed
? `⚠️ Cherry-pick completed with conflicts. A draft PR was created: ${prUrl}`
: `✅ Cherry-pick successful. A PR was created: ${prUrl}`;
} else {
body = '❌ Cherry-pick failed. Please check the workflow logs.';
}
github.rest.issues.createComment({
issue_number: ${{ steps.check_command.outputs.pr_number }},
owner: context.repo.owner,
repo: context.repo.repo,
body
});