Skip to content

Commit 521c7c4

Browse files
gruljaclaude
andcommitted
Add --version argument to pull-from-upstream comment command
Allow users to specify a version string when retriggering via a dist-git PR comment. Fixes #3033 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 1d3ed53 commit 521c7c4

File tree

3 files changed

+272
-0
lines changed

3 files changed

+272
-0
lines changed

packit_service/utils.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,10 @@ def get_pr_comment_parser(prog: str, description: str, epilog: str) -> argparse.
299299
action="store_true",
300300
help="Use the configuration file from this dist-git pull request",
301301
)
302+
pull_from_upstream_parser.add_argument(
303+
"--version",
304+
help="Version to use for the pull-from-upstream job",
305+
)
302306

303307
subparsers.add_parser(
304308
"koji-build",

packit_service/worker/handlers/distgit.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,35 @@ def get_resolved_bugs(self) -> list[str]:
747747
)
748748
return bugs.split(",")
749749

750+
def get_version_from_comment(self) -> Optional[str]:
751+
"""
752+
Extract the --version argument from the PR comment if present.
753+
The format in the comment should be:
754+
/packit pull-from-upstream --version 1.2.3
755+
"""
756+
comment = self.data.event_dict.get("comment")
757+
if not comment:
758+
return None
759+
760+
commands = get_packit_commands_from_comment(
761+
comment,
762+
self.service_config.comment_command_prefix,
763+
)
764+
args = commands[1:] if len(commands) > 1 else []
765+
version_keyword = "--version"
766+
if version_keyword not in args:
767+
return None
768+
769+
idx = args.index(version_keyword)
770+
return args[idx + 1] if idx < len(args) - 1 else None
771+
772+
@property
773+
def tag(self) -> Optional[str]:
774+
version = self.get_version_from_comment()
775+
if version:
776+
return version
777+
return super().tag
778+
750779
def _report_errors_for_each_branch(self, message: str) -> None:
751780
body_msg = (
752781
f"{message}\n\n---\n\n*Get in [touch with us]({CONTACTS_URL}) if you need some help.*\n"
@@ -771,6 +800,9 @@ def _run(self) -> TaskResults:
771800
with ChoosenGithubAuthMethod(self, AuthMethod.token):
772801
# allow upstream git_project to be None
773802
self.packit_api.up._project_required = False
803+
version = self.get_version_from_comment()
804+
if version:
805+
self.data.event_dict["version"] = version
774806
return super()._run()
775807

776808

tests/integration/test_pr_comment.py

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3142,6 +3142,242 @@ def _get_project(url, *_, **__):
31423142
assert first_dict_value(results["job"])["success"]
31433143

31443144

3145+
def _run_pull_from_upstream_with_version(
3146+
pagure_pr_comment_added,
3147+
packit_yaml,
3148+
sync_release_version_kwargs,
3149+
git_upstream_project=None,
3150+
):
3151+
"""
3152+
Shared setup for --version tests. Pass git_upstream_project for a git upstream
3153+
(tag=), or leave it as None for a non-git upstream (versions=).
3154+
"""
3155+
pagure_pr_comment_added["pullrequest"]["comments"][0]["comment"] = (
3156+
"/packit pull-from-upstream --version 2.0.0"
3157+
)
3158+
sync_release_pr_model = flexmock(sync_release_targets=[flexmock(), flexmock()])
3159+
model = flexmock(status="queued", id=1234, branch="main")
3160+
flexmock(SyncReleaseTargetModel).should_receive("create").with_args(
3161+
status=SyncReleaseTargetStatus.queued,
3162+
branch="main",
3163+
).and_return(model)
3164+
flexmock(SyncReleasePullRequestModel).should_receive("get_or_create").with_args(
3165+
pr_id=21,
3166+
namespace="downstream-namespace",
3167+
repo_name="downstream-repo",
3168+
project_url="https://src.fedoraproject.org/rpms/downstream-repo",
3169+
target_branch=str,
3170+
url=str,
3171+
).and_return(sync_release_pr_model)
3172+
3173+
pr_mock = (
3174+
flexmock()
3175+
.should_receive("comment")
3176+
.with_args(
3177+
"The task was accepted. You can check the recent runs of pull from upstream jobs in "
3178+
"[Packit dashboard](/jobs/pull-from-upstreams)"
3179+
f"{DistgitAnnouncement.get_comment_footer_with_announcement_if_present()}",
3180+
)
3181+
.mock()
3182+
)
3183+
distgit_project = flexmock(
3184+
get_files=lambda ref, recursive: [".packit.yaml"],
3185+
get_file_content=lambda path, ref, headers: packit_yaml,
3186+
full_repo_name=pagure_pr_comment_added["pullrequest"]["project"]["fullname"],
3187+
repo=pagure_pr_comment_added["pullrequest"]["project"]["name"],
3188+
namespace=pagure_pr_comment_added["pullrequest"]["project"]["namespace"],
3189+
is_private=lambda: False,
3190+
default_branch="main",
3191+
service=flexmock(get_project=lambda **_: None),
3192+
get_pr=lambda pr_id: pr_mock,
3193+
)
3194+
3195+
lp = flexmock(LocalProject, refresh_the_arguments=lambda: None)
3196+
flexmock(LocalProjectBuilder, _refresh_the_state=lambda *args: lp)
3197+
lp.working_dir = ""
3198+
flexmock(DistGit).should_receive("local_project").and_return(lp)
3199+
3200+
if git_upstream_project:
3201+
flexmock(Github, get_repo=lambda full_name_or_id: None)
3202+
lp.git_project = git_upstream_project
3203+
flexmock(LocalProject).should_receive("git_repo").and_return(
3204+
flexmock(
3205+
head=flexmock()
3206+
.should_receive("reset")
3207+
.with_args("HEAD", index=True, working_tree=True)
3208+
.once()
3209+
.mock(),
3210+
git=flexmock(clear_cache=lambda: None),
3211+
submodules=[
3212+
flexmock()
3213+
.should_receive("update")
3214+
.with_args(init=True, recursive=True, force=True)
3215+
.once()
3216+
.mock()
3217+
],
3218+
),
3219+
)
3220+
# get_last_tag would return "7.0.3" but --version 2.0.0 from the comment must win
3221+
flexmock(GitUpstream).should_receive("get_last_tag").and_return("7.0.3").never()
3222+
3223+
flexmock(GithubService).should_receive("set_auth_method").with_args(
3224+
AuthMethod.token,
3225+
).once()
3226+
flexmock(Allowlist, check_and_report=True)
3227+
flexmock(PackitAPIWithDownstreamMixin).should_receive("is_packager").and_return(True)
3228+
3229+
def _get_project(url, *_, **__):
3230+
if url == pagure_pr_comment_added["pullrequest"]["project"]["full_url"]:
3231+
return distgit_project
3232+
return git_upstream_project
3233+
3234+
service_config = ServiceConfig().get_service_config()
3235+
flexmock(service_config).should_receive("get_project").replace_with(_get_project)
3236+
target_project = (
3237+
flexmock(namespace="downstream-namespace", repo="downstream-repo")
3238+
.should_receive("get_web_url")
3239+
.and_return("https://src.fedoraproject.org/rpms/downstream-repo")
3240+
.mock()
3241+
)
3242+
pr = (
3243+
flexmock(id=21, url="some_url", target_project=target_project, description="")
3244+
.should_receive("comment")
3245+
.mock()
3246+
)
3247+
flexmock(PackitAPI).should_receive("sync_release").with_args(
3248+
dist_git_branch="main",
3249+
**sync_release_version_kwargs,
3250+
create_pr=True,
3251+
local_pr_branch_suffix="update-pull_from_upstream",
3252+
use_downstream_specfile=True,
3253+
add_pr_instructions=True,
3254+
resolved_bugs=[],
3255+
release_monitoring_project_id=None,
3256+
sync_acls=True,
3257+
pr_description_footer=DistgitAnnouncement.get_announcement(),
3258+
add_new_sources=True,
3259+
fast_forward_merge_branches=set(),
3260+
warn_about_koji_build_triggering_bug=False,
3261+
).and_return((pr, {})).once()
3262+
flexmock(PackitAPI).should_receive("clean")
3263+
3264+
flexmock(model).should_receive("set_status").with_args(
3265+
status=SyncReleaseTargetStatus.running,
3266+
).once()
3267+
flexmock(model).should_receive("set_downstream_pr_url").with_args(
3268+
downstream_pr_url="some_url",
3269+
).once()
3270+
flexmock(model).should_receive("set_downstream_prs").with_args(
3271+
downstream_prs=[sync_release_pr_model],
3272+
).once()
3273+
flexmock(model).should_receive("set_status").with_args(
3274+
status=SyncReleaseTargetStatus.submitted,
3275+
).once()
3276+
flexmock(model).should_receive("set_start_time").once()
3277+
flexmock(model).should_receive("set_finished_time").once()
3278+
flexmock(model).should_receive("set_logs").once()
3279+
3280+
db_project_object = flexmock(
3281+
id=12,
3282+
project_event_model_type=ProjectEventModelType.pull_request,
3283+
job_config_trigger_type=JobConfigTriggerType.pull_request,
3284+
)
3285+
db_project_event = (
3286+
flexmock().should_receive("get_project_event_object").and_return(db_project_object).mock()
3287+
)
3288+
run_model = flexmock(PipelineModel)
3289+
flexmock(ProjectEventModel).should_receive("get_or_create").with_args(
3290+
type=ProjectEventModelType.pull_request,
3291+
event_id=12,
3292+
commit_sha="beaf90bcecc51968a46663f8d6f092bfdc92e682",
3293+
).and_return(db_project_event)
3294+
flexmock(PullRequestModel).should_receive("get_or_create").with_args(
3295+
pr_id=pagure_pr_comment_added["pullrequest"]["id"],
3296+
namespace=pagure_pr_comment_added["pullrequest"]["project"]["namespace"],
3297+
repo_name=pagure_pr_comment_added["pullrequest"]["project"]["name"],
3298+
project_url=pagure_pr_comment_added["pullrequest"]["project"]["full_url"],
3299+
).and_return(db_project_object)
3300+
sync_release_model = flexmock(id=123, sync_release_targets=[])
3301+
flexmock(SyncReleaseModel).should_receive("create_with_new_run").with_args(
3302+
status=SyncReleaseStatus.running,
3303+
project_event_model=db_project_event,
3304+
job_type=SyncReleaseJobType.pull_from_upstream,
3305+
package_name="python-teamcity-messages",
3306+
).and_return(sync_release_model, run_model).once()
3307+
flexmock(sync_release_model).should_receive("set_status").with_args(
3308+
status=SyncReleaseStatus.finished,
3309+
).once()
3310+
3311+
flexmock(IsRunConditionSatisfied).should_receive("pre_check").and_return(True)
3312+
3313+
flexmock(AddPullRequestEventToDb).should_receive("db_project_object").and_return(
3314+
flexmock(
3315+
job_config_trigger_type=JobConfigTriggerType.pull_request,
3316+
id=123,
3317+
project=flexmock(project_url=None),
3318+
project_event_model_type=ProjectEventModelType.pull_request,
3319+
),
3320+
)
3321+
flexmock(celery_group).should_receive("apply_async").once()
3322+
flexmock(Pushgateway).should_receive("push").times(2).and_return()
3323+
flexmock(shutil).should_receive("rmtree").with_args("")
3324+
flexmock(pagure.pr.Comment).should_receive(
3325+
"get_base_project",
3326+
).once().and_return(distgit_project)
3327+
3328+
processing_results = SteveJobs().process_message(pagure_pr_comment_added)
3329+
event_dict, _, job_config, package_config = get_parameters_from_results(
3330+
processing_results,
3331+
)
3332+
assert json.dumps(event_dict)
3333+
3334+
results = run_pull_from_upstream_handler(
3335+
package_config=package_config,
3336+
event=event_dict,
3337+
job_config=job_config,
3338+
)
3339+
assert first_dict_value(results["job"])["success"]
3340+
3341+
3342+
def test_pull_from_upstream_retrigger_via_dist_git_pr_comment_with_version(
3343+
pagure_pr_comment_added,
3344+
):
3345+
"""--version overrides get_last_tag() for a git upstream."""
3346+
_run_pull_from_upstream_with_version(
3347+
pagure_pr_comment_added,
3348+
packit_yaml=(
3349+
"{'specfile_path': 'hello-world.spec', 'upstream_project_url': "
3350+
"'https://github.com/packit-service/hello-world'"
3351+
", jobs: [{trigger: release, job: pull_from_upstream, metadata: {targets:[]}}]}"
3352+
),
3353+
sync_release_version_kwargs={"tag": "2.0.0"},
3354+
git_upstream_project=flexmock(
3355+
full_repo_name="packit-service/hello-world",
3356+
repo="hello-world",
3357+
namespace="packit-service",
3358+
get_files=lambda ref, filter_regex: [],
3359+
get_sha_from_tag=lambda tag_name: "123456",
3360+
get_web_url=lambda: "https://github.com/packit/hello-world",
3361+
is_private=lambda: False,
3362+
default_branch="main",
3363+
),
3364+
)
3365+
3366+
3367+
def test_pull_from_upstream_retrigger_via_dist_git_pr_comment_non_git_with_version(
3368+
pagure_pr_comment_added,
3369+
):
3370+
"""--version is passed as versions= to sync_release for a non-git upstream."""
3371+
_run_pull_from_upstream_with_version(
3372+
pagure_pr_comment_added,
3373+
packit_yaml=(
3374+
"{'specfile_path': 'hello-world.spec', "
3375+
"jobs: [{trigger: release, job: pull_from_upstream, metadata: {targets:[]}}]}"
3376+
),
3377+
sync_release_version_kwargs={"versions": ["2.0.0"]},
3378+
)
3379+
3380+
31453381
@pytest.mark.parametrize(
31463382
"all_branches",
31473383
[False, True],

0 commit comments

Comments
 (0)