Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package openQA for openSUSE:Factory checked 
in at 2026-06-22 17:27:35
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/openQA (Old)
 and      /work/SRC/openSUSE:Factory/.openQA.new.1956 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "openQA"

Mon Jun 22 17:27:35 2026 rev:855 rq:1360717 version:5.1781884690.e39cc969

Changes:
--------
--- /work/SRC/openSUSE:Factory/openQA/openQA.changes    2026-06-19 
17:17:54.402918373 +0200
+++ /work/SRC/openSUSE:Factory/.openQA.new.1956/openQA.changes  2026-06-22 
17:28:17.155334940 +0200
@@ -1,0 +2,13 @@
+Fri Jun 19 16:54:50 UTC 2026 - [email protected]
+
+- Update to version 5.1781884690.e39cc969:
+  * test: Make parallel execution of the fullstack test more reliable
+  * ci: disable Mergify interactive queue controls in PR comments
+  * feat(worker): enable direct execution with podman
+  * fix(worker): add --init and --rm flags for containerized engine
+  * feat(worker): support containerized os-autoinst worker engine
+  * test: refactor archive download tests for maintainability
+  * feat: implement category-specific ZIP downloads for jobs
+  * chore(deps): Dependency cron 2026-06-19
+
+-------------------------------------------------------------------

Old:
----
  openQA-5.1781832185.5ddf5343.obscpio

New:
----
  openQA-5.1781884690.e39cc969.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ openQA-client-test.spec ++++++
--- /var/tmp/diff_new_pack.vHObut/_old  2026-06-22 17:28:18.847393883 +0200
+++ /var/tmp/diff_new_pack.vHObut/_new  2026-06-22 17:28:18.851394022 +0200
@@ -18,7 +18,7 @@
 
 %define         short_name openQA-client
 Name:           %{short_name}-test
-Version:        5.1781832185.5ddf5343
+Version:        5.1781884690.e39cc969
 Release:        0
 Summary:        Test package for %{short_name}
 License:        GPL-2.0-or-later

++++++ openQA-devel-test.spec ++++++
--- /var/tmp/diff_new_pack.vHObut/_old  2026-06-22 17:28:18.879394997 +0200
+++ /var/tmp/diff_new_pack.vHObut/_new  2026-06-22 17:28:18.883395137 +0200
@@ -18,7 +18,7 @@
 
 %define         short_name openQA-devel
 Name:           %{short_name}-test
-Version:        5.1781832185.5ddf5343
+Version:        5.1781884690.e39cc969
 Release:        0
 Summary:        Test package for %{short_name}
 License:        GPL-2.0-or-later

++++++ openQA-test.spec ++++++
--- /var/tmp/diff_new_pack.vHObut/_old  2026-06-22 17:28:18.915396252 +0200
+++ /var/tmp/diff_new_pack.vHObut/_new  2026-06-22 17:28:18.919396391 +0200
@@ -18,7 +18,7 @@
 
 %define         short_name openQA
 Name:           %{short_name}-test
-Version:        5.1781832185.5ddf5343
+Version:        5.1781884690.e39cc969
 Release:        0
 Summary:        Test package for openQA
 License:        GPL-2.0-or-later

++++++ openQA-worker-test.spec ++++++
--- /var/tmp/diff_new_pack.vHObut/_old  2026-06-22 17:28:18.947397366 +0200
+++ /var/tmp/diff_new_pack.vHObut/_new  2026-06-22 17:28:18.947397366 +0200
@@ -18,7 +18,7 @@
 
 %define         short_name openQA-worker
 Name:           %{short_name}-test
-Version:        5.1781832185.5ddf5343
+Version:        5.1781884690.e39cc969
 Release:        0
 Summary:        Test package for %{short_name}
 License:        GPL-2.0-or-later

++++++ openQA.spec ++++++
--- /var/tmp/diff_new_pack.vHObut/_old  2026-06-22 17:28:18.975398341 +0200
+++ /var/tmp/diff_new_pack.vHObut/_new  2026-06-22 17:28:18.979398481 +0200
@@ -104,7 +104,7 @@
 %define devel_requires %devel_no_selenium_requires chromedriver
 
 Name:           openQA
-Version:        5.1781832185.5ddf5343
+Version:        5.1781884690.e39cc969
 Release:        0
 Summary:        Framework for automated system-level testing (web-frontend, 
scheduler and tools)
 Group:          Development/Tools/Other

++++++ openQA-5.1781832185.5ddf5343.obscpio -> 
openQA-5.1781884690.e39cc969.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-5.1781832185.5ddf5343/.mergify.yml 
new/openQA-5.1781884690.e39cc969/.mergify.yml
--- old/openQA-5.1781832185.5ddf5343/.mergify.yml       2026-06-19 
03:23:05.000000000 +0200
+++ new/openQA-5.1781884690.e39cc969/.mergify.yml       2026-06-19 
17:58:10.000000000 +0200
@@ -61,3 +61,4 @@
 queue_rules: []
 merge_queue:
   status_comments: none
+  queue_controls_comment: false
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-5.1781832185.5ddf5343/docs/WritingTests.md 
new/openQA-5.1781884690.e39cc969/docs/WritingTests.md
--- old/openQA-5.1781832185.5ddf5343/docs/WritingTests.md       2026-06-19 
03:23:05.000000000 +0200
+++ new/openQA-5.1781884690.e39cc969/docs/WritingTests.md       2026-06-19 
17:58:10.000000000 +0200
@@ -726,6 +726,16 @@
 test variable setting
 `ISOTOVIDEO=podman run --pull=always --rm -it 
registry.example.org/my/container/isotovideo /usr/bin/isotovideo -d`
 
+Alternatively, you can run the worker engine inside a rootless Podman
+container by setting `OS_AUTOINST_GIT_REPO` to a Git repository URL.
+openQA will construct a Podman command to clone, build, and execute
+`os-autoinst` from that repository.
+The following optional test variables are supported:
+* `OS_AUTOINST_GIT_BRANCH`: The branch to clone (defaults to `master`).
+* `OS_AUTOINST_CONTAINER_IMAGE`: A custom container image to use (defaults to
+  `registry.opensuse.org/devel/openqa/containers/os-autoinst_dev:latest`).
+  `OS_AUTOINST_CONTAINER_IMAGE` requires `OS_AUTOINST_GIT_REPO` to be set.
+
 ### Automatic retries of jobs
 
 You might encounter flaky openQA tests that fail sporadically. The best way to
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-5.1781832185.5ddf5343/lib/OpenQA/Archive.pm 
new/openQA-5.1781884690.e39cc969/lib/OpenQA/Archive.pm
--- old/openQA-5.1781832185.5ddf5343/lib/OpenQA/Archive.pm      2026-06-19 
03:23:05.000000000 +0200
+++ new/openQA-5.1781884690.e39cc969/lib/OpenQA/Archive.pm      2026-06-19 
17:58:10.000000000 +0200
@@ -13,6 +13,8 @@
 use Feature::Compat::Try;
 use Fcntl qw(:flock);
 
+use constant VALID_CATEGORIES => qw(all resultfiles ulogs assets);
+
 sub archive_cache_dir () {
     return $ENV{OPENQA_JOB_DETAILS_ARCHIVE_CACHE_DIR} if 
$ENV{OPENQA_JOB_DETAILS_ARCHIVE_CACHE_DIR};
     my $config = OpenQA::App->singleton->config->{job_details_archive};
@@ -34,11 +36,15 @@
       // 80;
 }
 
-sub create_job_archive ($job) {
+sub job_archive_filename ($job_id, $category = 'all') {
+    return $category eq 'all' ? "job_$job_id.zip" : 
"job_${job_id}_$category.zip";
+}
+
+sub create_job_archive ($job, $category = 'all') {
     my $job_id = $job->id;
     my $cache_dir = path(archive_cache_dir());
     $cache_dir->make_path unless -d $cache_dir;
-    my $archive_name = "job_$job_id.zip";
+    my $archive_name = job_archive_filename($job_id, $category);
     my $archive_path = $cache_dir->child($archive_name);
     my $lock_path = $cache_dir->child("$archive_name.lock");
     open my $lock_fh, '>', $lock_path->to_string
@@ -48,20 +54,39 @@
         close $lock_fh;
         return $archive_path;
     }
-    log_info "Creating archive for job $job_id at $archive_path";
+    log_info "Creating archive for job $job_id (category: $category) at 
$archive_path";
     # Archive::Zip does not keep all member data in memory. When using addFile 
or
     # addTree, it only remembers the filenames. writeToFileNamed then streams
     # the content from the original files to the output zip.
     my $zip = Archive::Zip->new();
-    if (my $res_dir = $job->result_dir) {
-        log_debug "Adding results from $res_dir to archive";
-        $zip->addTree($res_dir, 'testresults/') if -d $res_dir;
+    my $res_dir = $job->result_dir;
+
+    if ($res_dir && -d $res_dir) {
+        if ($category eq 'all') {
+            log_debug "Adding results from $res_dir to archive";
+            $zip->addTree($res_dir, 'testresults/');
+        }
+        elsif ($category eq 'resultfiles') {
+            for my $file (@{$job->test_resultfile_list}) {
+                my $full_path = path($res_dir)->child($file);
+                $zip->addFile($full_path->to_string, "testresults/$file") if 
-e $full_path;
+            }
+        }
+        elsif ($category eq 'ulogs') {
+            my $ulogs_dir = path($res_dir)->child('ulogs');
+            for my $file (@{$job->test_uploadlog_list}) {
+                my $full_path = $ulogs_dir->child($file);
+                $zip->addFile($full_path->to_string, 
"testresults/ulogs/$file") if -e $full_path;
+            }
+        }
     }
-    my $assets = $job->jobs_assets;
-    while (my $ja = $assets->next) {
-        my $asset = $ja->asset;
-        my $disk_file = $asset->disk_file;
-        if ($disk_file && -e $disk_file) {
+
+    if ($category eq 'all' || $category eq 'assets') {
+        my $assets = $job->jobs_assets;
+        while (my $ja = $assets->next) {
+            my $asset = $ja->asset;
+            my $disk_file = $asset->disk_file;
+            next unless $disk_file && -e $disk_file;
             log_debug 'Adding asset ' . $asset->name . ' to archive';
             my $zip_path = $asset->type . '/' . $asset->name;
             if (-d $disk_file) {
@@ -72,6 +97,7 @@
             }
         }
     }
+
     cleanup_cache();
     my $temp_path = $cache_dir->child($archive_name . '.tmp.' . random_hex(8));
     my $status = $zip->writeToFileNamed($temp_path->to_string);
@@ -100,7 +126,8 @@
 
 sub _perform_cache_cleanup ($cache_dir) {
     my ($available, $total) = check_df($cache_dir->to_string);
-    my $archives = $cache_dir->list->grep(sub { $_->basename =~ 
/^job_\d+\.zip$/ })->map(
+    my $category_regex = join '|', VALID_CATEGORIES;
+    my $archives = $cache_dir->list->grep(sub { $_->basename =~ 
/^job_\d+(?:_(?:$category_regex))?\.zip$/ })->map(
         sub {
             my $stat = $_->stat;
             {path => $_, mtime => $stat->mtime, size => $stat->size};
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-5.1781832185.5ddf5343/lib/OpenQA/Task/Job/CreateZipArchive.pm 
new/openQA-5.1781884690.e39cc969/lib/OpenQA/Task/Job/CreateZipArchive.pm
--- old/openQA-5.1781832185.5ddf5343/lib/OpenQA/Task/Job/CreateZipArchive.pm    
2026-06-19 03:23:05.000000000 +0200
+++ new/openQA-5.1781884690.e39cc969/lib/OpenQA/Task/Job/CreateZipArchive.pm    
2026-06-19 17:58:10.000000000 +0200
@@ -12,7 +12,7 @@
     $app->minion->add_task(create_zip_archive => \&_create_zip_archive);
 }
 
-sub _create_zip_archive ($minion_job, $job_id) {
+sub _create_zip_archive ($minion_job, $job_id, $category = 'all') {
     my $app = $minion_job->app;
 
     # avoid running too many archive generation jobs in parallel
@@ -26,7 +26,7 @@
         return undef;
     }
     try {
-        OpenQA::Archive::create_job_archive($job);
+        OpenQA::Archive::create_job_archive($job, $category);
         $minion_job->finish;
     }
     catch ($e) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-5.1781832185.5ddf5343/lib/OpenQA/WebAPI/Controller/File.pm 
new/openQA-5.1781884690.e39cc969/lib/OpenQA/WebAPI/Controller/File.pm
--- old/openQA-5.1781832185.5ddf5343/lib/OpenQA/WebAPI/Controller/File.pm       
2026-06-19 03:23:05.000000000 +0200
+++ new/openQA-5.1781884690.e39cc969/lib/OpenQA/WebAPI/Controller/File.pm       
2026-06-19 17:58:10.000000000 +0200
@@ -160,21 +160,24 @@
 
 sub archive ($self) {
     return $self->reply->not_found unless my $job = 
$self->schema->resultset('Jobs')->find($self->param('testid'));
+    my $category = $self->param('category') // 'all';
+    my %valid = map { $_ => 1 } OpenQA::Archive::VALID_CATEGORIES;
+    return $self->reply->not_found unless $valid{$category};
     my $job_id = $job->id;
-    my $archive_name = "job_$job_id.zip";
+    my $archive_name = OpenQA::Archive::job_archive_filename($job_id, 
$category);
     my $cache_dir = path(OpenQA::Archive::archive_cache_dir());
     my $archive_path = $cache_dir->child($archive_name);
     return $self->_redirect_to_archive($archive_path) if -e $archive_path;
     try {
         if (my $minion = $self->app->can('minion') ? $self->app->minion : 
undef) {
-            $self->app->log->info("Enqueuing create_zip_archive for job 
$job_id");
+            $self->app->log->info("Enqueuing create_zip_archive for job 
$job_id (category: $category)");
             $minion->enqueue(
-                create_zip_archive => [$job_id],
-                {notes => {job_id => $job_id}, priority => -10, expire => 
3600});
+                create_zip_archive => [$job_id, $category],
+                {notes => {job_id => $job_id, category => $category}, priority 
=> -10, expire => 3600});
         }
         else {
             # Fallback for environments without a fully-functional Minion 
(e.g. tests)
-            return 
$self->_redirect_to_archive(OpenQA::Archive::create_job_archive($job));
+            return 
$self->_redirect_to_archive(OpenQA::Archive::create_job_archive($job, 
$category));
         }
     }
     catch ($e) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-5.1781832185.5ddf5343/lib/OpenQA/Worker/Engines/isotovideo.pm 
new/openQA-5.1781884690.e39cc969/lib/OpenQA/Worker/Engines/isotovideo.pm
--- old/openQA-5.1781832185.5ddf5343/lib/OpenQA/Worker/Engines/isotovideo.pm    
2026-06-19 03:23:05.000000000 +0200
+++ new/openQA-5.1781884690.e39cc969/lib/OpenQA/Worker/Engines/isotovideo.pm    
2026-06-19 17:58:10.000000000 +0200
@@ -7,7 +7,7 @@
 use OpenQA::JobSettings;
 use OpenQA::Log qw(log_error log_info log_debug log_warning get_channel_handle 
format_settings);
 use OpenQA::Utils
-  qw(asset_type_from_setting base_host locate_asset looks_like_url_with_scheme 
effective_distri testcasedir productdir needledir);
+  qw(asset_type_from_setting base_host locate_asset looks_like_url_with_scheme 
effective_distri testcasedir productdir needledir prjdir);
 use POSIX qw(:sys_wait_h strftime uname _exit);
 use Mojo::JSON 'encode_json';    # booleans
 use Cpanel::JSON::XS ();
@@ -15,7 +15,7 @@
 use File::Spec::Functions qw(abs2rel catdir file_name_is_absolute);
 use File::Basename qw(basename fileparse);
 use Errno;
-use Cwd 'abs_path';
+use Cwd qw(abs_path getcwd);
 use OpenQA::CacheService::Client;
 use OpenQA::CacheService::Request;
 use Time::HiRes 'sleep';
@@ -477,8 +477,8 @@
             # them in the spawned process as it does not belong to openQA code
             local $ENV{PERL5OPT} = '';
             # Allow to override isotovideo executable with an arbitrary
-            # command line based on a config option
-            exec $job_settings->{ISOTOVIDEO} ? $job_settings->{ISOTOVIDEO} : 
('perl', $isotovideo, '-d');
+            # command line based on a config option or environment variables
+            exec _construct_isotovideo_cmd($job_settings, $isotovideo);
             die "exec failed: $!\n";    # uncoverable statement
         });
     $child->on(
@@ -521,4 +521,45 @@
     return undef;
 }
 
+sub _construct_isotovideo_cmd ($job_settings, $isotovideo) {
+    if (my $custom_cmd = $job_settings->{ISOTOVIDEO}) {
+        return $custom_cmd;
+    }
+
+    if ($job_settings->{OS_AUTOINST_CONTAINER_IMAGE} && 
!$job_settings->{OS_AUTOINST_GIT_REPO}) {
+        die
+"OS_AUTOINST_CONTAINER_IMAGE requires OS_AUTOINST_GIT_REPO. To run a custom 
container command, use ISOTOVIDEO instead.\n";
+    }
+
+    if (my $repo = $job_settings->{OS_AUTOINST_GIT_REPO}) {
+        my $branch = $job_settings->{OS_AUTOINST_GIT_BRANCH} // 'master';
+        my $image = $job_settings->{OS_AUTOINST_CONTAINER_IMAGE}
+          // 
'registry.opensuse.org/devel/openqa/containers/os-autoinst_dev:latest';
+
+        my $podman_dir = prjdir() . '/cache/podman';
+        my @cmd = (
+            'env',
+            "HOME=$podman_dir",
+            'podman',
+            '--root', "$podman_dir/data/containers/storage",
+            '--runroot', "$podman_dir/run/containers",
+            '--storage-opt', 'ignore_chown_errors=true',
+            '--cgroup-manager=cgroupfs',
+            '--events-backend=file',
+            'run',
+            '--init',
+            '--rm',
+            '--entrypoint', '',
+            '--device', '/dev/kvm',
+            '-v', getcwd() . ':/pool',
+            '-w', '/pool',
+            $image,
+            'sh', '-c', "git clone --branch=$branch --depth=1 $repo && make -C 
os-autoinst && os-autoinst/isotovideo -d"
+        );
+        return @cmd;
+    }
+
+    return ('perl', $isotovideo, '-d');
+}
+
 1;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-5.1781832185.5ddf5343/t/24-worker-engine.t 
new/openQA-5.1781884690.e39cc969/t/24-worker-engine.t
--- old/openQA-5.1781832185.5ddf5343/t/24-worker-engine.t       2026-06-19 
03:23:05.000000000 +0200
+++ new/openQA-5.1781884690.e39cc969/t/24-worker-engine.t       2026-06-19 
17:58:10.000000000 +0200
@@ -25,7 +25,7 @@
 use OpenQA::Test::FakeWorker;
 use Mojo::File qw(path tempdir);
 use Mojo::JSON 'decode_json';
-use OpenQA::Utils qw(testcasedir productdir needledir locate_asset base_host);
+use OpenQA::Utils qw(testcasedir productdir needledir locate_asset base_host 
prjdir);
 use Cwd qw(getcwd);
 use Mojo::Util 'scope_guard';
 use File::Copy::Recursive qw(dircopy);
@@ -580,4 +580,75 @@
     qr|Using cgroup /sys/fs/cgroup/.*/42|, 'use of cgroup logged';
 };
 
+subtest '_construct_isotovideo_cmd' => sub {
+    my $isotovideo = '/usr/bin/isotovideo';
+
+    subtest 'default command' => sub {
+        my $settings = {};
+        my @cmd = 
OpenQA::Worker::Engines::isotovideo::_construct_isotovideo_cmd($settings, 
$isotovideo);
+        is_deeply \@cmd, ['perl', '/usr/bin/isotovideo', '-d'], 'returns 
default execution array';
+    };
+
+    subtest 'custom ISOTOVIDEO override' => sub {
+        my $settings = {ISOTOVIDEO => 'some custom command'};
+        my @cmd = 
OpenQA::Worker::Engines::isotovideo::_construct_isotovideo_cmd($settings, 
$isotovideo);
+        is_deeply \@cmd, ['some custom command'], 'returns custom command 
string';
+    };
+
+    subtest 'containerized with OS_AUTOINST_GIT_REPO' => sub {
+        my $settings = {OS_AUTOINST_GIT_REPO => 
'https://github.com/foo/os-autoinst.git'};
+        my @cmd = 
OpenQA::Worker::Engines::isotovideo::_construct_isotovideo_cmd($settings, 
$isotovideo);
+        is scalar(@cmd), 26, 'returns a list of execution arguments';
+        my $podman_dir = prjdir() . '/cache/podman';
+        is $cmd[0], 'env', 'uses env';
+        is $cmd[1], "HOME=$podman_dir", 'sets correct home directory under 
openqa cache';
+        is $cmd[2], 'podman', 'runs podman';
+        is $cmd[3], '--root', 'uses --root';
+        is $cmd[4], "$podman_dir/data/containers/storage", 'sets correct root 
directory';
+        is $cmd[5], '--runroot', 'uses --runroot';
+        is $cmd[6], "$podman_dir/run/containers", 'sets correct run root 
directory';
+        is $cmd[7], '--storage-opt', 'sets storage-opt';
+        is $cmd[8], 'ignore_chown_errors=true', 'ignores chown errors';
+        is $cmd[9], '--cgroup-manager=cgroupfs', 'uses cgroupfs manager';
+        is $cmd[10], '--events-backend=file', 'uses file events backend';
+        is $cmd[11], 'run', 'runs the container';
+        is $cmd[12], '--init', 'runs with --init for signal handling';
+        is $cmd[13], '--rm', 'runs with --rm for auto-cleanup';
+        is $cmd[14], '--entrypoint', 'specifies entrypoint';
+        is $cmd[15], '', 'clears entrypoint';
+        is $cmd[16], '--device', 'adds device';
+        is $cmd[17], '/dev/kvm', 'passes kvm';
+        is $cmd[18], '-v', 'mounts a volume';
+        is $cmd[19], getcwd() . ':/pool', 'mounts getcwd() to pool';
+        is $cmd[20], '-w', 'sets working dir flag';
+        is $cmd[21], '/pool', 'sets working dir';
+        is $cmd[22], 
'registry.opensuse.org/devel/openqa/containers/os-autoinst_dev:latest', 'uses 
default image';
+        is $cmd[23], 'sh', 'executes sh';
+        is $cmd[24], '-c', 'runs shell command';
+        like $cmd[25], qr/git clone --branch=master --depth=1 
https:\/\/github.com\/foo\/os-autoinst\.git/,
+          'clones master branch by default';
+    };
+
+    subtest 'containerized with OS_AUTOINST_GIT_BRANCH and custom image' => 
sub {
+        my $settings = {
+            OS_AUTOINST_GIT_REPO => 'https://github.com/foo/os-autoinst.git',
+            OS_AUTOINST_GIT_BRANCH => 'my_feature',
+            OS_AUTOINST_CONTAINER_IMAGE => 'my_custom_image:latest'
+        };
+        my @cmd = 
OpenQA::Worker::Engines::isotovideo::_construct_isotovideo_cmd($settings, 
$isotovideo);
+        is scalar(@cmd), 26, 'returns a list of execution arguments';
+        is $cmd[22], 'my_custom_image:latest', 'uses custom image';
+        like $cmd[25], qr/git clone --branch=my_feature --depth=1 
https:\/\/github.com\/foo\/os-autoinst\.git/,
+          'clones custom branch';
+    };
+
+    subtest 'containerized without OS_AUTOINST_GIT_REPO' => sub {
+        my $settings = {OS_AUTOINST_CONTAINER_IMAGE => 
'my_custom_image:latest'};
+        throws_ok {
+            
OpenQA::Worker::Engines::isotovideo::_construct_isotovideo_cmd($settings, 
$isotovideo);
+        }
+        qr/OS_AUTOINST_CONTAINER_IMAGE requires OS_AUTOINST_GIT_REPO/, 'dies 
with helpful error message';
+    };
+};
+
 done_testing();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-5.1781832185.5ddf5343/t/60-archive-download.t 
new/openQA-5.1781884690.e39cc969/t/60-archive-download.t
--- old/openQA-5.1781832185.5ddf5343/t/60-archive-download.t    2026-06-19 
03:23:05.000000000 +0200
+++ new/openQA-5.1781884690.e39cc969/t/60-archive-download.t    2026-06-19 
17:58:10.000000000 +0200
@@ -34,7 +34,7 @@
 my $cache_dir = path($ENV{OPENQA_JOB_DETAILS_ARCHIVE_CACHE_DIR});
 $cache_dir->make_path;
 
-subtest 'Archive download' => sub {
+subtest 'ZIP archive download and categorization' => sub {
     my $job = $schema->resultset('Jobs')->create(
         {
             DISTRI => 'archtest',
@@ -45,47 +45,56 @@
             state => 'done',
             result => 'passed',
         });
-    $job->create_result_dir;
-    $job->update({result_dir => $job->result_dir});
-    my $res_dir_str = $job->result_dir;
-    die 'result_dir is not set' unless $res_dir_str;
-    my $res_dir = path($res_dir_str);
-    $res_dir->make_path;
-    $res_dir->child('details-test.json')->spew('{"test": "data"}');
+    my $res_dir = path($job->create_result_dir);
+    $res_dir->child('vars.json')->spew('{"test": "data"}');
     $res_dir->child('ulogs')->make_path->child('test.log')->spew('log data');
-    my $asset = $schema->resultset('Assets')->create(
-        {
-            type => 'iso',
-            name => 'test.iso',
-        });
-    $schema->resultset('JobsAssets')->create(
-        {
-            job_id => $job->id,
-            asset_id => $asset->id,
-        });
-    my $asset_path = path(assetdir(), 'iso', 'test.iso');
-    $asset_path->dirname->make_path;
-    $asset_path->spew('iso data');
+    my $asset = $schema->resultset('Assets')->create({type => 'iso', name => 
'test.iso'});
+    $schema->resultset('JobsAssets')->create({job_id => $job->id, asset_id => 
$asset->id});
+    path(assetdir(), 'iso', 
'test.iso')->dirname->make_path->child('test.iso')->spew('iso data');
+
     $t->get_ok('/tests/' . $job->id . '/archive')->status_is(302)
       ->header_is('Location' => '/login?return_page=%2Ftests%2F' . $job->id . 
'%2Farchive');
     $t->get_ok('/');
-    ok $case->login($t, 'admin'), 'Logged in as admin';
-    $t->get_ok('/tests/' . $job->id . 
'/archive')->status_is(302)->header_like('Location' => qr|/archives/job_|);
-    my $archive_url = $t->tx->res->headers->location;
-    $t->get_ok('/tests/' . $job->id . '/downloads_ajax')->status_is(200)
-      ->element_exists('a[title="Download all test results and assets as a ZIP 
archive"]');
-    
$t->get_ok($archive_url)->status_is(200)->content_type_is('application/zip')
-      ->header_is('Content-Disposition' => 'attachment; filename=job_' . 
$job->id . '.zip;');
-    my $zip_content = $t->tx->res->body;
-    my $zip_file = $tmp->child('downloaded.zip');
-    $zip_file->spew($zip_content);
-    my $zip = Archive::Zip->new();
-    is $zip->read($zip_file->to_string), AZ_OK, 'Zip is valid';
-    ok $zip->memberNamed('testresults/details-test.json'), 'Contains test 
results';
-    ok $zip->memberNamed('testresults/ulogs/test.log'), 'Contains ulogs';
-    ok $zip->memberNamed('iso/test.iso'), 'Contains assets';
-    is $zip->contents('testresults/details-test.json'), '{"test": "data"}', 
'Result content is correct';
-    is $zip->contents('iso/test.iso'), 'iso data', 'Asset content is correct';
+    ok $case->login($t, 'admin'), 'Admin login succeeds';
+
+    my @test_cases = (
+        {
+            category => 'all',
+            expected => [qw(testresults/vars.json testresults/ulogs/test.log 
iso/test.iso)],
+        },
+        {
+            category => 'resultfiles',
+            expected => ['testresults/vars.json'],
+            unexpected => [qw(testresults/ulogs/test.log iso/test.iso)],
+        },
+        {
+            category => 'ulogs',
+            expected => ['testresults/ulogs/test.log'],
+            unexpected => [qw(testresults/vars.json iso/test.iso)],
+        },
+        {
+            category => 'assets',
+            expected => ['iso/test.iso'],
+            unexpected => [qw(testresults/vars.json 
testresults/ulogs/test.log)],
+        },
+    );
+
+    for my $tc (@test_cases) {
+        my $cat = $tc->{category};
+        my $url = '/tests/' . $job->id . '/archive' . ($cat eq 'all' ? '' : 
"?category=$cat");
+        my $suffix = $cat eq 'all' ? '' : "_$cat";
+
+        $t->get_ok($url)->status_is(302)->header_like('Location' => 
qr|/archives/job_@{[ $job->id ]}${suffix}\.zip|);
+        
$t->get_ok($t->tx->res->headers->location)->status_is(200)->content_type_is('application/zip');
+
+        my $zip = Archive::Zip->new();
+        my $content = $t->tx->res->body;
+        open my $fh, '<', \$content;
+        is $zip->read($fh), AZ_OK, "ZIP archive for category '$cat' is 
structurally valid";
+
+        ok $zip->memberNamed($_), "Category '$cat' archive correctly includes: 
$_" for @{$tc->{expected}};
+        ok !$zip->memberNamed($_), "Category '$cat' archive correctly 
excludes: $_" for @{$tc->{unexpected} // []};
+    }
 };
 
 subtest 'Archive with large files' => sub {
@@ -101,32 +110,29 @@
         });
     my $res_dir = path($job->create_result_dir);
     my $large_file = $res_dir->child('large.bin');
-    # 50MB is enough to test without taking too long
     my $fh = $large_file->open('>');
-    for (1 .. 50 * 1024) {
-        print $fh 'A' x 1024;
-    }
+    print $fh 'A' x 1024 for 1 .. 50 * 1024;
     $fh->close;
 
     my $archive_path = OpenQA::Archive::create_job_archive($job);
-    ok -e $archive_path, 'Archive with large file created';
+    ok -e $archive_path, 'Archive with 50MB file created successfully';
     my $zip = Archive::Zip->new();
-    is $zip->read($archive_path->to_string), AZ_OK, 'Large zip is valid';
+    is $zip->read($archive_path->to_string), AZ_OK, 'Large ZIP archive is 
readable';
     my $member = $zip->memberNamed('testresults/large.bin');
-    ok $member, 'Contains large file';
-    is $member->uncompressedSize, 50 * 1024 * 1024, 'Size is correct';
+    ok $member, 'ZIP archive contains the large file';
+    is $member->uncompressedSize, 50 * 1024 * 1024, 'Archived file size 
matches expected 50MB';
 };
 
 subtest 'Archive caching' => sub {
     my $job_id = $schema->resultset('Jobs')->first->id;
     my $cache_file = 
path($ENV{OPENQA_JOB_DETAILS_ARCHIVE_CACHE_DIR})->child("job_$job_id.zip");
-    ok -e $cache_file, 'Archive is cached';
+    ok -e $cache_file, 'Initial archive exists in cache';
     my $mtime = $cache_file->stat->mtime;
     utime $mtime - 10, $mtime - 10, $cache_file->to_string;
     $mtime = $cache_file->stat->mtime;
     $t->get_ok('/tests/' . $job_id . '/archive')->status_is(302);
     $t->get_ok($t->tx->res->headers->location)->status_is(200);
-    is $cache_file->stat->mtime, $mtime, 'Cached file was reused';
+    is $cache_file->stat->mtime, $mtime, 'Archive request reuses existing 
cached file without regeneration';
 };
 
 subtest 'Hide "Download All" button when no content' => sub {
@@ -155,17 +161,20 @@
     my $orig_env = $ENV{OPENQA_JOB_DETAILS_ARCHIVE_CACHE_DIR};
     delete $ENV{OPENQA_JOB_DETAILS_ARCHIVE_CACHE_DIR};
     is OpenQA::Archive::archive_cache_dir(), 
$app->config->{job_details_archive}->{job_details_archive_cache_dir},
-      'Cache dir from config';
-    is OpenQA::Archive::get_cache_limit(), 1024 * 1024 * 1024, 'Limit in 
bytes';
-    is OpenQA::Archive::get_min_free_percentage(), 20, 'Min free percentage';
-    is OpenQA::Archive::get_watermark_percentage(), 50, 'Watermark percentage';
-    ok OpenQA::Archive::is_cache_limit_exceeded(2 * 1024 * 1024 * 1024, 100, 
1000), 'Limit exceeded by size';
-    ok OpenQA::Archive::is_cache_limit_exceeded(100, 10, 100), 'Limit exceeded 
by free percentage';
-    ok !OpenQA::Archive::is_cache_limit_exceeded(100, 30, 100), 'Limit not 
exceeded';
+      'Archive cache directory follows application config';
+    is OpenQA::Archive::get_cache_limit(), 1024 * 1024 * 1024, 'Cache size 
limit is correctly converted to bytes';
+    is OpenQA::Archive::get_min_free_percentage(), 20, 'Minimum free space 
percentage follows config';
+    is OpenQA::Archive::get_watermark_percentage(), 50, 'Cleanup watermark 
percentage follows config';
+    ok OpenQA::Archive::is_cache_limit_exceeded(2 * 1024 * 1024 * 1024, 100, 
1000),
+      'Cache limit is exceeded when current size is over threshold';
+    ok OpenQA::Archive::is_cache_limit_exceeded(100, 10, 100),
+      'Cache limit is exceeded when free disk space is below minimum';
+    ok !OpenQA::Archive::is_cache_limit_exceeded(100, 30, 100),
+      'Cache limit is not exceeded when within safety thresholds';
     delete 
$app->config->{job_details_archive}->{job_details_archive_cache_dir};
-    like OpenQA::Archive::archive_cache_dir(), qr|/webui/cache/archives$|, 
'Default cache dir';
+    like OpenQA::Archive::archive_cache_dir(), qr|/webui/cache/archives$|, 
'Fallback to default cache directory works';
     delete $app->config->{job_details_archive};
-    is OpenQA::Archive::get_cache_limit(), 5 * 1024 * 1024 * 1024, 'Default 
limit';
+    is OpenQA::Archive::get_cache_limit(), 5 * 1024 * 1024 * 1024, 'Default 
cache limit is 5GB';
     $ENV{OPENQA_JOB_DETAILS_ARCHIVE_CACHE_DIR} = $orig_env;
     $app->config->{job_details_archive} = $orig_config;
 };
@@ -188,7 +197,7 @@
     my @initial = $cache_dir->list->grep(sub { $_->basename =~ 
/^job_\d+\.zip$/ })->each;
     OpenQA::Archive::cleanup_cache();
     my @remaining = $cache_dir->list->grep(sub { $_->basename =~ 
/^job_\d+\.zip$/ })->each;
-    ok scalar(@remaining) < scalar(@initial), 'Some archives were removed 
during cleanup';
+    ok scalar(@remaining) < scalar(@initial), 'Archive cache is rotated after 
exceeding limit';
     $app->config->{job_details_archive} = $orig_config;
 };
 
@@ -210,7 +219,8 @@
     $mock_assets_rs->mock(next => sub { shift @assets });
     $mock_job->set_always(jobs_assets => $mock_assets_rs);
     my $archive_path = OpenQA::Archive::create_job_archive($mock_job);
-    ok -e $archive_path, 'Archive created with directory asset';
+    ok -e $archive_path, 'Archive is created correctly when an asset is a 
directory';
+
     my $mock_asset_missing = Test::MockObject->new;
     $mock_asset_missing->set_always(disk_file => 
$tmp->child('nonexistent_asset')->to_string);
     $mock_asset_missing->set_always(name => 'missing_asset');
@@ -221,9 +231,10 @@
     $mock_assets_rs->mock(next => sub { shift @assets_missing });
     $mock_job->set_always(id => 7890);
     my $archive_path_missing = OpenQA::Archive::create_job_archive($mock_job);
-    ok -e $archive_path_missing, 'Archive created even with missing asset 
file';
+    ok -e $archive_path_missing, 'Archive creation succeeds even if an asset 
file is missing from disk';
     my $archive_path2 = OpenQA::Archive::create_job_archive($mock_job);
-    is $archive_path2->to_string, $archive_path_missing->to_string, 'Returned 
existing archive';
+    is $archive_path2->to_string, $archive_path_missing->to_string,
+      'Archive request returns existing file if already present in cache';
 };
 
 subtest 'Create archive failure' => sub {
@@ -238,33 +249,33 @@
             path($file)->spew('dummy');
             return AZ_IO_ERROR;
         });
-    throws_ok { OpenQA::Archive::create_job_archive($mock_job) } qr/Failed to 
create archive/, 'Throws on zip failure';
+    throws_ok { OpenQA::Archive::create_job_archive($mock_job) } qr/Failed to 
create archive/,
+      'Archive creation throws error on ZIP library failure';
 };
 
-subtest 'CreateZipArchive task' => sub {
+subtest 'CreateZipArchive Minion task' => sub {
     require OpenQA::Task::Job::CreateZipArchive;
-    my $mock_minion_job = Test::MockObject->new;
-    $mock_minion_job->set_always(app => $app);
+    my $mock_minion_job = Test::MockObject->new->set_always(app => 
$app)->set_true('finish')->set_true('fail');
     my $mock_schema_obj = Test::MockObject->new;
     my $mock_rs = Test::MockObject->new;
     $mock_schema_obj->set_always(resultset => $mock_rs);
     $mock_rs->set_always(find => undef);
+
     my $mock_app_module = Test::MockModule->new('OpenQA::WebAPI');
     $mock_app_module->mock(schema => sub { $mock_schema_obj });
-    $mock_minion_job->set_true('finish');
     OpenQA::Task::Job::CreateZipArchive::_create_zip_archive($mock_minion_job, 
4567);
-    $mock_minion_job->called_ok('finish', 'Finished with job not found 
message');
-    my $mock_job = Test::MockObject->new;
-    $mock_job->set_always(id => 4567);
+    $mock_minion_job->called_ok('finish', 'Task finishes gracefully if job is 
not found in database');
+
+    my $mock_job = Test::MockObject->new->set_always(id => 4567);
     $mock_rs->set_always(find => $mock_job);
     my $mock_archive_module = Test::MockModule->new('OpenQA::Archive');
     $mock_archive_module->mock(create_job_archive => sub { 
path('/tmp/dummy.zip') });
     OpenQA::Task::Job::CreateZipArchive::_create_zip_archive($mock_minion_job, 
4567);
-    $mock_minion_job->called_ok('finish', 'Finished successfully');
+    $mock_minion_job->called_ok('finish', 'Task completes successfully after 
archive generation');
+
     $mock_archive_module->mock(create_job_archive => sub { die 'creation 
error' });
-    $mock_minion_job->set_true('fail');
     OpenQA::Task::Job::CreateZipArchive::_create_zip_archive($mock_minion_job, 
4567);
-    $mock_minion_job->called_ok('fail', 'Failed correctly on error');
+    $mock_minion_job->called_ok('fail', 'Task fails correctly when archive 
generation logic throws an exception');
 
     # Verify create_zip_archive_limit config is respected
     my $mock_minion = Test::MockModule->new('Minion');
@@ -296,7 +307,7 @@
     $mock_minion->unmock_all;
 };
 
-subtest 'Controller extra tests' => sub {
+subtest 'Controller archive endpoint' => sub {
     my $job = $schema->resultset('Jobs')->create(
         {
             DISTRI => 'archtest',
@@ -309,7 +320,8 @@
         });
     $t->get_ok('/archives/..%2fetc%2fpasswd')->status_is(404);
     $t->get_ok('/archives/nonexistent.zip')->status_is(404);
-    my $mock_minion = Test::MockObject->new;
+
+    my $mock_minion = Test::MockObject->new->set_true('enqueue');
     my $mock_app_module = Test::MockModule->new('OpenQA::WebAPI');
     $mock_app_module->mock(minion => sub { $mock_minion });
     my $orig_can = $app->can('can');
@@ -321,15 +333,71 @@
     $mock_minion->set_true('enqueue');
 
     $case->login($t, 'admin');
-
     $t->get_ok('/tests/' . $job->id . 
'/archive')->status_is(200)->content_like(qr/Preparing Archive for Job/);
-    $mock_minion->called_ok('enqueue', 'Minion job enqueued');
+    $mock_minion->called_ok('enqueue', 'Archive generation is enqueued in 
Minion');
     $mock_minion->clear;
     $t->get_ok('/tests/' . $job->id . '/archive')->status_is(200);
-    $mock_minion->called_ok('enqueue', 'Minion job enqueued again');
+    $mock_minion->called_ok('enqueue', 'Repeated archive request re-enqueues 
generation task');
 
     $mock_minion->mock(enqueue => sub { die 'Enqueue failed' });
     $t->get_ok('/tests/' . $job->id . 
'/archive')->status_is(500)->content_is('Internal Server Error');
 };
 
+subtest 'Category specific archives' => sub {
+    my $job = $schema->resultset('Jobs')->create(
+        {
+            DISTRI => 'archtest',
+            VERSION => '1.0',
+            FLAVOR => 'test',
+            ARCH => 'x86_64',
+            TEST => 'category_test',
+            state => 'done',
+            result => 'passed',
+        });
+    my $res_dir = path($job->create_result_dir);
+    $res_dir->child('vars.json')->spew('{}');    # COMMON_RESULT_FILES 
includes vars.json
+    $res_dir->child('ulogs')->make_path->child('ulog.txt')->spew('ulog data');
+    my $asset = $schema->resultset('Assets')->create({type => 'iso', name => 
'asset.iso'});
+    $schema->resultset('JobsAssets')->create({job_id => $job->id, asset_id => 
$asset->id});
+    path(assetdir(), 'iso', 
'asset.iso')->dirname->make_path->child('asset.iso')->spew('asset data');
+
+    $case->login($t, 'admin');
+
+    # Test resultfiles category
+    $t->get_ok('/tests/' . $job->id . 
'/archive?category=resultfiles')->status_is(302)
+      ->header_like('Location' => qr|/archives/job_\d+_resultfiles.zip|);
+    $t->get_ok($t->tx->res->headers->location)->status_is(200);
+    my $zip_file = $tmp->child('resultfiles.zip');
+    $zip_file->spew($t->tx->res->body);
+    my $zip = Archive::Zip->new();
+    is $zip->read($zip_file->to_string), AZ_OK, 'Resultfiles zip is valid';
+    ok $zip->memberNamed('testresults/vars.json'), 'Contains result file';
+    ok !$zip->memberNamed('testresults/ulogs/ulog.txt'), 'Does not contain 
ulog';
+    ok !$zip->memberNamed('iso/asset.iso'), 'Does not contain asset';
+
+    # Test ulogs category
+    $t->get_ok('/tests/' . $job->id . 
'/archive?category=ulogs')->status_is(302)
+      ->header_like('Location' => qr|/archives/job_\d+_ulogs.zip|);
+    $t->get_ok($t->tx->res->headers->location)->status_is(200);
+    $zip_file = $tmp->child('ulogs.zip');
+    $zip_file->spew($t->tx->res->body);
+    $zip = Archive::Zip->new();
+    is $zip->read($zip_file->to_string), AZ_OK, 'Ulogs zip is valid';
+    ok !$zip->memberNamed('testresults/vars.json'), 'Does not contain result 
file';
+    ok $zip->memberNamed('testresults/ulogs/ulog.txt'), 'Contains ulog';
+    ok !$zip->memberNamed('iso/asset.iso'), 'Does not contain asset';
+
+    # Test assets category
+    $t->get_ok('/tests/' . $job->id . 
'/archive?category=assets')->status_is(302)
+      ->header_like('Location' => qr|/archives/job_\d+_assets.zip|);
+    $t->get_ok($t->tx->res->headers->location)->status_is(200);
+    $zip_file = $tmp->child('assets.zip');
+    $zip_file->spew($t->tx->res->body);
+    $zip = Archive::Zip->new();
+    is $zip->read($zip_file->to_string), AZ_OK, 'Assets zip is valid';
+    ok !$zip->memberNamed('testresults/vars.json'), 'Does not contain result 
file';
+    ok !$zip->memberNamed('testresults/ulogs/ulog.txt'), 'Does not contain 
ulog';
+    ok $zip->memberNamed('iso/asset.iso'), 'Contains asset';
+};
+
 done_testing;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-5.1781832185.5ddf5343/t/data/workers.ini 
new/openQA-5.1781884690.e39cc969/t/data/workers.ini
--- old/openQA-5.1781832185.5ddf5343/t/data/workers.ini 2026-06-19 
03:23:05.000000000 +0200
+++ new/openQA-5.1781884690.e39cc969/t/data/workers.ini 2026-06-19 
17:58:10.000000000 +0200
@@ -7,6 +7,6 @@
 # the value set to 0 prevents jobs from being blocked in ci
 CRITICAL_LOAD_AVG_THRESHOLD = 0
 
-[1-99]
+[1-1024]
 WORKER_CLASS = qemu_i386,qemu_x86_64
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-5.1781832185.5ddf5343/t/full-stack.t 
new/openQA-5.1781884690.e39cc969/t/full-stack.t
--- old/openQA-5.1781832185.5ddf5343/t/full-stack.t     2026-06-19 
03:23:05.000000000 +0200
+++ new/openQA-5.1781884690.e39cc969/t/full-stack.t     2026-06-19 
17:58:10.000000000 +0200
@@ -304,6 +304,7 @@
 my $cache_location = path($ENV{OPENQA_BASEDIR}, 'cache')->make_path;
 ok -e $cache_location, 'Setting up Cache directory';
 
+my $max_instances = MAX_WORKER_INSTANCES;
 path($ENV{OPENQA_CONFIG})->child('workers.ini')->spew(<<"EOC");
 [global]
 CACHEDIRECTORY = $cache_location
@@ -313,7 +314,7 @@
 # Ensure fullstack tests run even under high load.
 CRITICAL_LOAD_AVG_THRESHOLD = 0
 
-[1-99]
+[1-$max_instances]
 WORKER_CLASS = qemu_i386,qemu_x86_64
 
 [http://localhost:$mojoport]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-5.1781832185.5ddf5343/t/lib/OpenQA/Test/Utils.pm 
new/openQA-5.1781884690.e39cc969/t/lib/OpenQA/Test/Utils.pm
--- old/openQA-5.1781832185.5ddf5343/t/lib/OpenQA/Test/Utils.pm 2026-06-19 
03:23:05.000000000 +0200
+++ new/openQA-5.1781884690.e39cc969/t/lib/OpenQA/Test/Utils.pm 2026-06-19 
17:58:10.000000000 +0200
@@ -38,7 +38,11 @@
 use Feature::Compat::Try;
 use Time::HiRes 'sleep';
 
-use constant MAX_WORKER_INSTANCES => 64;
+use constant MAX_WORKER_INSTANCES => 1024;
+# That is the highest power of two that still leaves os-autoinst enough 
headroom
+# to compute port numbers. This means there is only a < 0.1 % chance of running
+# into a conflict when running two fullstack tests in parallel and we still 
avoid
+# exceeding the maximum port number.
 
 BEGIN {
     if (!$ENV{MOJO_HOME}) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-5.1781832185.5ddf5343/templates/webapi/test/downloads.html.ep 
new/openQA-5.1781884690.e39cc969/templates/webapi/test/downloads.html.ep
--- old/openQA-5.1781832185.5ddf5343/templates/webapi/test/downloads.html.ep    
2026-06-19 03:23:05.000000000 +0200
+++ new/openQA-5.1781884690.e39cc969/templates/webapi/test/downloads.html.ep    
2026-06-19 17:58:10.000000000 +0200
@@ -8,11 +8,23 @@
 % }
 
 % if(@$resultfiles) {
-    <h5>Result files</h5>
+    <h5>Result files
+        % if (current_user) {
+            %= link_to url_for('test_archive', testid => 
$job->id)->query(category => 'resultfiles') => (class => 'btn 
btn-outline-primary btn-sm ms-2', title => 'Download result files as a ZIP 
archive', rel => 'nofollow') => begin
+                <i class="fa fa-download"></i> Download (ZIP)
+            % end
+        % }
+    </h5>
     %= include 'test/result_file_list', resultfiles => $resultfiles, 
is_userfile => 0
 % }
 % if (@$ulogs) {
-    <h6>Uploaded logs</h6>
+    <h6>Uploaded logs
+        % if (current_user) {
+            %= link_to url_for('test_archive', testid => 
$job->id)->query(category => 'ulogs') => (class => 'btn btn-outline-primary 
btn-sm ms-2', title => 'Download uploaded logs as a ZIP archive', rel => 
'nofollow') => begin
+                <i class="fa fa-download"></i> Download (ZIP)
+            % end
+        % }
+    </h6>
     %= include 'test/result_file_list', resultfiles => $ulogs, is_userfile => 1
 % }
 
@@ -38,6 +50,12 @@
     % }
 % }
 % if (length(content('asset_box'))) {
-    <h5>Assets</h5>
+    <h5>Assets
+        % if (current_user) {
+            %= link_to url_for('test_archive', testid => 
$job->id)->query(category => 'assets') => (class => 'btn btn-outline-primary 
btn-sm ms-2', title => 'Download assets as a ZIP archive', rel => 'nofollow') 
=> begin
+                <i class="fa fa-download"></i> Download (ZIP)
+            % end
+        % }
+    </h5>
     <ul id="asset-list"><%= content 'asset_box' %></ul>
 % }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-5.1781832185.5ddf5343/tools/ci/autoinst.sha 
new/openQA-5.1781884690.e39cc969/tools/ci/autoinst.sha
--- old/openQA-5.1781832185.5ddf5343/tools/ci/autoinst.sha      2026-06-19 
03:23:05.000000000 +0200
+++ new/openQA-5.1781884690.e39cc969/tools/ci/autoinst.sha      2026-06-19 
17:58:10.000000000 +0200
@@ -1 +1 @@
-22ced0db810357e853386e7dc6aed23ce4ed5b68
\ No newline at end of file
+0fb10771665c8db2173a5097c1dbdc837464c9d7
\ No newline at end of file

++++++ openQA.obsinfo ++++++
--- /var/tmp/diff_new_pack.vHObut/_old  2026-06-22 17:28:31.119821388 +0200
+++ /var/tmp/diff_new_pack.vHObut/_new  2026-06-22 17:28:31.135821946 +0200
@@ -1,5 +1,5 @@
 name: openQA
-version: 5.1781832185.5ddf5343
-mtime: 1781832185
-commit: 5ddf53437214d6869d82cdbbf990149b6b4da672
+version: 5.1781884690.e39cc969
+mtime: 1781884690
+commit: e39cc96968d08eb9d9691acd620da8ac8a483ccb
 

Reply via email to