1+ name : Style Bot Action
2+
3+ on :
4+ workflow_call :
5+ inputs :
6+ pre_commit_script :
7+ required : false
8+ type : string
9+ description : " Optional script to run before committing changes"
10+ pre_commit_script_name :
11+ required : false
12+ type : string
13+ description : " Custom name for the pre-commit script step"
14+ default : " Custom pre-commit script"
15+ python_quality_dependencies :
16+ required : true
17+ type : string
18+ description : " Python package extras to install for quality checks (e.g. '[quality]')"
19+ python_version :
20+ required : false
21+ type : string
22+ description : " Python version to run code formatter"
23+ default : " 3.10"
24+ style_command :
25+ required : false
26+ type : string
27+ description : " Command to run for style checks or/and style fixes"
28+ default : " make style && make quality"
29+ secrets :
30+ bot_token :
31+ required : true
32+ description : " GitHub token with permissions to comment and push to PR"
33+
34+ jobs :
35+ check-permissions :
36+ if : >
37+ contains(github.event.comment.body, '@bot /style') &&
38+ github.event.issue.pull_request != null
39+ runs-on : ubuntu-latest
40+ outputs :
41+ is_authorized : ${{ steps.check_user_permission.outputs.has_permission }}
42+ steps :
43+ - name : Check user permission
44+ id : check_user_permission
45+ uses : actions/github-script@v6
46+ with :
47+ script : |
48+ const comment_user = context.payload.comment.user.login;
49+ const { data: permission } = await github.rest.repos.getCollaboratorPermissionLevel({
50+ owner: context.repo.owner,
51+ repo: context.repo.repo,
52+ username: comment_user
53+ });
54+ const authorized = permission.permission === 'admin';
55+ console.log(`User ${comment_user} has permission level: ${permission.permission}, authorized: ${authorized} (only admins allowed)`);
56+ core.setOutput('has_permission', authorized);
57+
58+ run-style-bot :
59+ needs : check-permissions
60+ if : needs.check-permissions.outputs.is_authorized == 'true'
61+ runs-on : ubuntu-latest
62+ steps :
63+ - name : Extract PR details
64+ id : pr_info
65+ uses : actions/github-script@v6
66+ with :
67+ script : |
68+ const prNumber = context.payload.issue.number;
69+ const { data: pr } = await github.rest.pulls.get({
70+ owner: context.repo.owner,
71+ repo: context.repo.repo,
72+ pull_number: prNumber
73+ });
74+
75+ // We capture both the branch ref and the "full_name" of the head repo
76+ // so that we can check out the correct repository & branch (including forks).
77+ core.setOutput("prNumber", prNumber);
78+ core.setOutput("headRef", pr.head.ref);
79+ core.setOutput("headRepoFullName", pr.head.repo.full_name);
80+
81+ - name : Check out PR branch
82+ uses : actions/checkout@v3
83+ env :
84+ HEADREPOFULLNAME : ${{ steps.pr_info.outputs.headRepoFullName }}
85+ HEADREF : ${{ steps.pr_info.outputs.headRef }}
86+ with :
87+ # Instead of checking out the base repo, use the contributor's repo name
88+ repository : ${{ env.HEADREPOFULLNAME }}
89+ ref : ${{ env.HEADREF }}
90+ # You may need fetch-depth: 0 for being able to push
91+ fetch-depth : 0
92+ token : ${{ secrets.bot_token }}
93+
94+ - name : Debug
95+ env :
96+ HEADREPOFULLNAME : ${{ steps.pr_info.outputs.headRepoFullName }}
97+ HEADREF : ${{ steps.pr_info.outputs.headRef }}
98+ PRNUMBER : ${{ steps.pr_info.outputs.prNumber }}
99+ run : |
100+ echo "PR number: $PRNUMBER"
101+ echo "Head Ref: $HEADREF"
102+ echo "Head Repo Full Name: $HEADREPOFULLNAME"
103+
104+ - name : Set up Python
105+ uses : actions/setup-python@v4
106+ with :
107+ python-version : ${{ inputs.python_version }}
108+
109+ - name : Install dependencies
110+ env :
111+ python_quality_dependencies : ${{ inputs.python_quality_dependencies }}
112+ run : |
113+ python -m pip install --upgrade pip
114+ pip install .$python_quality_dependencies
115+
116+ - name : ${{ inputs.pre_commit_script_name }}
117+ env :
118+ pre_commit_script : ${{ inputs.pre_commit_script }}
119+ if : inputs.pre_commit_script != ''
120+ shell : bash
121+ run : |
122+ echo "$pre_commit_script" > pre_commit_script.sh
123+ chmod +x pre_commit_script.sh
124+ ./pre_commit_script.sh
125+
126+ - name : Run make style and make quality
127+ env :
128+ style_command : ${{ inputs.style_command }}
129+ run : |
130+ $style_command
131+
132+ - name : Commit and push changes
133+ id : commit_and_push
134+ env :
135+ HEADREPOFULLNAME : ${{ steps.pr_info.outputs.headRepoFullName }}
136+ HEADREF : ${{ steps.pr_info.outputs.headRef }}
137+ PRNUMBER : ${{ steps.pr_info.outputs.prNumber }}
138+ GITHUB_TOKEN : ${{ secrets.bot_token }}
139+ run : |
140+ echo "HEADREPOFULLNAME: $HEADREPOFULLNAME, HEADREF: $HEADREF"
141+ # Configure git with the Actions bot user
142+ git config user.name "github-actions[bot]"
143+ git config user.email "github-actions[bot]@users.noreply.github.com"
144+
145+ # Make sure your 'origin' remote is set to the contributor's fork
146+ git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@github.com/$HEADREPOFULLNAME.git"
147+
148+ # If there are changes after running style/quality, commit them
149+ if [ -n "$(git status --porcelain)" ]; then
150+ git add .
151+ git commit -m "Apply style fixes"
152+ # Push to the original contributor's forked branch
153+ git push origin HEAD:$HEADREF
154+ echo "changes_pushed=true" >> $GITHUB_OUTPUT
155+ else
156+ echo "No changes to commit."
157+ echo "changes_pushed=false" >> $GITHUB_OUTPUT
158+ fi
159+
160+ - name : Comment on PR with workflow run link
161+ if : steps.commit_and_push.outputs.changes_pushed == 'true'
162+ uses : actions/github-script@v6
163+ with :
164+ script : |
165+ const prNumber = parseInt(process.env.prNumber, 10);
166+ const runUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`
167+
168+ await github.rest.issues.createComment({
169+ owner: context.repo.owner,
170+ repo: context.repo.repo,
171+ issue_number: prNumber,
172+ body: `Style fixes have been applied. [View the workflow run here](${runUrl}).`
173+ });
174+ env :
175+ prNumber : ${{ steps.pr_info.outputs.prNumber }}
0 commit comments