Skip to content

Commit c21effa

Browse files
committed
feat: introduce unified sync command and grouped syncs
Motivation: Each command execution in production has a high container startup overhead. Combining the most frequent sync operations into a single command reduces this overhead significantly. Design Choices: - Introduced a new "sync" command that runs both SMELT and Gitea syncs. - Moved individual "smelt-sync" and "gitea-sync" commands into the "advanced" sub-command group. - Added hidden, deprecated top-level aliases for "smelt-sync" and "gitea-sync" to maintain backward compatibility. - Unified sync accepts all Gitea-related options. Benefits: - Reduced production runtime by approximately 30 seconds per execution. - Improved CLI organization while maintaining compatibility with existing automation pipelines.
1 parent ac12425 commit c21effa

File tree

2 files changed

+132
-2
lines changed

2 files changed

+132
-2
lines changed

openqabot/args.py

Lines changed: 109 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ def updates_run(
326326
sys.exit(bot())
327327

328328

329-
@app.command("smelt-sync")
329+
@advanced_app.command("smelt-sync")
330330
def smelt_sync(ctx: typer.Context) -> None:
331331
"""Sync data from SMELT into QEM Dashboard."""
332332
args = ctx.obj
@@ -336,7 +336,68 @@ def smelt_sync(ctx: typer.Context) -> None:
336336
sys.exit(syncer())
337337

338338

339-
@app.command("gitea-sync")
339+
@app.command("smelt-sync", hidden=True)
340+
def smelt_sync_deprecated(ctx: typer.Context) -> None:
341+
"""DEPRECATED: Sync data from SMELT into QEM Dashboard (use sync or advanced smelt-sync).""" # noqa: D401
342+
smelt_sync(ctx)
343+
344+
345+
@app.command("sync")
346+
def sync( # noqa: PLR0913
347+
ctx: typer.Context,
348+
*,
349+
gitea_repo: gitea_repo_arg = "products/SLFO",
350+
allow_build_failures: Annotated[
351+
bool,
352+
typer.Option("--allow-build-failures", help="Sync data from PRs despite failing packages"),
353+
] = False,
354+
consider_unrequested_prs: Annotated[
355+
bool,
356+
typer.Option(
357+
"--consider-unrequested-prs",
358+
help=f"Consider PRs where no review from team {config_module.settings.obs_group} was requested as well",
359+
),
360+
] = False,
361+
pr_number: pr_number_arg = None,
362+
amqp: Annotated[
363+
bool,
364+
typer.Option(
365+
"--amqp",
366+
help="After initial sync listen for new PRs via AMQP and submit them to QEM dashboard immediately",
367+
),
368+
] = False,
369+
amqp_url: Annotated[str | None, typer.Option("--amqp-url", help="the URL of the AMQP server")] = None,
370+
skip_initial_sync: Annotated[
371+
bool,
372+
typer.Option(
373+
"--amqp-only",
374+
help="Skip initial sync before handling AMQP events for new PRs",
375+
),
376+
] = False,
377+
) -> None:
378+
"""Sync data from both SMELT and Gitea into QEM Dashboard."""
379+
args = ctx.obj
380+
_require_token(args)
381+
382+
# SMELT sync
383+
smelt_ret = SMELTSync(args)()
384+
385+
# Gitea sync
386+
args.gitea_repo = gitea_repo
387+
args.allow_build_failures = allow_build_failures
388+
args.consider_unrequested_prs = consider_unrequested_prs
389+
args.pr_number = pr_number
390+
args.amqp = amqp
391+
# Default from settings (which was already loaded in main callback)
392+
args.amqp_url = amqp_url if amqp_url is not None else config_module.settings.amqp_url
393+
args.skip_initial_sync = skip_initial_sync
394+
395+
gitea_ret = GiteaSync(args)()
396+
397+
sys.exit(smelt_ret or gitea_ret)
398+
399+
400+
@advanced_app.command("gitea-sync")
340401
def gitea_sync( # noqa: PLR0913
341402
ctx: typer.Context,
342403
*,
@@ -385,6 +446,52 @@ def gitea_sync( # noqa: PLR0913
385446
sys.exit(syncer())
386447

387448

449+
@app.command("gitea-sync", hidden=True)
450+
def gitea_sync_deprecated( # noqa: PLR0913
451+
ctx: typer.Context,
452+
*,
453+
gitea_repo: gitea_repo_arg = "products/SLFO",
454+
allow_build_failures: Annotated[
455+
bool,
456+
typer.Option("--allow-build-failures", help="Sync data from PRs despite failing packages"),
457+
] = False,
458+
consider_unrequested_prs: Annotated[
459+
bool,
460+
typer.Option(
461+
"--consider-unrequested-prs",
462+
help=f"Consider PRs where no review from team {config_module.settings.obs_group} was requested as well",
463+
),
464+
] = False,
465+
pr_number: pr_number_arg = None,
466+
amqp: Annotated[
467+
bool,
468+
typer.Option(
469+
"--amqp",
470+
help="After initial sync listen for new PRs via AMQP and submit them to QEM dashboard immediately",
471+
),
472+
] = False,
473+
amqp_url: Annotated[str | None, typer.Option("--amqp-url", help="the URL of the AMQP server")] = None,
474+
skip_initial_sync: Annotated[
475+
bool,
476+
typer.Option(
477+
"--amqp-only",
478+
help="Skip initial sync before handling AMQP events for new PRs",
479+
),
480+
] = False,
481+
) -> None:
482+
"""DEPRECATED: Sync data from Gitea into QEM Dashboard (use sync or advanced gitea-sync).""" # noqa: D401
483+
gitea_sync(
484+
ctx,
485+
gitea_repo=gitea_repo,
486+
allow_build_failures=allow_build_failures,
487+
consider_unrequested_prs=consider_unrequested_prs,
488+
pr_number=pr_number,
489+
amqp=amqp,
490+
amqp_url=amqp_url,
491+
skip_initial_sync=skip_initial_sync,
492+
)
493+
494+
388495
@app.command("gitea-trigger")
389496
def gitea_trigger( # noqa: PLR0913
390497
ctx: typer.Context,

tests/test_args.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,40 @@ def test_updates_run(mocker: MockerFixture, tmp_path: Path) -> None:
5757
def test_sync_smelt(mocker: MockerFixture, tmp_path: Path) -> None:
5858
syncer = mocker.patch("openqabot.args.SMELTSync")
5959
syncer.return_value.return_value = 0
60+
# Test top-level (deprecated)
6061
result = runner.invoke(app, ["--token", "foo", "--configs", str(tmp_path), "smelt-sync"])
6162
assert result.exit_code == 0
6263
syncer.assert_called_once()
64+
# Test advanced group
65+
syncer.reset_mock()
66+
result = runner.invoke(app, ["--token", "foo", "--configs", str(tmp_path), "advanced", "smelt-sync"])
67+
assert result.exit_code == 0
68+
syncer.assert_called_once()
6369

6470

6571
def test_sync_gitea(mocker: MockerFixture, tmp_path: Path) -> None:
6672
syncer = mocker.patch("openqabot.args.GiteaSync")
6773
syncer.return_value.return_value = 0
74+
# Test top-level (deprecated)
6875
result = runner.invoke(app, ["--token", "foo", "--configs", str(tmp_path), "gitea-sync"])
6976
assert result.exit_code == 0
7077
syncer.assert_called_once()
78+
# Test advanced group
79+
syncer.reset_mock()
80+
result = runner.invoke(app, ["--token", "foo", "--configs", str(tmp_path), "advanced", "gitea-sync"])
81+
assert result.exit_code == 0
82+
syncer.assert_called_once()
83+
84+
85+
def test_sync(mocker: MockerFixture, tmp_path: Path) -> None:
86+
smelt = mocker.patch("openqabot.args.SMELTSync")
87+
smelt.return_value.return_value = 0
88+
gitea = mocker.patch("openqabot.args.GiteaSync")
89+
gitea.return_value.return_value = 0
90+
result = runner.invoke(app, ["--token", "foo", "--configs", str(tmp_path), "sync"])
91+
assert result.exit_code == 0
92+
smelt.assert_called_once()
93+
gitea.assert_called_once()
7194

7295

7396
def test_gitea_trigger(mocker: MockerFixture, tmp_path: Path) -> None:

0 commit comments

Comments
 (0)