-
Notifications
You must be signed in to change notification settings - Fork 37
Expand file tree
/
Copy pathissue-comment
More file actions
executable file
·192 lines (158 loc) · 6.65 KB
/
Copy pathissue-comment
File metadata and controls
executable file
·192 lines (158 loc) · 6.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#!/usr/bin/env python3
# Copyright (C) 2026 Red Hat, Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
import argparse
import asyncio
import json
import logging
import sys
import time
from collections.abc import Sequence
from typing import Literal, assert_never, cast
from lib import ALLOWLIST
from lib.aio.base import SubjectSpecification
from lib.aio.github import GitHub
from lib.aio.job import Job, run_job
from lib.aio.jobcontext import JobContext
from lib.aio.jsonutil import JsonObject, get_int, get_nested, get_str
from lib.aio.testingfarm import TestingFarmClient
from lib.jobqueue import JobSpecification
logger = logging.getLogger(__name__)
async def process_comment(
*,
repo: str,
username: str,
issue_nr: int,
pull_nr: int | None,
comment_body: str,
run_to: Literal['local', 'testing-farm'] = 'testing-farm',
) -> int:
logger.debug(
"process_comment: repo=%r username=%r issue_nr=%r pull_nr=%r comment_body=%r run_to=%r",
repo, username, issue_nr, pull_nr, comment_body, run_to,
)
if not comment_body.startswith("/image-refresh "):
logger.debug("comment does not start with /image-refresh, ignoring")
return 1
image = comment_body.removeprefix("/image-refresh ").strip()
if not image:
logger.debug("empty image name, ignoring")
return 1
if username not in ALLOWLIST:
logger.debug("user %r not in allowlist, ignoring", username)
return 1
logger.debug("processing image-refresh for image=%r", image)
async with JobContext() as ctx:
spec = SubjectSpecification({
'repo': repo,
'pull': pull_nr,
})
logger.debug("resolving subject for spec=%r", spec)
subject = await ctx.resolve_subject(spec)
logger.debug("resolved subject sha=%r", subject.sha)
current = time.strftime('%Y%m%d-%H%M%S')
job: JobSpecification = {
'repo': repo,
'sha': subject.sha,
'pull': pull_nr,
'slug': f'image-refresh-{image}-{subject.sha[:8]}-{current}',
'context': f'image-refresh/{image}',
'command': ['./image-refresh', '--verbose', f'--issue={issue_nr}', image],
'secrets': ['github-token', 'image-upload'],
}
logger.debug("created job specification: %r", job)
match run_to:
case 'testing-farm':
logger.debug("submitting job to testing-farm")
async with TestingFarmClient() as tf:
request_id = await tf.submit_job(ctx, job)
logger.debug("submitted job, request_id=%r", request_id)
artifacts_url = await tf.wait_for_artifacts(request_id)
if artifacts_url is None:
artifacts_url = tf.get_request_url(request_id)
logger.debug("artifacts_url=%r", artifacts_url)
forge = subject.forge
assert isinstance(forge, GitHub)
logger.debug("posting comment to issue %r", issue_nr)
await forge.post(
f'repos/{repo}/issues/{issue_nr}/comments',
{
'body': f"""\
**Task scheduled:** issue-{issue_nr} {job['context']}
Testing Farm link: {artifacts_url}
<details>
<summary>Job JSON</summary>
```json
{json.dumps(job, indent=2)}
```
</details>"""
},
)
case 'local':
logger.debug("running job locally")
await run_job(Job(cast(JsonObject, job)), ctx)
case other:
assert_never(other)
logger.debug("process_comment completed successfully")
return 0
async def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Handle /image-refresh commands in comments")
parser.add_argument('--debug', action='store_true', help="Enable debug logging")
parser.add_argument(
'--to',
choices=['local', 'testing-farm'],
default='testing-farm',
help="Where to run the job (default: testing-farm)",
)
subparsers = parser.add_subparsers(dest='command', required=True)
# Event file mode
event_parser = subparsers.add_parser('event', help='Process GitHub event file')
event_parser.add_argument('event_data_file', help='Path to event JSON file (e.g., $GITHUB_EVENT_PATH)')
# Test mode (for local testing)
test_parser = subparsers.add_parser('test', help='Run with explicit arguments')
test_parser.add_argument('--repo', required=True, help="Repository")
test_parser.add_argument('--username', required=True, help="Username (must be in ALLOWLIST)")
test_parser.add_argument('--comment-id', type=int, help="Comment ID to update")
issue_group = test_parser.add_mutually_exclusive_group(required=True)
issue_group.add_argument('--issue', type=int, dest='issue_nr', help="Issue number")
issue_group.add_argument('--pull', type=int, dest='pull_nr', help="Pull request number")
test_parser.add_argument('body', help='Comment body (e.g., "/image-refresh foonux")')
args = parser.parse_args(argv)
logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO)
logger.debug("parsed args: %r", args)
match args.command:
case 'event':
logger.debug("reading event file %r", args.event_data_file)
with open(args.event_data_file) as f:
event: JsonObject = json.load(f)
logger.debug("loaded event: %r", event)
with get_nested(event, "repository") as repository:
repo = get_str(repository, "full_name")
with get_nested(event, "comment") as comment:
with get_nested(comment, "user") as user:
username = get_str(user, "login")
body = get_str(comment, "body", "")
with get_nested(event, "issue") as issue:
issue_nr = get_int(issue, "number")
pull_nr = issue_nr if "pull_request" in issue else None
return await process_comment(
repo=repo,
username=username,
issue_nr=issue_nr,
pull_nr=pull_nr,
comment_body=body,
run_to=args.to,
)
case 'test':
return await process_comment(
repo=args.repo,
username=args.username,
issue_nr=args.issue_nr or args.pull_nr,
pull_nr=args.pull_nr,
comment_body=args.body,
run_to=args.to,
)
case other:
assert_never(other)
if __name__ == '__main__':
sys.exit(asyncio.run(main()))