Skip to content

Commit 4377d51

Browse files
committed
Merge remote-tracking branch 'upstream/main' into cherry-pick
2 parents 59254cd + 8fece39 commit 4377d51

File tree

85 files changed

+3624
-1426
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+3624
-1426
lines changed

.github/workflows/add-remove-labels.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ on:
44
types: [synchronize]
55

66
pull_request_review:
7+
types: [submitted, edited]
8+
9+
pull_request_review_comment:
10+
types: [created, edited]
711

812
issue_comment:
913
types: [created, edited, deleted]
@@ -39,12 +43,13 @@ jobs:
3943

4044
- name: Run add remove labels
4145
env:
42-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
46+
GITHUB_TOKEN: ${{ secrets.OPENDATAHUB_TESTS_BOT_PAT }}
4347
GITHUB_PR_NUMBER: "${{ github.event.pull_request.number || github.event.issue.number }}"
4448
GITHUB_EVENT_ACTION: ${{ github.event.action }}
4549
GITHUB_EVENT_REVIEW_STATE: ${{ github.event.review.state }}
4650
GITHUB_EVENT_NAME: ${{ github.event_name }}
4751
COMMENT_BODY: ${{ github.event.comment.body }}
52+
REVIEW_COMMENT_BODY: ${{ github.event.review.body }}
4853
GITHUB_USER_LOGIN: ${{ github.event.sender.login }}
4954
ACTION: "add-remove-labels"
5055
run: uv run python .github/workflows/scripts/pr_workflow.py
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
name: Build and Push Container Image On PR Merge
2+
3+
on:
4+
pull_request_target:
5+
types: [closed]
6+
7+
permissions:
8+
pull-requests: write
9+
contents: write
10+
issues: write
11+
12+
jobs:
13+
if_merged:
14+
if: github.event.pull_request.merged == true
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Checkout Repository
18+
uses: actions/checkout@v4
19+
- name: Set env TAG
20+
run: |
21+
if [ ${{ github.event.pull_request.base.ref }} == "main" ]; then
22+
echo "TAG=latest" >> $GITHUB_ENV
23+
else
24+
echo "TAG=${{ github.event.pull_request.base.ref }}" >> "$GITHUB_ENV"
25+
fi
26+
- name: Build Image
27+
id: build-image
28+
uses: redhat-actions/buildah-build@v2
29+
with:
30+
image: opendatahub-tests
31+
tags: ${{ env.TAG }}
32+
containerfiles: |
33+
./Dockerfile
34+
35+
- name: Push To Image Registry
36+
id: push-to-registry
37+
uses: redhat-actions/push-to-registry@v2
38+
with:
39+
image: ${{ steps.build-image.outputs.image }}
40+
tags: ${{ steps.build-image.outputs.tags }}
41+
registry: quay.io/opendatahub
42+
username: ${{ secrets.QUAY_USERNAME }}
43+
password: ${{ secrets.QUAY_PASSWORD }}
44+
45+
- name: Add comment to PR
46+
if: always()
47+
env:
48+
URL: ${{ github.event.pull_request.comments_url }}
49+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
50+
run: |
51+
curl \
52+
-X POST \
53+
$URL \
54+
-H "Content-Type: application/json" \
55+
-H "Authorization: token $GITHUB_TOKEN" \
56+
--data '{ "body": "Status of building tag ${{ env.TAG }}: ${{ steps.build-image.outcome }}. \nStatus of pushing tag ${{ env.TAG }} to image registry: ${{ steps.push-to-registry.outcome }}." }'
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
name: Run Label Action on PR Review Event
2+
on:
3+
workflow_run:
4+
workflows: ["Dummy Workflow on review"]
5+
types:
6+
- completed
7+
permissions:
8+
pull-requests: write
9+
contents: write
10+
issues: write
11+
12+
jobs:
13+
run_on_workflow_a_success:
14+
if: ${{ github.event.workflow_run.conclusion == 'success' }}
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Run this on Dummy workflow success
18+
run: echo "Dummy Workflow on review completes successfully"
19+
download_context_artifact:
20+
runs-on: ubuntu-latest
21+
steps:
22+
- name: 'Download artifact'
23+
uses: actions/github-script@v7
24+
with:
25+
script: |
26+
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
27+
owner: context.repo.owner,
28+
repo: context.repo.repo,
29+
run_id: context.payload.workflow_run.id,
30+
});
31+
32+
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
33+
return artifact.name == "context.json"
34+
})[0];
35+
36+
let download = await github.rest.actions.downloadArtifact({
37+
owner: context.repo.owner,
38+
repo: context.repo.repo,
39+
artifact_id: matchArtifact.id,
40+
archive_format: 'zip',
41+
});
42+
43+
let fs = require('fs');
44+
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/context.zip`, Buffer.from(download.data));
45+
46+
- name: 'Unzip artifact'
47+
run: unzip context.zip
48+
- name: 'Return Parsed JSON'
49+
uses: actions/github-script@v7
50+
id: return-parsed-json
51+
with:
52+
script: |
53+
let fs = require('fs');
54+
let data = fs.readFileSync('./context.json');
55+
return JSON.parse(data);
56+
outputs:
57+
pr_num: ${{fromJSON(steps.return-parsed-json.outputs.result).pr_num}}
58+
event_action: ${{fromJSON(steps.return-parsed-json.outputs.result).event_action}}
59+
review_state: ${{fromJSON(steps.return-parsed-json.outputs.result).review_state}}
60+
event_name: ${{fromJSON(steps.return-parsed-json.outputs.result).event_name}}
61+
comment_body: ${{fromJSON(steps.return-parsed-json.outputs.result).comment_body}}
62+
review_comment_body: ${{fromJSON(steps.return-parsed-json.outputs.result).review_comment_body}}
63+
user_login: ${{fromJSON(steps.return-parsed-json.outputs.result).user_login}}
64+
action: ${{fromJSON(steps.return-parsed-json.outputs.result).action}}
65+
log_context_values:
66+
needs:
67+
- download_context_artifact
68+
runs-on: ubuntu-latest
69+
steps:
70+
- name: 'Set all Env Variable'
71+
run: |
72+
echo "GITHUB_PR_NUMBER=${{ needs.download_context_artifact.outputs.pr_num }}" >> "$GITHUB_ENV"
73+
echo "GITHUB_EVENT_ACTION=${{ needs.download_context_artifact.outputs.event_action }}" >> "$GITHUB_ENV"
74+
echo "GITHUB_EVENT_REVIEW_STATE=${{ needs.download_context_artifact.outputs.review_state }}" >> "$GITHUB_ENV"
75+
echo "GITHUB_EVENT_NAME=${{ needs.download_context_artifact.outputs.event_name }}" >> "$GITHUB_ENV"
76+
echo "COMMENT_BODY=${{ needs.download_context_artifact.outputs.comment_body }}" >> "$GITHUB_ENV"
77+
echo "REVIEW_COMMENT_BODY=${{ needs.download_context_artifact.outputs.review_comment_body }}" >> "$GITHUB_ENV"
78+
echo "GITHUB_USER_LOGIN=${{ needs.download_context_artifact.outputs.user_login }}" >> "$GITHUB_ENV"
79+
echo "ACTION=${{ needs.download_context_artifact.outputs.action }}" >> "$GITHUB_ENV"
80+
- uses: actions/checkout@v4
81+
82+
- name: Install uv
83+
uses: astral-sh/setup-uv@v5
84+
- name: 'Run add-remove-labels action'
85+
env:
86+
GITHUB_TOKEN: ${{ secrets.OPENDATAHUB_TESTS_BOT_PAT }}
87+
GITHUB_EVENT_NAME: ${{ needs.download_context_artifact.outputs.event_name }}
88+
run: uv run python .github/workflows/scripts/pr_workflow.py

.github/workflows/scripts/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
SUCCESS_STR: str = "success"
1212
FAILURE_STR: str = "failure"
1313
QUEUED_STR: str = "queued"
14+
APPROVED: str = "approved"
1415

1516
SUPPORTED_LABELS: set[str] = {
1617
f"{LABEL_PREFIX}{WIP_LABEL_STR}",

.github/workflows/scripts/pr_workflow.py

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
from github.PullRequest import PullRequest
88
from github.Repository import Repository
9+
from github.MainClass import Github
10+
from github.GithubException import UnknownObjectException
11+
from github.Organization import Organization
12+
from github.Team import Team
913

1014
from constants import (
1115
ALL_LABELS_DICT,
@@ -20,8 +24,8 @@
2024
SUPPORTED_LABELS,
2125
VERIFIED_LABEL_STR,
2226
WELCOME_COMMENT,
27+
APPROVED,
2328
)
24-
from github import Github, UnknownObjectException
2529
from simple_logger.logger import get_logger
2630

2731
LOGGER = get_logger(name="pr_labeler")
@@ -41,6 +45,7 @@ class SupportedActions:
4145
def __init__(self) -> None:
4246
self.repo: Repository
4347
self.pr: PullRequest
48+
self.gh_client: Github
4449

4550
self.repo_name = os.environ["GITHUB_REPOSITORY"]
4651
self.pr_number = int(os.getenv("GITHUB_PR_NUMBER", 0))
@@ -77,8 +82,8 @@ def verify_base_config(self) -> None:
7782
)
7883

7984
def set_gh_config(self) -> None:
80-
gh_client: Github = Github(login_or_token=self.github_token)
81-
self.repo = gh_client.get_repo(full_name_or_id=self.repo_name)
85+
self.gh_client = Github(login_or_token=self.github_token)
86+
self.repo = self.gh_client.get_repo(full_name_or_id=self.repo_name)
8287
self.pr = self.repo.get_pull(number=self.pr_number)
8388

8489

@@ -87,12 +92,29 @@ def __init__(self) -> None:
8792
super().__init__()
8893
self.user_login = os.getenv("GITHUB_USER_LOGIN")
8994
self.review_state = os.getenv("GITHUB_EVENT_REVIEW_STATE")
95+
# We don't care if the body of the comment is in the discussion page or a review
9096
self.comment_body = os.getenv("COMMENT_BODY", "")
97+
if self.comment_body == "":
98+
# if it wasn't a discussion page comment, try to get a review comment, otherwise keep empty
99+
self.comment_body = os.getenv("REVIEW_COMMENT_BODY", "")
91100
self.last_commit = list(self.pr.get_commits())[-1]
92101
self.last_commit_sha = self.last_commit.sha
93102

94103
self.verify_labeler_config()
95104

105+
def verify_allowed_user(self) -> None:
106+
org: Organization = self.gh_client.get_organization("opendatahub-io")
107+
# slug is the team name with replaced special characters,
108+
# all words to lowercase and spaces replace with a -
109+
team: Team = org.get_team_by_slug("opendatahub-tests-contributors")
110+
try:
111+
# check if the user is a member of opendatahub-tests-contributors
112+
membership = team.get_team_membership(self.user_login)
113+
LOGGER.info(f"User {self.user_login} is a member of the test contributor team. {membership}")
114+
except UnknownObjectException:
115+
LOGGER.error(f"User {self.user_login} is not allowed for this action. Exiting.")
116+
sys.exit(0)
117+
96118
def verify_labeler_config(self) -> None:
97119
if self.action == self.SupportedActions.add_remove_labels_action_name and self.event_name in (
98120
"issue_comment",
@@ -101,17 +123,18 @@ def verify_labeler_config(self) -> None:
101123
if not self.user_login:
102124
sys.exit("`GITHUB_USER_LOGIN` is not set")
103125

104-
if self.event_name == "issue_comment" and not self.comment_body:
105-
sys.exit("`COMMENT_BODY` is not set")
106-
107-
if self.event_name == "pull_request_review" and not self.review_state:
108-
sys.exit("`GITHUB_EVENT_REVIEW_STATE` is not set")
126+
if (
127+
self.event_name == "issue_comment" or self.event_name == "pull_request_review"
128+
) and not self.comment_body:
129+
LOGGER.info("No comment, nothing to do. Exiting.")
130+
sys.exit(0)
109131

110132
def run_pr_label_action(self) -> None:
111133
if self.action == self.SupportedActions.pr_size_action_name:
112134
self.set_pr_size()
113135

114136
if self.action == self.SupportedActions.add_remove_labels_action_name:
137+
self.verify_allowed_user()
115138
self.add_remove_pr_labels()
116139

117140
if self.action == self.SupportedActions.welcome_comment_action_name:
@@ -226,6 +249,10 @@ def add_remove_pr_labels(self) -> None:
226249

227250
return
228251

252+
# We will only reach here if the PR was created from a fork
253+
elif self.event_name == "workflow_run" and self.event_action == "submitted":
254+
self.pull_request_review_label_actions()
255+
229256
LOGGER.warning("`add_remove_pr_label` called without a supported event")
230257

231258
def pull_request_review_label_actions(
@@ -239,7 +266,7 @@ def pull_request_review_label_actions(
239266
label_to_remove = None
240267
label_to_add = None
241268

242-
if self.review_state == "approved":
269+
if self.review_state == APPROVED:
243270
label_to_remove = change_requested_label
244271
label_to_add = lgtm_label
245272

@@ -276,12 +303,20 @@ def issue_comment_label_actions(
276303
LOGGER.info(f"Processing labels: {labels}")
277304
for label, action in labels.items():
278305
if label == LGTM_LABEL_STR:
279-
label = f"{LGTM_BY_LABEL_PREFIX}{self.user_login}"
306+
if self.user_login == self.pr.user.login:
307+
LOGGER.info("PR submitter cannot approve for their own PR")
308+
continue
309+
else:
310+
label = f"{LGTM_BY_LABEL_PREFIX}{self.user_login}"
311+
if not action[CANCEL_ACTION] or self.event_action == "deleted":
312+
self.approve_pr()
280313

281314
label_in_pr = any([label == _label.lower() for _label in self.pr_labels])
282315
LOGGER.info(f"Processing label: {label}, action: {action}")
283316

284317
if action[CANCEL_ACTION] or self.event_action == "deleted":
318+
if label == LGTM_LABEL_STR:
319+
self.dismiss_pr_approval()
285320
if label_in_pr:
286321
LOGGER.info(f"Removing label {label}")
287322
self.pr.remove_from_labels(label=label)
@@ -296,7 +331,23 @@ def issue_comment_label_actions(
296331

297332
def add_welcome_comment_set_assignee(self) -> None:
298333
self.pr.create_issue_comment(body=WELCOME_COMMENT)
299-
self.pr.add_to_assignees(self.pr.user.login)
334+
try:
335+
self.pr.add_to_assignees(self.pr.user.login)
336+
except UnknownObjectException:
337+
LOGGER.warning(f"User {self.pr.user.login} can not be assigned to the PR.")
338+
339+
def approve_pr(self) -> None:
340+
self.pr.create_review(event="APPROVE")
341+
342+
def dismiss_pr_approval(self) -> None:
343+
all_reviews = self.pr.get_reviews()
344+
current_user = self.gh_client.get_user().login
345+
LOGGER.info(f"Looking for approving review by user {current_user}")
346+
# The reviews are paginated in chronological order. We need to get the newest by our account
347+
for review in all_reviews.reversed:
348+
if review.user.login == current_user and review.state == APPROVED:
349+
LOGGER.info(f"found review by user {current_user} with id {review.id}")
350+
review.dismiss(message="Dismissing review due to '/lgtm cancel' comment")
300351

301352

302353
def main() -> None:
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Dummy Workflow on review
2+
3+
on:
4+
pull_request_review:
5+
types: [submitted, edited]
6+
7+
permissions:
8+
pull-requests: write
9+
contents: write
10+
issues: write
11+
12+
jobs:
13+
14+
dummy-workflow:
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: save the pr information
19+
run: |
20+
printf '{
21+
"pr_num": "${{ github.event.pull_request.number || github.event.issue.number }}",
22+
"event_action": "${{ github.event.action }}",
23+
"review_state": "${{ github.event.review.state }}",
24+
"event_name": "${{ github.event_name }}",
25+
"comment_body": "${{ github.event.comment.body }}",
26+
"review_comment_body": "${{ github.event.review.body }}",
27+
"user_login": "${{ github.event.sender.login }}",
28+
"action": "add-remove-labels"
29+
}' >> context.json
30+
- uses: actions/upload-artifact@v4
31+
with:
32+
name: context.json
33+
path: ./

0 commit comments

Comments
 (0)