Skip to content

Commit 89132bf

Browse files
pxLiCopilot
andauthored
Update automerge workflow to support file_use_base and new branching pattern (#40)
fix #6 and #36 1. Migrate JNI auto-merge strategy here 2. Support new branching model release/X.Y to main 3. Detect tag to early skip auto-merge steps (This has been applied to both branch-* and release/*) 4. The old auto-merge action will be deprecated soon Cases: Plugin branch-25.06 to branch-25.08: https://github.com/pxLi/spark-rapids/actions/runs/15671686947/job/44143832505 <img width="494" alt="image" src="https://github.com/user-attachments/assets/f2b1da01-24f6-48b2-9c43-50b25a195efe" /> Plugin skipped auto-merge due to vX.Y.0 tag exists: https://github.com/pxLi/spark-rapids/actions/runs/15671714990/job/44144626269 <img width="439" alt="image" src="https://github.com/user-attachments/assets/0a23947e-fb0b-4866-a556-b3e3372847de" /> JNI branch-25.06 to branch-25.08: https://github.com/pxLi/spark-rapids-jni/actions/runs/15673010021/job/44147327479 <img width="516" alt="image" src="https://github.com/user-attachments/assets/6e73f6da-ce48-41d3-9d66-0a72ff103381" /> JNI release/25.08 to main: https://github.com/pxLi/spark-rapids-jni/actions/runs/15673971509/job/44150070792 <img width="425" alt="image" src="https://github.com/user-attachments/assets/2fb56e1d-3624-4a8b-98a4-b52f996eb672" /> --------- Signed-off-by: Peixin Li <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent 2a6bdb2 commit 89132bf

File tree

6 files changed

+434
-28
lines changed

6 files changed

+434
-28
lines changed

.github/workflows/auto-merge.yml

Lines changed: 100 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2024, NVIDIA CORPORATION.
1+
# Copyright (c) 2024-2025, NVIDIA CORPORATION.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -28,6 +28,11 @@ on:
2828
branch: # github.event.pull_request.base.ref
2929
required: true
3030
type: string
31+
file_use_base:
32+
description: 'file/path that require to use content of BASE'
33+
default: ''
34+
required: false
35+
type: string
3136
secrets:
3237
token:
3338
required: true
@@ -38,47 +43,114 @@ jobs:
3843
runs-on: ubuntu-latest
3944

4045
steps:
41-
# TODO: make this step as a shared action
46+
- name: set HEAD ENV
47+
run: |
48+
echo "HEAD=${{ inputs.branch }}" >> $GITHUB_ENV
49+
echo "DELETE_HEAD=False" >> $GITHUB_ENV
50+
4251
- name: Generate target branch
43-
id: generate_version
4452
run: |
45-
current_branch="${{ inputs.branch }}"
46-
version=${current_branch#branch-}
47-
48-
IFS='.' read -r -a parts <<< "$version"
49-
year=${parts[0]}
50-
month=${parts[1]}
51-
month=$((10#$month + 2))
52-
if [ $month -gt 12 ]; then
53-
month=$((month - 12))
54-
year=$((year + 1))
53+
# https://github.com/NVIDIA/spark-rapids-common/issues/34
54+
if [[ "${{ inputs.branch }}" == release/* ]]; then
55+
echo "BASE=main" >> $GITHUB_ENV
56+
else # maintain compatibility support for branch-*
57+
current_branch="${{ inputs.branch }}"
58+
version=${current_branch#branch-}
59+
60+
IFS='.' read -r -a parts <<< "$version"
61+
year=${parts[0]}
62+
month=${parts[1]}
63+
month=$((10#$month + 2))
64+
if [ $month -gt 12 ]; then
65+
month=$((month - 12))
66+
year=$((year + 1))
67+
fi
68+
69+
next_release=$(printf "%02d.%02d" $year $month)
70+
echo "Next release is $next_release"
71+
echo "BASE=branch-$next_release" >> $GITHUB_ENV
5572
fi
5673
57-
next_release=$(printf "%02d.%02d" $year $month)
58-
echo "Next release is $next_release"
59-
echo "target_branch=branch-$next_release" >> $GITHUB_ENV
74+
- name: Check for existing v*.0 tag
75+
run: |
76+
branch="${{ inputs.branch }}"
77+
if [[ "$branch" == release/* ]]; then
78+
version="${branch#release/}"
79+
elif [[ "$branch" == branch-* ]]; then
80+
version="${branch#branch-}"
81+
else
82+
echo "Unsupported branch ${{ inputs.branch }}..."
83+
exit 1
84+
fi
85+
tag_name="v${version}.0"
86+
echo "Checking for tag $tag_name..."
87+
88+
CODE=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token ${{ secrets.token }}" \
89+
https://api.github.com/repos/${{ inputs.owner }}/${{ inputs.repo }}/git/ref/tags/${tag_name})
90+
if [ "$CODE" -eq 200 ]; then
91+
echo "Tag $tag_name exists. Skipping merge..."
92+
echo "continue_merge=false" >> $GITHUB_ENV
93+
else
94+
echo "Proceeding with merge..."
95+
echo "continue_merge=true" >> $GITHUB_ENV
96+
fi
6097
61-
# TODO: make this step as a shared action
6298
- name: Check if target branch exists
63-
id: check_branch
99+
if: env.continue_merge == 'true'
64100
run: |
65101
CODE=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
66-
https://api.github.com/repos/${{ inputs.owner }}/${{ inputs.repo }}/branches/${{ env.target_branch }})
102+
https://api.github.com/repos/${{ inputs.owner }}/${{ inputs.repo }}/branches/${{ env.BASE }})
67103
echo "Response code: $CODE..."
68104
69105
if [ $CODE -eq 200 ]; then
70-
echo "branch_exists=true" >> $GITHUB_ENV
106+
echo "continue_merge=true" >> $GITHUB_ENV
71107
else
72-
echo "branch_exists=false" >> $GITHUB_ENV
73-
echo "Failed to find ${BRANCH_NAME}. Skip auto-merge..."
108+
echo "continue_merge=false" >> $GITHUB_ENV
109+
echo "Failed to find $BASE. Skip auto-merge..."
74110
fi
75111
112+
- name: prepare code base if $FILE_USE_BASE has content
113+
uses: actions/checkout@v4
114+
if: ${{ inputs.file_use_base != '' && env.continue_merge == 'true' }}
115+
with:
116+
ref: ${{ env.HEAD }} # force to fetch from the latest upstream instead of PR ref
117+
token: ${{ secrets.token }}
118+
119+
- name: push intermediate branch for auto-merge
120+
if: ${{ inputs.file_use_base != '' && env.continue_merge == 'true' }}
121+
run: |
122+
git config user.name "spark-rapids automation"
123+
git config user.email "[email protected]"
124+
125+
git fetch origin "${HEAD}" "${BASE}"
126+
git checkout -b ${INTERMEDIATE_HEAD} origin/${HEAD}
127+
128+
# Sync the $BASE branch with the commits from the $HEAD branch,
129+
# excluding the paths defined as $FILE_USE_BASE.
130+
git checkout origin/${BASE} -- ${FILE_USE_BASE}
131+
132+
# If any $FILE_USE_BASE is updated in the HEAD branch,
133+
# always change it to the corresponding one from the BASE branch.
134+
[ ! -z "$(git status --porcelain=v1 --untracked=no)" ] && \
135+
git commit -s -am "Auto-merge use ${BASE} versions"
136+
git push origin ${INTERMEDIATE_HEAD} -f
137+
138+
# overwrite HEAD env
139+
echo "HEAD=$INTERMEDIATE_HEAD" >> $GITHUB_ENV
140+
echo "DELETE_HEAD=True" >> $GITHUB_ENV
141+
env:
142+
INTERMEDIATE_HEAD: bot-auto-merge-${{ env.HEAD }}
143+
FILE_USE_BASE: ${{ inputs.file_use_base }}
144+
76145
- name: auto-merge job
77-
if: env.branch_exists == 'true'
78-
uses: NVIDIA/spark-rapids-common/auto-merge@main
146+
if: env.continue_merge == 'true'
147+
uses: NVIDIA/spark-rapids-common/action-helper@main
148+
with:
149+
operator: auto-merge
79150
env:
80151
OWNER: ${{ inputs.owner }}
81-
REPO_NAME: ${{ inputs.repo }}
82-
HEAD: ${{ inputs.branch }}
83-
BASE: ${{ env.target_branch }}
84-
AUTOMERGE_TOKEN: ${{ secrets.token }}
152+
REPO: ${{ inputs.repo }}
153+
HEAD: ${{ env.HEAD }}
154+
BASE: ${{ env.BASE }}
155+
TOKEN: ${{ secrets.token }}
156+
DELETE_HEAD: ${{ env.DELETE_HEAD }}

action-helper/Dockerfile

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright (c) 2025, NVIDIA CORPORATION.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
FROM python:alpine
16+
17+
WORKDIR /
18+
COPY python /python
19+
COPY entrypoint.sh .
20+
RUN chmod -R +x /python /entrypoint.sh
21+
RUN pip install requests
22+
23+
ENTRYPOINT ["/entrypoint.sh"]

action-helper/action.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Copyright (c) 2025, NVIDIA CORPORATION.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
name: 'action helper'
16+
description: 'helper for github-related operations'
17+
inputs:
18+
operator:
19+
required: true
20+
description: 'specify operator, e.g. auto-merge'
21+
runs:
22+
using: 'docker'
23+
image: 'Dockerfile'
24+
args:
25+
- ${{ inputs.operator }}

action-helper/entrypoint.sh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/bin/sh -l
2+
#
3+
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
18+
set -e
19+
20+
if [ $# -ne 1 ]; then
21+
echo "ERROR: invalid number of parameters, should be exact one"
22+
exit 1
23+
fi
24+
25+
case $1 in
26+
27+
auto-merge)
28+
/python/auto-merge --delete_head="${DELETE_HEAD}"
29+
;;
30+
31+
*)
32+
echo "ERROR: unknown parameter: $1"
33+
;;
34+
esac

action-helper/python/auto-merge

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright (c) 2025, NVIDIA CORPORATION.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
import sys
18+
from argparse import ArgumentParser
19+
20+
from utils import EnvDefault, PullRequest, strtobool
21+
22+
23+
def main():
24+
parser = ArgumentParser(description="Automerge")
25+
parser.add_argument("--owner", action=EnvDefault, env="OWNER",
26+
help="github token, will try use env OWNER if empty")
27+
parser.add_argument("--repo", action=EnvDefault, env="REPO",
28+
help="repo name, will try use env REPO if empty")
29+
parser.add_argument("--head", action=EnvDefault, env="HEAD",
30+
help="HEAD ref, will try use env HEAD if empty")
31+
parser.add_argument("--base", action=EnvDefault, env="BASE",
32+
help="Base ref, will try use env BASE if empty")
33+
parser.add_argument("--token", action=EnvDefault, env="TOKEN",
34+
help="github token, will try use env TOKEN if empty")
35+
parser.add_argument("--delete_head", default=False, type=lambda x: bool(strtobool(x)),
36+
help="if delete HEAD branch after auto-merge")
37+
args = parser.parse_args()
38+
39+
pr = PullRequest(head_owner=args.owner, head=args.head, head_token=args.token,
40+
base_owner=args.owner, repo=args.repo, base=args.base, base_token=args.token)
41+
try:
42+
if exist := pr.get_open():
43+
number = exist[0].get('number')
44+
sha = exist[0].get('head').get('sha')
45+
else:
46+
params = {
47+
# head share the same owner/repo with base in auto-merge
48+
'title': f"[auto-merge] {pr.head} to {pr.base} [skip ci] [bot]",
49+
'head': f"{pr.head_owner}:{pr.head}",
50+
'base': pr.base,
51+
'body': f"auto-merge triggered by github actions on `{pr.head}` to "
52+
f"create a PR keeping `{pr.base}` up-to-date. "
53+
"If this PR is unable to be merged due to conflicts, "
54+
"it will remain open until manually fix.",
55+
'maintainer_can_modify': True
56+
}
57+
number, sha, term = pr.create(params)
58+
if term:
59+
sys.exit(0)
60+
pr.auto_merge(number, sha)
61+
if args.delete_head:
62+
pr.delete_head()
63+
except Exception as e:
64+
print(e)
65+
sys.exit(1)
66+
67+
68+
if __name__ == '__main__':
69+
main()

0 commit comments

Comments
 (0)