-
Notifications
You must be signed in to change notification settings - Fork 82
frontend,backend,rpmbuild,cli,python: uploadrpm - multiple RPMs, optional srpm/logs #4459
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ | |
| import os | ||
| import shutil | ||
| import statistics | ||
| import tarfile | ||
| import time | ||
| import json | ||
| import shlex | ||
|
|
@@ -581,6 +582,22 @@ def _transfer_log_file(self): | |
| except SSHConnectionError as exc: | ||
| return "Stopped following builder for broken SSH: {}".format(exc) | ||
|
|
||
| def _archive_uploaded_logs(self): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, the more I think about this, the clearer it gets... this should live on the rpmbuild side. The only reason we compress log files on the backend is that we provide "live" logs during the build (not that we couldn't do it better). |
||
| uploaded_logs_dir = os.path.join(self.job.results_dir, "uploaded-logs") | ||
| if not os.path.isdir(uploaded_logs_dir): | ||
| return | ||
|
|
||
| tarball_path = os.path.join(self.job.results_dir, "uploaded-logs.tar.gz") | ||
| try: | ||
| with tarfile.open(tarball_path, "w:gz") as tar: | ||
| for entry in sorted(os.listdir(uploaded_logs_dir)): | ||
| tar.add(os.path.join(uploaded_logs_dir, entry), arcname=entry) | ||
|
Comment on lines
+592
to
+594
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- target function ---'
sed -n '540,620p' backend/copr_backend/background_worker_build.py
printf '%s\n' '--- related test and call sites ---'
rg -n -C 8 'test_archive_uploaded_logs_keeps_tarball|archive_uploaded_logs|uploaded-logs\.tar\.gz' . \
-g '*.py' -g '*.yaml' -g '*.yml'Repository: fedora-copr/copr Length of output: 18064 Preserve an existing uploaded-log archive. If 🤖 Prompt for AI Agents |
||
| except OSError as ex: | ||
| self.log.error("Unable to archive uploaded logs: %s", ex) | ||
| return | ||
|
|
||
| shutil.rmtree(uploaded_logs_dir, ignore_errors=True) | ||
|
|
||
| def _compress_logs(self): | ||
| """ | ||
| Compress builder-live.log, backend.log, and fedora-review.log using gzip. | ||
|
|
@@ -1106,6 +1123,7 @@ def handle_task(self): | |
| self._drop_host() | ||
| if self.job: | ||
| self._mark_finished() | ||
| self._archive_uploaded_logs() | ||
| self._compress_logs() | ||
| else: | ||
| self.log.error("No job object from Frontend") | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -464,17 +464,31 @@ def action_upload_rpm(self, args): | |
| username, projectname, project_dirname = self.parse_dirname(args.copr_repo) | ||
| buildopts = buildopts_from_args(args) | ||
|
|
||
| if not os.path.exists(args.rpm): | ||
| raise CoprException("File {0} not found".format(args.rpm)) | ||
| if len(args.rpms) > 1 and not args.pkgname: | ||
| raise CoprException( | ||
| "--name is required when uploading more than one RPM " | ||
| "(it can't be reliably guessed).") | ||
|
|
||
| all_paths = list(args.rpms) | ||
| if args.srpm: | ||
| all_paths.append(args.srpm) | ||
| if args.logs: | ||
| all_paths.extend(args.logs) | ||
|
|
||
| for path in all_paths: | ||
| if not os.path.exists(path): | ||
| raise CoprException("File {0} not found".format(path)) | ||
|
|
||
| progress_callback = get_progress_callback(os.path.getsize(args.rpm)) | ||
| total_size = sum(os.path.getsize(path) for path in all_paths) | ||
| progress_callback = get_progress_callback(total_size) | ||
| buildopts["progress_callback"] = progress_callback | ||
| print('Uploading package {0}'.format(args.rpm)) | ||
| print('Uploading package(s) {0}'.format(', '.join(all_paths))) | ||
| try: | ||
| build = self.client.build_proxy.create_from_rpm_upload( | ||
| ownername=username, projectname=projectname, | ||
| project_dirname=project_dirname, buildopts=buildopts, | ||
| path=args.rpm, | ||
| paths=args.rpms, name=args.pkgname, | ||
| srpm_path=args.srpm, log_paths=args.logs, | ||
| sha256=getattr(args, "sha256", None)) | ||
| finally: | ||
| if progress_callback: | ||
|
|
@@ -884,6 +898,8 @@ def action_download_build(self, args): | |
|
|
||
| if args.logs: | ||
| cmd.extend(["-A", "*.log.gz"]) | ||
| # tarball of client-uploaded logs for "uploadrpm" builds | ||
| cmd.extend(["-A", "uploaded-logs.tar.gz"]) | ||
|
|
||
| if args.review: | ||
| cmd.extend([ | ||
|
|
@@ -1739,13 +1755,30 @@ def setup_parser(): | |
| # create the parser for the "uploadrpm" command | ||
| parser_upload_rpm = subparsers.add_parser( | ||
| "uploadrpm", parents=[parser_build_parent], | ||
| help="Publish an already-built local RPM directly to a specified copr, " | ||
| "skipping the SRPM build phase entirely") | ||
| help="Publish one or more already-built local RPMs directly to a " | ||
| "specified copr, skipping the SRPM build phase entirely") | ||
| parser_upload_rpm.add_argument( | ||
| "rpms", nargs="+", | ||
| help="Local path(s) to the already-built .rpm file(s) to publish") | ||
| parser_upload_rpm.add_argument( | ||
| "--name", dest="pkgname", required=False, | ||
| help=("Package name. Optional when uploading a single RPM " | ||
| "(guessed from its filename), required when uploading " | ||
| "more than one RPM.")) | ||
| parser_upload_rpm.add_argument( | ||
| "--srpm", dest="srpm", metavar="SRPM", required=False, | ||
| help=("Optional local path to an accompanying .src.rpm/.nosrc.rpm " | ||
| "file, published alongside the uploaded RPM(s)")) | ||
| parser_upload_rpm.add_argument( | ||
| "rpm", help="Local path to the already-built .rpm file to publish") | ||
| "--logs", dest="logs", nargs="+", metavar="LOG", required=False, | ||
| help=("Optional local path(s) to .log/.log.gz/.txt/.txt.gz " | ||
| "file(s), auto-compressed into a single tarball and " | ||
| "stored on the backend filesystem")) | ||
|
Comment on lines
+1773
to
+1776
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- parser definition and nearby positional arguments ---'
sed -n '1715,1805p' cli/copr_cli/main.py
printf '%s\n' '--- uploadrpm parser and dispatch references ---'
rg -n -C 4 'uploadrpm|dest="logs"|dest=.rpms.|add_argument.*rpms|logs' cli/copr_cli/main.py
printf '%s\n' '--- documented command ---'
sed -n '30,48p' cli/man/copr-cli.cheat
printf '%s\n' '--- Beaker command and surrounding setup ---'
sed -n '135,168p' beaker-tests/Sanity/copr-cli-basic-operations/runtest-rpm-upload.shRepository: fedora-copr/copr Length of output: 10733 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- parent parser positional contract ---'
rg -n -C 5 'parser_build_parent|add_argument\([^)]*"project|add_argument\([^)]*"rpms|projectname|project_dirname' cli/copr_cli/main.py | head -n 160
printf '%s\n' '--- minimal argparse behavior for the reviewed argument shapes ---'
python3 - <<'PY'
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("project")
parser.add_argument("rpms", nargs="+")
parser.add_argument("--name")
parser.add_argument("--srpm")
parser.add_argument("--logs", nargs="+")
cases = [
[
"test-project", "--name", "package", "--srpm", "package.src.rpm",
"--logs", "build.log", "build.txt", "package.rpm", "package-devel.rpm",
],
[
"test-project", "--name", "package", "--srpm", "package.src.rpm",
"package.rpm", "package-devel.rpm", "--logs", "build.log", "build.txt",
],
]
for argv in cases:
print("ARGV:", argv)
try:
print("PARSED:", parser.parse_args(argv))
except SystemExit as exc:
print("EXIT:", exc.code)
PYRepository: fedora-copr/copr Length of output: 7810 Make
Use a repeatable single-value log option, or place all RPM paths before 📍 Affects 3 files
🤖 Prompt for AI Agents |
||
| parser_upload_rpm.add_argument( | ||
| "--sha256", help="Expected SHA256 hex digest of the uploaded file; " | ||
| "the server rejects the build on mismatch") | ||
| "--sha256", nargs="+", metavar="SHA256", required=False, | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. due to multiple RPMs this has to be list as well which made the PR more complex than I anticipated... I am thinking whether it is good idea to allow multiple RPMs then? (also the version problem...) |
||
| help=("Optional expected SHA256 hex digest(s) of the uploaded " | ||
| "RPM(s), one per RPM in the same order as the RPM " | ||
| "arguments; the server rejects the build on mismatch")) | ||
| parser_upload_rpm.set_defaults(func="action_upload_rpm") | ||
|
|
||
| # create the parser for the "buildpypi" command | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-1 for using tarfile :-/ if needed, keep it on the rpmbuild side (or call /bin/tar)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 for doing it on builder.... I put the logic into backend since it is doing the compressing logic for other logs as well
btw still -1 for using tarfile on builder?