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-02-05 17:59:27 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/openQA (Old) and /work/SRC/openSUSE:Factory/.openQA.new.1670 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "openQA" Thu Feb 5 17:59:27 2026 rev:803 rq:1331112 version:5.1769644379.ef069e9d Changes: -------- --- /work/SRC/openSUSE:Factory/openQA/openQA.changes 2026-01-29 17:48:18.317876959 +0100 +++ /work/SRC/openSUSE:Factory/.openQA.new.1670/openQA.changes 2026-02-05 18:02:23.164605392 +0100 @@ -2 +2 @@ -Wed Jan 28 15:07:49 UTC 2026 - [email protected] +Thu Jan 29 06:10:03 UTC 2026 - [email protected] @@ -4 +4,20 @@ -- Update to version 5.1769603414.6c0fa72e: +- Update to version 5.1769644379.ef069e9d: + * style: Add quotes in openqa-bootstrap + * feat: default API key expiration to 1 year, aligning with UI + * feat: wrap array in an object in api_key API responses + * feat: add API endpoint for deleting API keys + * feat: add API endpoint for listing API keys + * feat: add API endpoint for creating API keys + * fix(openqa-bootstrap): prevent shellcheck warning SC2086 + * Add dependency on 'file' + * refactor: Write code in `JobGroup.pm` in a more compact way + * test: Consider `Job.pm` fully covered + * test: Add tests for error handling of artefact upload + * refactor: Format artefact upload test in a more compact way + * test: Add tests for using assigned worker on job status updates + * test: Add tests for re-scheduling invalid scheduled product + * test: Add tests for querying non-existent scheduled product + * refactor: Use more compact coding style in `show_scheduled_product` + * refactor: Improve `Mm.pm` + * test: Improve tests of multi-machine API + * Remove unused module Config::Tiny from dependencies Old: ---- openQA-5.1769603414.6c0fa72e.obscpio New: ---- openQA-5.1769644379.ef069e9d.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ openQA-client-test.spec ++++++ --- /var/tmp/diff_new_pack.U2tDce/_old 2026-02-05 18:02:26.728754903 +0100 +++ /var/tmp/diff_new_pack.U2tDce/_new 2026-02-05 18:02:26.744755574 +0100 @@ -18,7 +18,7 @@ %define short_name openQA-client Name: %{short_name}-test -Version: 5.1769603414.6c0fa72e +Version: 5.1769644379.ef069e9d Release: 0 Summary: Test package for %{short_name} License: GPL-2.0-or-later ++++++ openQA-devel-test.spec ++++++ --- /var/tmp/diff_new_pack.U2tDce/_old 2026-02-05 18:02:27.032767656 +0100 +++ /var/tmp/diff_new_pack.U2tDce/_new 2026-02-05 18:02:27.048768327 +0100 @@ -18,7 +18,7 @@ %define short_name openQA-devel Name: %{short_name}-test -Version: 5.1769603414.6c0fa72e +Version: 5.1769644379.ef069e9d Release: 0 Summary: Test package for %{short_name} License: GPL-2.0-or-later ++++++ openQA-test.spec ++++++ --- /var/tmp/diff_new_pack.U2tDce/_old 2026-02-05 18:02:27.388782590 +0100 +++ /var/tmp/diff_new_pack.U2tDce/_new 2026-02-05 18:02:27.408783429 +0100 @@ -18,7 +18,7 @@ %define short_name openQA Name: %{short_name}-test -Version: 5.1769603414.6c0fa72e +Version: 5.1769644379.ef069e9d Release: 0 Summary: Test package for openQA License: GPL-2.0-or-later ++++++ openQA-worker-test.spec ++++++ --- /var/tmp/diff_new_pack.U2tDce/_old 2026-02-05 18:02:27.660794000 +0100 +++ /var/tmp/diff_new_pack.U2tDce/_new 2026-02-05 18:02:27.688795175 +0100 @@ -18,7 +18,7 @@ %define short_name openQA-worker Name: %{short_name}-test -Version: 5.1769603414.6c0fa72e +Version: 5.1769644379.ef069e9d Release: 0 Summary: Test package for %{short_name} License: GPL-2.0-or-later ++++++ openQA.spec ++++++ --- /var/tmp/diff_new_pack.U2tDce/_old 2026-02-05 18:02:28.104812626 +0100 +++ /var/tmp/diff_new_pack.U2tDce/_new 2026-02-05 18:02:28.136813969 +0100 @@ -59,10 +59,10 @@ # The following line is generated from dependencies.yaml %define assetpack_requires perl(CSS::Minifier::XS) >= 0.01 perl(JavaScript::Minifier::XS) >= 0.11 perl(Mojolicious) perl(Mojolicious::Plugin::AssetPack) >= 1.36 perl(YAML::PP) >= 0.026 # The following line is generated from dependencies.yaml -%define common_requires ntp-daemon perl >= 5.20.0 perl(Carp::Always) >= 0.14.02 perl(Config::IniFiles) perl(Config::Tiny) perl(Cpanel::JSON::XS) >= 4.09 perl(Cwd) perl(Data::Dump) perl(Data::Dumper) perl(Digest::MD5) perl(Feature::Compat::Try) perl(Filesys::Df) perl(Getopt::Long) perl(Minion) >= 10.25 perl(Mojolicious) >= 9.340.0 perl(Regexp::Common) perl(Storable) perl(Text::Glob) perl(Time::Moment) +%define common_requires ntp-daemon perl >= 5.20.0 perl(Carp::Always) >= 0.14.02 perl(Config::IniFiles) perl(Cpanel::JSON::XS) >= 4.09 perl(Cwd) perl(Data::Dump) perl(Data::Dumper) perl(Digest::MD5) perl(Feature::Compat::Try) perl(Filesys::Df) perl(Getopt::Long) perl(Minion) >= 10.25 perl(Mojolicious) >= 9.340.0 perl(Regexp::Common) perl(Storable) perl(Text::Glob) perl(Time::Moment) # runtime requirements for the main package that are not required by other sub-packages # The following line is generated from dependencies.yaml -%define main_requires %assetpack_requires bsdtar git-core hostname openssh-clients perl(BSD::Resource) perl(Carp) perl(CommonMark) perl(Config::Tiny) perl(DBD::Pg) >= 3.7.4 perl(DBI) >= 1.632 perl(DBIx::Class) >= 0.082801 perl(DBIx::Class::DeploymentHandler) perl(DBIx::Class::DynamicDefault) perl(DBIx::Class::OptimisticLocking) perl(DBIx::Class::ResultClass::HashRefInflator) perl(DBIx::Class::Schema::Config) perl(DBIx::Class::Storage::Statistics) perl(Date::Format) perl(DateTime) perl(DateTime::Duration) perl(DateTime::Format::Pg) perl(Exporter) perl(Fcntl) perl(File::Basename) perl(File::Copy) perl(File::Copy::Recursive) perl(File::Path) perl(File::Spec) perl(FindBin) perl(Getopt::Long::Descriptive) perl(IO::Handle) perl(IPC::Run) perl(JSON::Validator) perl(LWP::UserAgent) perl(Module::Load::Conditional) perl(Module::Pluggable) perl(Mojo::Base) perl(Mojo::ByteStream) perl(Mojo::IOLoop) perl(Mojo::JSON) perl(Mojo::Pg) perl(Mojo::RabbitMQ::Client) >= 0.2 perl(Mojo::URL) perl(Mojo::Ut il) perl(Mojolicious::Commands) perl(Mojolicious::Plugin) perl(Mojolicious::Plugin::OAuth2) perl(Mojolicious::Static) perl(Net::OpenID::Consumer) perl(POSIX) perl(Pod::POM) perl(SQL::Translator) perl(Scalar::Util) perl(Sort::Versions) perl(Text::Diff) perl(Time::HiRes) perl(Time::ParseDate) perl(Time::Piece) perl(Time::Seconds) perl(URI::Escape) perl(YAML::PP) >= 0.026 perl(YAML::XS) perl(aliased) perl(base) perl(constant) perl(diagnostics) perl(strict) perl(warnings) +%define main_requires %assetpack_requires bsdtar git-core hostname openssh-clients perl(BSD::Resource) perl(Carp) perl(CommonMark) perl(DBD::Pg) >= 3.7.4 perl(DBI) >= 1.632 perl(DBIx::Class) >= 0.082801 perl(DBIx::Class::DeploymentHandler) perl(DBIx::Class::DynamicDefault) perl(DBIx::Class::OptimisticLocking) perl(DBIx::Class::ResultClass::HashRefInflator) perl(DBIx::Class::Schema::Config) perl(DBIx::Class::Storage::Statistics) perl(Date::Format) perl(DateTime) perl(DateTime::Duration) perl(DateTime::Format::Pg) perl(Exporter) perl(Fcntl) perl(File::Basename) perl(File::Copy) perl(File::Copy::Recursive) perl(File::Path) perl(File::Spec) perl(FindBin) perl(Getopt::Long::Descriptive) perl(IO::Handle) perl(IPC::Run) perl(JSON::Validator) perl(LWP::UserAgent) perl(Module::Load::Conditional) perl(Module::Pluggable) perl(Mojo::Base) perl(Mojo::ByteStream) perl(Mojo::IOLoop) perl(Mojo::JSON) perl(Mojo::Pg) perl(Mojo::RabbitMQ::Client) >= 0.2 perl(Mojo::URL) perl(Mojo::Util) perl(Mojoliciou s::Commands) perl(Mojolicious::Plugin) perl(Mojolicious::Plugin::OAuth2) perl(Mojolicious::Static) perl(Net::OpenID::Consumer) perl(POSIX) perl(Pod::POM) perl(SQL::Translator) perl(Scalar::Util) perl(Sort::Versions) perl(Text::Diff) perl(Time::HiRes) perl(Time::ParseDate) perl(Time::Piece) perl(Time::Seconds) perl(URI::Escape) perl(YAML::PP) >= 0.026 perl(YAML::XS) perl(aliased) perl(base) perl(constant) perl(diagnostics) perl(strict) perl(warnings) # The following line is generated from dependencies.yaml %define client_requires curl git-core jq perl(Getopt::Long::Descriptive) perl(IO::Socket::SSL) >= 2.009 perl(IPC::Run) perl(JSON::Validator) perl(LWP::Protocol::https) perl(LWP::UserAgent) perl(Test::More) perl(YAML::PP) >= 0.020 perl(YAML::XS) # The following line is generated from dependencies.yaml @@ -83,7 +83,7 @@ # Do not require on this in individual sub-packages except for the devel # package. # The following line is generated from dependencies.yaml -%define test_requires %common_requires %main_requires %mcp_requires %python_scripts_requires %worker_requires curl jq openssh-common os-autoinst perl(App::cpanminus) perl(Selenium::Remote::Driver) >= 1.23 perl(Selenium::Remote::WDKeys) perl(Test::Exception) perl(Test::Fatal) perl(Test::MockModule) perl(Test::MockObject) perl(Test::Mojo) perl(Test::Most) perl(Test::Output) perl(Test::Pod) perl(Test::Strict) perl(Test::Warnings) >= 0.029 postgresql-server >= 14 python3-setuptools +%define test_requires %common_requires %main_requires %mcp_requires %python_scripts_requires %worker_requires curl file jq openssh-common os-autoinst perl(App::cpanminus) perl(Selenium::Remote::Driver) >= 1.23 perl(Selenium::Remote::WDKeys) perl(Test::Exception) perl(Test::Fatal) perl(Test::MockModule) perl(Test::MockObject) perl(Test::Mojo) perl(Test::Most) perl(Test::Output) perl(Test::Pod) perl(Test::Strict) perl(Test::Warnings) >= 0.029 postgresql-server >= 14 python3-setuptools %ifarch x86_64 %define qemu qemu qemu-kvm %else @@ -99,7 +99,7 @@ %define devel_requires %devel_no_selenium_requires chromedriver Name: openQA -Version: 5.1769603414.6c0fa72e +Version: 5.1769644379.ef069e9d Release: 0 Summary: The openQA web-frontend, scheduler and tools License: GPL-2.0-or-later ++++++ openQA-5.1769603414.6c0fa72e.obscpio -> openQA-5.1769644379.ef069e9d.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1769603414.6c0fa72e/codecov.yml new/openQA-5.1769644379.ef069e9d/codecov.yml --- old/openQA-5.1769603414.6c0fa72e/codecov.yml 2026-01-28 13:30:14.000000000 +0100 +++ new/openQA-5.1769644379.ef069e9d/codecov.yml 2026-01-29 00:52:59.000000000 +0100 @@ -26,6 +26,10 @@ - lib/OpenQA/WebAPI/Controller/File.pm - lib/OpenQA/WebAPI/Controller/Step.pm - lib/OpenQA/WebAPI/Controller/Test.pm + - lib/OpenQA/WebAPI/Controller/API/V1/Iso.pm + - lib/OpenQA/WebAPI/Controller/API/V1/Job.pm + - lib/OpenQA/WebAPI/Controller/API/V1/JobGroup.pm + - lib/OpenQA/WebAPI/Controller/API/V1/Mm.pm tests: target: 100.0 threshold: 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1769603414.6c0fa72e/cpanfile new/openQA-5.1769644379.ef069e9d/cpanfile --- old/openQA-5.1769603414.6c0fa72e/cpanfile 2026-01-28 13:30:14.000000000 +0100 +++ new/openQA-5.1769644379.ef069e9d/cpanfile 2026-01-29 00:52:59.000000000 +0100 @@ -11,7 +11,6 @@ requires 'Carp::Always', '>= 0.14.02'; requires 'CommonMark'; requires 'Config::IniFiles'; -requires 'Config::Tiny'; requires 'Cpanel::JSON::XS', '>= 4.09'; requires 'Cwd'; requires 'DBD::Pg', '>= 3.007004'; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1769603414.6c0fa72e/dependencies.yaml new/openQA-5.1769644379.ef069e9d/dependencies.yaml --- old/openQA-5.1769603414.6c0fa72e/dependencies.yaml 2026-01-28 13:30:14.000000000 +0100 +++ new/openQA-5.1769644379.ef069e9d/dependencies.yaml 2026-01-29 00:52:59.000000000 +0100 @@ -51,7 +51,6 @@ perl(Storable): perl(Text::Glob): perl(Time::Moment): - perl(Config::Tiny): ntp-daemon: cover_requires: @@ -105,7 +104,6 @@ perl(warnings): perl(BSD::Resource): perl(Carp): - perl(Config::Tiny): perl(CommonMark): perl(Date::Format): perl(DateTime): @@ -190,6 +188,7 @@ '%worker_requires': openssh-common: curl: + file: jq: os-autoinst: postgresql-server: '>= 14' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1769603414.6c0fa72e/dist/rpm/openQA.spec new/openQA-5.1769644379.ef069e9d/dist/rpm/openQA.spec --- old/openQA-5.1769603414.6c0fa72e/dist/rpm/openQA.spec 2026-01-28 13:30:14.000000000 +0100 +++ new/openQA-5.1769644379.ef069e9d/dist/rpm/openQA.spec 2026-01-29 00:52:59.000000000 +0100 @@ -59,10 +59,10 @@ # The following line is generated from dependencies.yaml %define assetpack_requires perl(CSS::Minifier::XS) >= 0.01 perl(JavaScript::Minifier::XS) >= 0.11 perl(Mojolicious) perl(Mojolicious::Plugin::AssetPack) >= 1.36 perl(YAML::PP) >= 0.026 # The following line is generated from dependencies.yaml -%define common_requires ntp-daemon perl >= 5.20.0 perl(Carp::Always) >= 0.14.02 perl(Config::IniFiles) perl(Config::Tiny) perl(Cpanel::JSON::XS) >= 4.09 perl(Cwd) perl(Data::Dump) perl(Data::Dumper) perl(Digest::MD5) perl(Feature::Compat::Try) perl(Filesys::Df) perl(Getopt::Long) perl(Minion) >= 10.25 perl(Mojolicious) >= 9.340.0 perl(Regexp::Common) perl(Storable) perl(Text::Glob) perl(Time::Moment) +%define common_requires ntp-daemon perl >= 5.20.0 perl(Carp::Always) >= 0.14.02 perl(Config::IniFiles) perl(Cpanel::JSON::XS) >= 4.09 perl(Cwd) perl(Data::Dump) perl(Data::Dumper) perl(Digest::MD5) perl(Feature::Compat::Try) perl(Filesys::Df) perl(Getopt::Long) perl(Minion) >= 10.25 perl(Mojolicious) >= 9.340.0 perl(Regexp::Common) perl(Storable) perl(Text::Glob) perl(Time::Moment) # runtime requirements for the main package that are not required by other sub-packages # The following line is generated from dependencies.yaml -%define main_requires %assetpack_requires bsdtar git-core hostname openssh-clients perl(BSD::Resource) perl(Carp) perl(CommonMark) perl(Config::Tiny) perl(DBD::Pg) >= 3.7.4 perl(DBI) >= 1.632 perl(DBIx::Class) >= 0.082801 perl(DBIx::Class::DeploymentHandler) perl(DBIx::Class::DynamicDefault) perl(DBIx::Class::OptimisticLocking) perl(DBIx::Class::ResultClass::HashRefInflator) perl(DBIx::Class::Schema::Config) perl(DBIx::Class::Storage::Statistics) perl(Date::Format) perl(DateTime) perl(DateTime::Duration) perl(DateTime::Format::Pg) perl(Exporter) perl(Fcntl) perl(File::Basename) perl(File::Copy) perl(File::Copy::Recursive) perl(File::Path) perl(File::Spec) perl(FindBin) perl(Getopt::Long::Descriptive) perl(IO::Handle) perl(IPC::Run) perl(JSON::Validator) perl(LWP::UserAgent) perl(Module::Load::Conditional) perl(Module::Pluggable) perl(Mojo::Base) perl(Mojo::ByteStream) perl(Mojo::IOLoop) perl(Mojo::JSON) perl(Mojo::Pg) perl(Mojo::RabbitMQ::Client) >= 0.2 perl(Mojo::URL) perl(Mojo::Ut il) perl(Mojolicious::Commands) perl(Mojolicious::Plugin) perl(Mojolicious::Plugin::OAuth2) perl(Mojolicious::Static) perl(Net::OpenID::Consumer) perl(POSIX) perl(Pod::POM) perl(SQL::Translator) perl(Scalar::Util) perl(Sort::Versions) perl(Text::Diff) perl(Time::HiRes) perl(Time::ParseDate) perl(Time::Piece) perl(Time::Seconds) perl(URI::Escape) perl(YAML::PP) >= 0.026 perl(YAML::XS) perl(aliased) perl(base) perl(constant) perl(diagnostics) perl(strict) perl(warnings) +%define main_requires %assetpack_requires bsdtar git-core hostname openssh-clients perl(BSD::Resource) perl(Carp) perl(CommonMark) perl(DBD::Pg) >= 3.7.4 perl(DBI) >= 1.632 perl(DBIx::Class) >= 0.082801 perl(DBIx::Class::DeploymentHandler) perl(DBIx::Class::DynamicDefault) perl(DBIx::Class::OptimisticLocking) perl(DBIx::Class::ResultClass::HashRefInflator) perl(DBIx::Class::Schema::Config) perl(DBIx::Class::Storage::Statistics) perl(Date::Format) perl(DateTime) perl(DateTime::Duration) perl(DateTime::Format::Pg) perl(Exporter) perl(Fcntl) perl(File::Basename) perl(File::Copy) perl(File::Copy::Recursive) perl(File::Path) perl(File::Spec) perl(FindBin) perl(Getopt::Long::Descriptive) perl(IO::Handle) perl(IPC::Run) perl(JSON::Validator) perl(LWP::UserAgent) perl(Module::Load::Conditional) perl(Module::Pluggable) perl(Mojo::Base) perl(Mojo::ByteStream) perl(Mojo::IOLoop) perl(Mojo::JSON) perl(Mojo::Pg) perl(Mojo::RabbitMQ::Client) >= 0.2 perl(Mojo::URL) perl(Mojo::Util) perl(Mojoliciou s::Commands) perl(Mojolicious::Plugin) perl(Mojolicious::Plugin::OAuth2) perl(Mojolicious::Static) perl(Net::OpenID::Consumer) perl(POSIX) perl(Pod::POM) perl(SQL::Translator) perl(Scalar::Util) perl(Sort::Versions) perl(Text::Diff) perl(Time::HiRes) perl(Time::ParseDate) perl(Time::Piece) perl(Time::Seconds) perl(URI::Escape) perl(YAML::PP) >= 0.026 perl(YAML::XS) perl(aliased) perl(base) perl(constant) perl(diagnostics) perl(strict) perl(warnings) # The following line is generated from dependencies.yaml %define client_requires curl git-core jq perl(Getopt::Long::Descriptive) perl(IO::Socket::SSL) >= 2.009 perl(IPC::Run) perl(JSON::Validator) perl(LWP::Protocol::https) perl(LWP::UserAgent) perl(Test::More) perl(YAML::PP) >= 0.020 perl(YAML::XS) # The following line is generated from dependencies.yaml @@ -83,7 +83,7 @@ # Do not require on this in individual sub-packages except for the devel # package. # The following line is generated from dependencies.yaml -%define test_requires %common_requires %main_requires %mcp_requires %python_scripts_requires %worker_requires curl jq openssh-common os-autoinst perl(App::cpanminus) perl(Selenium::Remote::Driver) >= 1.23 perl(Selenium::Remote::WDKeys) perl(Test::Exception) perl(Test::Fatal) perl(Test::MockModule) perl(Test::MockObject) perl(Test::Mojo) perl(Test::Most) perl(Test::Output) perl(Test::Pod) perl(Test::Strict) perl(Test::Warnings) >= 0.029 postgresql-server >= 14 python3-setuptools +%define test_requires %common_requires %main_requires %mcp_requires %python_scripts_requires %worker_requires curl file jq openssh-common os-autoinst perl(App::cpanminus) perl(Selenium::Remote::Driver) >= 1.23 perl(Selenium::Remote::WDKeys) perl(Test::Exception) perl(Test::Fatal) perl(Test::MockModule) perl(Test::MockObject) perl(Test::Mojo) perl(Test::Most) perl(Test::Output) perl(Test::Pod) perl(Test::Strict) perl(Test::Warnings) >= 0.029 postgresql-server >= 14 python3-setuptools %ifarch x86_64 %define qemu qemu qemu-kvm %else diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1769603414.6c0fa72e/lib/OpenQA/WebAPI/Controller/API/V1/Iso.pm new/openQA-5.1769644379.ef069e9d/lib/OpenQA/WebAPI/Controller/API/V1/Iso.pm --- old/openQA-5.1769603414.6c0fa72e/lib/OpenQA/WebAPI/Controller/API/V1/Iso.pm 2026-01-28 13:30:14.000000000 +0100 +++ new/openQA-5.1769644379.ef069e9d/lib/OpenQA/WebAPI/Controller/API/V1/Iso.pm 2026-01-29 00:52:59.000000000 +0100 @@ -38,21 +38,12 @@ =cut -sub show_scheduled_product { - my ($self) = @_; - +sub show_scheduled_product ($self) { my $scheduled_product_id = $self->param('scheduled_product_id'); my $scheduled_products = $self->app->schema->resultset('ScheduledProducts'); - my $scheduled_product = $scheduled_products->find($scheduled_product_id); - if (!$scheduled_product) { - return $self->render( - json => {error => 'Scheduled product does not exist.'}, - status => 404, - ); - } - + return $self->render(json => {error => 'Scheduled product does not exist.'}, status => 404) + unless my $scheduled_product = $scheduled_products->find($scheduled_product_id); my @args = (include_job_ids => $self->param('include_job_ids')); - $self->render(json => $scheduled_product->to_hash(@args)); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1769603414.6c0fa72e/lib/OpenQA/WebAPI/Controller/API/V1/JobGroup.pm new/openQA-5.1769644379.ef069e9d/lib/OpenQA/WebAPI/Controller/API/V1/JobGroup.pm --- old/openQA-5.1769603414.6c0fa72e/lib/OpenQA/WebAPI/Controller/API/V1/JobGroup.pm 2026-01-28 13:30:14.000000000 +0100 +++ new/openQA-5.1769644379.ef069e9d/lib/OpenQA/WebAPI/Controller/API/V1/JobGroup.pm 2026-01-29 00:52:59.000000000 +0100 @@ -38,9 +38,7 @@ =cut -sub is_parent ($self) { - return $self->req->url->path =~ qr/.*\/parent_groups/; -} +sub is_parent ($self) { $self->req->url->path =~ qr/.*\/parent_groups/ } =over 4 @@ -52,9 +50,7 @@ =cut -sub resultset ($self) { - return $self->schema->resultset($self->is_parent ? 'JobGroupParents' : 'JobGroups'); -} +sub resultset ($self) { $self->schema->resultset($self->is_parent ? 'JobGroupParents' : 'JobGroups') } =over 4 @@ -67,18 +63,10 @@ =cut sub find_group ($self) { - my $group_id = $self->param('group_id'); - if (!defined $group_id) { - $self->render(json => {error => 'No group ID specified'}, status => 400); - return; - } - - my $group = $self->resultset->find($group_id); - if (!$group) { - $self->render(json => {error => "Job group $group_id does not exist"}, status => 404); - return; - } - + return $self->render(json => {error => 'No group ID specified'}, status => 400) && 0 + unless defined(my $group_id = $self->param('group_id')); + return $self->render(json => {error => "Job group $group_id does not exist"}, status => 404) && 0 + unless my $group = $self->resultset->find($group_id); return $group; } @@ -297,8 +285,7 @@ =cut sub update ($self) { - my $group = $self->find_group; - return unless $group; + return unless my $group = $self->find_group; my $validation = $self->validation; # Don't check group name if sorting group by dragging @@ -336,8 +323,7 @@ =cut sub list_jobs ($self) { - my $group = $self->find_group; - return unless $group; + return unless my $group = $self->find_group; my @jobs; if ($self->param('expired')) { @@ -361,8 +347,7 @@ =cut sub delete ($self) { - my $group = $self->find_group(); - return unless $group; + return unless my $group = $self->find_group; if ($group->can('jobs') && scalar($group->jobs) != 0) { return $self->render(json => {error => 'Job group ' . $group->id . ' is not empty'}, status => 400); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1769603414.6c0fa72e/lib/OpenQA/WebAPI/Controller/API/V1/Mm.pm new/openQA-5.1769644379.ef069e9d/lib/OpenQA/WebAPI/Controller/API/V1/Mm.pm --- old/openQA-5.1769603414.6c0fa72e/lib/OpenQA/WebAPI/Controller/API/V1/Mm.pm 2026-01-28 13:30:14.000000000 +0100 +++ new/openQA-5.1769644379.ef069e9d/lib/OpenQA/WebAPI/Controller/API/V1/Mm.pm 2026-01-29 00:52:59.000000000 +0100 @@ -1,10 +1,11 @@ -# Copyright 2015 SUSE LLC +# Copyright SUSE LLC # SPDX-License-Identifier: GPL-2.0-or-later package OpenQA::WebAPI::Controller::API::V1::Mm; -use Mojo::Base 'Mojolicious::Controller'; +use Mojo::Base 'Mojolicious::Controller', -signatures; -use OpenQA::Jobs::Constants; +use OpenQA::Jobs::Constants qw(RUNNING SCHEDULED DONE); +use OpenQA::JobDependencies::Constants qw(PARALLEL); use OpenQA::Schema::Result::Jobs; use OpenQA::Schema::Result::JobDependencies; @@ -38,24 +39,22 @@ # this needs 2 calls to do anything useful # IMHO it should be replaced with get_children and removed -sub get_children_status { - my ($self) = @_; - my $state = $self->stash('state'); - if ($state eq 'running') { - $state = OpenQA::Jobs::Constants::RUNNING; - } - elsif ($state eq 'scheduled') { - $state = OpenQA::Jobs::Constants::SCHEDULED; - } - else { - $state = OpenQA::Jobs::Constants::DONE; - } + +my %STATE_MAPPING = (running => RUNNING, scheduled => SCHEDULED); + +sub get_children_status ($self) { + my $state = $STATE_MAPPING{$self->stash('state')} // DONE; my $jobid = $self->stash('job_id'); + my $jobs = $self->schema->resultset('Jobs'); + my %attr = (columns => ['id'], join => 'parents'); + my @res = $jobs->search({'parents.parent_job_id' => $jobid, state => $state}, \%attr); + return $self->render(json => {jobs => [map { $_->id } @res]}, status => 200); +} - my @res = $self->schema->resultset('Jobs') - ->search({'parents.parent_job_id' => $jobid, state => $state}, {columns => ['id'], join => 'parents'}); - my @res_ids = map { $_->id } @res; - return $self->render(json => {jobs => \@res_ids}, status => 200); +sub _find_jobs ($self, $id_column, $dependency_column, $columns, $join) { + my $job_id = $self->stash('job_id'); + my $jobs = $self->schema->resultset('Jobs'); + [$jobs->search({$id_column => $job_id, $dependency_column => PARALLEL}, {columns => $columns, join => $join})->all]; } =over 4 @@ -69,17 +68,9 @@ =cut -sub get_children { - my ($self) = @_; - my $jobid = $self->stash('job_id'); - - my @res - = $self->schema->resultset('Jobs') - ->search( - {'parents.parent_job_id' => $jobid, 'parents.dependency' => OpenQA::JobDependencies::Constants::PARALLEL}, - {columns => ['id', 'state'], join => 'parents'}); - my %res_ids = map { ($_->id, $_->state) } @res; - return $self->render(json => {jobs => \%res_ids}, status => 200); +sub get_children ($self) { + my $jobs = $self->_find_jobs('parents.parent_job_id', 'parents.dependency', ['id', 'state'], 'parents'); + $self->render(json => {jobs => {map { ($_->id, $_->state) } @$jobs}}, status => 200); } =over 4 @@ -93,17 +84,9 @@ =cut -sub get_parents { - my ($self) = @_; - my $jobid = $self->stash('job_id'); - - my @res - = $self->schema->resultset('Jobs') - ->search( - {'children.child_job_id' => $jobid, 'children.dependency' => OpenQA::JobDependencies::Constants::PARALLEL}, - {columns => ['id'], join => 'children'}); - my @res_ids = map { $_->id } @res; - return $self->render(json => {jobs => \@res_ids}, status => 200); +sub get_parents ($self) { + my $jobs = $self->_find_jobs('children.child_job_id', 'children.dependency', ['id'], 'children'); + return $self->render(json => {jobs => [map { $_->id } @$jobs]}, status => 200); } 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1769603414.6c0fa72e/lib/OpenQA/WebAPI/Controller/API/V1/User.pm new/openQA-5.1769644379.ef069e9d/lib/OpenQA/WebAPI/Controller/API/V1/User.pm --- old/openQA-5.1769603414.6c0fa72e/lib/OpenQA/WebAPI/Controller/API/V1/User.pm 2026-01-28 13:30:14.000000000 +0100 +++ new/openQA-5.1769644379.ef069e9d/lib/OpenQA/WebAPI/Controller/API/V1/User.pm 2026-01-29 00:52:59.000000000 +0100 @@ -2,7 +2,9 @@ # SPDX-License-Identifier: GPL-2.0-or-later package OpenQA::WebAPI::Controller::API::V1::User; -use Mojo::Base 'Mojolicious::Controller'; +use Mojo::Base 'Mojolicious::Controller', -signatures; +use DateTime::Format::Pg; +use Feature::Compat::Try; =pod @@ -30,8 +32,7 @@ =cut -sub delete { - my ($self) = @_; +sub delete ($self) { my $user = $self->schema->resultset('Users')->find($self->param('id')); return $self->render(json => {error => 'Not found'}, status => 404) unless $user; my $result = $user->delete(); @@ -39,4 +40,72 @@ $self->render(json => {result => $result}); } +=over 4 + +=item create_api_key() + +Create a new API key. + +=back + +=cut + +sub create_api_key ($self) { + my $user = $self->current_user; + my $expiration; + my $validation = $self->validation; + $validation->optional('expiration', 'seconds_optional')->datetime; + return $self->render( + json => {error => 'Date must be in format ' . DateTime::Format::Pg->format_datetime(DateTime->now())}, + status => 400 + ) if $validation->has_error; + $expiration + = $validation->is_valid('expiration') + ? DateTime::Format::Pg->parse_datetime($validation->param('expiration')) + : DateTime->now->add(years => 1); + my $apikey = $user->api_keys->create({t_expiration => $expiration}); + $self->render(json => {id => $apikey->id, key => $apikey->key, t_expiration => $apikey->t_expiration}); +} + +=over 4 + +=item list_api_keys() + +List API keys of the current user. + +=back + +=cut + +sub list_api_keys ($self) { + my $user = $self->current_user; + my @keys = map { + { + key => $_->key, + t_expiration => $_->t_expiration, + t_created => $_->t_created, + t_updated => $_->t_updated, + } + } $user->api_keys->all; + $self->render(json => {keys => \@keys}); +} + +=over 4 + +=item delete_api_key() + +Delete a specific API key of the current user. + +=back + +=cut + +sub delete_api_key ($self) { + my $user = $self->current_user; + my $key = $user->api_keys->find({key => $self->param('key')}); + return $self->render(json => {error => 'Not found'}, status => 404) unless $key; + $key->delete; + $self->render(json => {result => 1}); +} + 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1769603414.6c0fa72e/lib/OpenQA/WebAPI.pm new/openQA-5.1769644379.ef069e9d/lib/OpenQA/WebAPI.pm --- old/openQA-5.1769603414.6c0fa72e/lib/OpenQA/WebAPI.pm 2026-01-28 13:30:14.000000000 +0100 +++ new/openQA-5.1769644379.ef069e9d/lib/OpenQA/WebAPI.pm 2026-01-29 00:52:59.000000000 +0100 @@ -469,6 +469,10 @@ $api_ra->delete('/user/<id:num>')->name('apiv1_delete_user')->to('user#delete'); + $api_ru->get('/users/me/api_keys')->name('apiv1_list_user_api_keys')->to('user#list_api_keys'); + $api_ru->post('/users/me/api_keys')->name('apiv1_create_user_api_key')->to('user#create_api_key'); + $api_ru->delete('/users/me/api_keys/<key>')->name('apiv1_delete_user_api_key')->to('user#delete_api_key'); + # api/v1/search $api_public_r->get('/experimental/search')->name('apiv1_search_query')->to('search#query'); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1769603414.6c0fa72e/script/openqa-bootstrap new/openQA-5.1769644379.ef069e9d/script/openqa-bootstrap --- old/openQA-5.1769603414.6c0fa72e/script/openqa-bootstrap 2026-01-28 13:30:14.000000000 +0100 +++ new/openQA-5.1769644379.ef069e9d/script/openqa-bootstrap 2026-01-29 00:52:59.000000000 +0100 @@ -112,8 +112,8 @@ proxy_args="" [[ -n "$setup_web_proxy" ]] && proxy_args="--proxy=$setup_web_proxy" setup=$OPENQA_DIR/script/configure-web-proxy -if command -v $setup; then - bash -ex $setup "$proxy_args" +if command -v "$setup"; then + bash -ex "$setup" "$proxy_args" else curl -s https://raw.githubusercontent.com/os-autoinst/openQA/master/script/configure-web-proxy | bash -ex -s -- "$proxy_args" fi diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1769603414.6c0fa72e/t/05-scheduler-dependencies.t new/openQA-5.1769644379.ef069e9d/t/05-scheduler-dependencies.t --- old/openQA-5.1769603414.6c0fa72e/t/05-scheduler-dependencies.t 2026-01-28 13:30:14.000000000 +0100 +++ new/openQA-5.1769644379.ef069e9d/t/05-scheduler-dependencies.t 2026-01-29 00:52:59.000000000 +0100 @@ -345,11 +345,17 @@ sub _check_mm_api { my $explain_tx_res = sub { - always_explain $t->tx->res->content; # uncoverable statement + always_explain $t->tx->res->body; # uncoverable statement }; $t->get_ok('/api/v1/mm/children/running')->status_is(200)->json_is('/jobs' => [$jobF->id])->or($explain_tx_res); $t->get_ok('/api/v1/mm/children/scheduled')->status_is(200)->json_is('/jobs' => [])->or($explain_tx_res); $t->get_ok('/api/v1/mm/children/done')->status_is(200)->json_is('/jobs' => [$jobE->id])->or($explain_tx_res); + + my %children = ($jobF->id => RUNNING, $jobE->id => DONE); + $t->get_ok('/api/v1/mm/children')->status_is(200)->json_is('/jobs' => \%children)->or($explain_tx_res); + + my @parents = (99983); + $t->get_ok('/api/v1/mm/parents')->status_is(200)->json_is('/jobs' => \@parents)->or($explain_tx_res); } subtest 'MM API for children status - available only for running jobs' => sub { isnt(my $job_token = $sent->{job}->{$jobC->id}->{worker}->get_property('JOBTOKEN'), undef, 'JOBTOKEN is present'); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1769603414.6c0fa72e/t/api/02-iso.t new/openQA-5.1769644379.ef069e9d/t/api/02-iso.t --- old/openQA-5.1769603414.6c0fa72e/t/api/02-iso.t 2026-01-28 13:30:14.000000000 +0100 +++ new/openQA-5.1769644379.ef069e9d/t/api/02-iso.t 2026-01-29 00:52:59.000000000 +0100 @@ -85,6 +85,11 @@ group_id => 1002, ); +subtest 'querying non-existent scheduled product' => sub { + $t->get_ok('/api/v1/isos/999')->status_is(404); + $t->json_is('/error' => 'Scheduled product does not exist.'); +}; + subtest 'group filter and priority override' => sub { # add a job template for group 1002 my $job_template = $job_templates->create({@job_template_params, product_id => 1}); @@ -824,7 +829,12 @@ subtest 're-schedule product' => sub { plan skip_all => 'previous test "async flag" has not scheduled a product' unless $scheduled_product_id; - my $res = schedule_iso($t, {scheduled_product_clone_id => $scheduled_product_id}, 200, {async => 1}); + my $res = schedule_iso($t, {scheduled_product_clone_id => 'foobar'}, 400); + like $res->body, qr/scheduled_product_id.*invalid/, 'error returned if scheduled product to clone from is invalid'; + $res = schedule_iso($t, {scheduled_product_clone_id => 1234567}, 404); + like $res->body, qr/to clone.*not found/, 'error returned if scheduled product to clone from does not exist'; + + $res = schedule_iso($t, {scheduled_product_clone_id => $scheduled_product_id}, 200, {async => 1}); my $json = $res->json; my $cloned_scheduled_product_id = $json->{scheduled_product_id}; ok($cloned_scheduled_product_id, 'scheduled product ID returned'); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1769603414.6c0fa72e/t/api/04-jobs.t new/openQA-5.1769644379.ef069e9d/t/api/04-jobs.t --- old/openQA-5.1769603414.6c0fa72e/t/api/04-jobs.t 2026-01-28 13:30:14.000000000 +0100 +++ new/openQA-5.1769644379.ef069e9d/t/api/04-jobs.t 2026-01-29 00:52:59.000000000 +0100 @@ -70,6 +70,7 @@ my $schema = $t->app->schema; my $assets = $schema->resultset('Assets'); my $jobs = $schema->resultset('Jobs'); +my $workers = $schema->resultset('Workers'); my $comments = $schema->resultset('Comments'); my $products = $schema->resultset('Products'); my $testsuites = $schema->resultset('TestSuites'); @@ -468,11 +469,16 @@ }; subtest 'parameter validation on artefact upload' => sub { - $t->post_ok('/api/v1/jobs/99963/artefact?file=not-a-file&md5=not-an-md5sum&image=1')->status_is(400)->json_is( - { - error_status => 400, - error => 'Erroneous parameters (file invalid, md5 invalid)', - }); + $t->post_ok('/api/v1/jobs/99963/artefact?file=not-a-file&md5=not-an-md5sum&image=1')->status_is(400); + $t->json_is({error_status => 400, error => 'Erroneous parameters (file invalid, md5 invalid)'}); + + combined_like { $t->post_ok('/api/v1/jobs/34563/artefact')->status_is(404) } qr/artefact for non-existing job/, + 'upload without job logged'; + $t->json_is({error_status => 404, error => 'Specified job 34563 does not exist'}); + + combined_like { $t->post_ok('/api/v1/jobs/99926/artefact')->status_is(404) } + qr/artefact for job with no worker assigned/, 'upload without worker logged'; + $t->json_is({error_status => 404, error => 'No worker assigned'}); }; my $expected_result_size = 0; @@ -791,6 +797,26 @@ # note: The arrays are supposed to be sorted so it is fine to assume a fix order here. }; + $schema->txn_begin; + + subtest 'update running job using assigned worker' => sub { + my $job = $jobs->search({id => 99963}); + my $worker = $workers->search({job_id => 99963}); + + $job->update({state => RUNNING, result => NONE, assigned_worker_id => 2, t_finished => undef}); + $worker->update({job_id => undef}); + $t->post_ok('/api/v1/jobs/99963/status', json => {status => {worker_id => 2}})->status_is(200); + is $worker->count, 1, 'job of assigned worker updated'; + + $job->update({state => RUNNING, result => NONE, assigned_worker_id => undef}); + $worker->update({job_id => undef}); + $t->post_ok('/api/v1/jobs/99963/status', json => {status => {worker_id => 2}})->status_is(400); + $t->json_like('/error' => qr/worker 2.*not.*assigned/, 'error about update without assigned worker'); + is $worker->count, 0, 'job of worker not updated'; + }; + + $schema->txn_rollback; + subtest 'wrong parameters' => sub { combined_like { $t->post_ok('/api/v1/jobs/9999999/status', json => {})->status_is(400) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1769603414.6c0fa72e/t/api/15-users.t new/openQA-5.1769644379.ef069e9d/t/api/15-users.t --- old/openQA-5.1769603414.6c0fa72e/t/api/15-users.t 2026-01-28 13:30:14.000000000 +0100 +++ new/openQA-5.1769644379.ef069e9d/t/api/15-users.t 2026-01-29 00:52:59.000000000 +0100 @@ -5,6 +5,8 @@ use Test::Most; use Test::Mojo; use Test::Warnings ':report_warnings'; +use Date::Format 'time2str'; +use Time::Seconds; use FindBin; use lib "$FindBin::Bin/../lib", "$FindBin::Bin/../../external/os-autoinst-common/lib"; use OpenQA::Test::TimeLimit '8'; @@ -34,4 +36,49 @@ $t->post_ok('/api/v1/feature?version=42')->status_is(200, 'can set the feature version of current user'); is $app->schema->resultset('Users')->find(99902)->feature_version, 42, 'feature version was updated'; +my $res = $t->post_ok('/api/v1/users/me/api_keys')->status_is(200, 'create api key')->tx->res->json; +ok $res->{key}, 'key returned'; +my $expected_year = time2str('%Y', time + ONE_YEAR, 'UTC'); +like $res->{t_expiration}, qr/^$expected_year-/, 'default expiration is set to 1 year from now'; +my $key1 = $res->{key}; + +my $new_key = $app->schema->resultset('ApiKeys')->find({key => $res->{key}}); +ok $new_key, 'key found in DB'; +is $new_key->user_id, 99902, 'key belongs to correct user'; + +subtest 'create_api_key with expiration' => sub { + my $expiration_time = time + ONE_YEAR; + my $expiration = time2str('%Y-%m-%d %H:%M:%S', $expiration_time, 'UTC'); + $res = $t->post_ok('/api/v1/users/me/api_keys' => form => {expiration => $expiration}) + ->status_is(200, 'create api key with expiration')->tx->res->json; + ok $res->{key}, 'key returned'; + my $expected_year = time2str('%Y', $expiration_time, 'UTC'); + like $res->{t_expiration}, qr/^$expected_year-/, 'expiration matches expected year'; +}; + +subtest 'create_api_key with invalid expiration' => sub { + $t->post_ok('/api/v1/users/me/api_keys' => form => {expiration => 'invalid'}) + ->status_is(400, 'invalid expiration rejected'); +}; + +subtest 'test list_api_keys' => sub { + my $key2 = $res->{key}; + $res = $t->get_ok('/api/v1/users/me/api_keys')->status_is(200, 'list api keys')->tx->res->json; + my $api_keys = $res->{keys}; + ok scalar @$api_keys >= 2, 'at least two keys found'; + ok((grep { $_->{key} eq $key1 } @$api_keys), "key $key1 found in list"); + ok((grep { $_->{key} eq $key2 } @$api_keys), "key $key2 found in list"); + ok !exists $api_keys->[0]{secret}, 'secret is not returned in list'; +}; + +subtest 'test delete_api_key' => sub { + my $count_before = scalar @{$res->{keys}}; + $t->delete_ok("/api/v1/users/me/api_keys/$key1")->status_is(200, 'delete api key'); + $res = $t->get_ok('/api/v1/users/me/api_keys')->status_is(200)->tx->res->json; + is scalar @{$res->{keys}}, $count_before - 1, 'one key less'; + ok !((grep { $_->{key} eq $key1 } @{$res->{keys}})), "key $key1 no longer in list"; + + $t->delete_ok("/api/v1/users/me/api_keys/NONEXISTENT")->status_is(404, 'delete nonexistent key'); +}; + done_testing(); ++++++ openQA.obsinfo ++++++ --- /var/tmp/diff_new_pack.U2tDce/_old 2026-02-05 18:03:03.606301855 +0100 +++ /var/tmp/diff_new_pack.U2tDce/_new 2026-02-05 18:03:03.618302358 +0100 @@ -1,5 +1,5 @@ name: openQA -version: 5.1769603414.6c0fa72e -mtime: 1769603414 -commit: 6c0fa72ec7b1b63bbc5faec28aaf246adb5acd8e +version: 5.1769644379.ef069e9d +mtime: 1769644379 +commit: ef069e9dffeab3a6dd3a1834218ed539ef3d6d82
