diff --git a/external/os-autoinst-common/.github/workflows/base-commit-message-checker.yml b/external/os-autoinst-common/.github/workflows/base-commit-message-checker.yml new file mode 100644 index 00000000..a34ceebe --- /dev/null +++ b/external/os-autoinst-common/.github/workflows/base-commit-message-checker.yml @@ -0,0 +1,58 @@ +--- +name: 'Commit message check' + +# yamllint disable-line rule:truthy +on: + workflow_call: + secrets: + accessToken: + required: true + +jobs: + base-check-commit-message: + name: Check commit message + runs-on: ubuntu-latest + steps: + - name: Check subject beginning + uses: gsactions/commit-message-checker@v2 + with: + pattern: '^([A-Z]|\S+:|git subrepo pull)' + flags: 'g' + error: 'The subject does not start with a capital or tag.' + excludeDescription: 'true' + excludeTitle: 'true' + checkAllCommitMessages: 'true' + accessToken: ${{ secrets.accessToken }} + + - name: Check subject line length + uses: gsactions/commit-message-checker@v2 + with: + pattern: '^.{1,72}(\n|$)' + flags: 'g' + error: 'The maximum subject line length of 72 characters is exceeded.' + excludeDescription: 'true' + excludeTitle: 'true' + checkAllCommitMessages: 'true' + accessToken: ${{ secrets.accessToken }} + + - name: Check subject ending + uses: gsactions/commit-message-checker@v2 + with: + pattern: '^.+(?. +--character-encoding=none +--no-valign +-l=160 +-fbl # don't change blank lines +-fnl # don't remove new lines +-nsfs # no spaces before semicolons +-baao # space after operators +-bbao # space before operators +-pt=2 # no spaces around () +-bt=2 # no spaces around [] +-sbt=2 # no spaces around {} +-sct # stack closing tokens )} \ No newline at end of file diff --git a/external/os-autoinst-common/.tidyallrc b/external/os-autoinst-common/.tidyallrc new file mode 100644 index 00000000..d4c7a456 --- /dev/null +++ b/external/os-autoinst-common/.tidyallrc @@ -0,0 +1,3 @@ +[PerlTidy] +select = **/*.{pl,pm,t} tools/tidyall tools/perlcritic tools/update-deps +argv = --profile=$ROOT/.perltidyrc diff --git a/external/os-autoinst-common/README.md b/external/os-autoinst-common/README.md index c968354d..537ea4b0 100644 --- a/external/os-autoinst-common/README.md +++ b/external/os-autoinst-common/README.md @@ -8,8 +8,6 @@ This repository is to be used as a [![Packaging status](https://repology.org/badge/vertical-allrepos/git-subrepo.svg)](https://repology.org/project/git-subrepo/versions) -and in `devel:openQA`, e.g. https://download.opensuse.org/repositories/devel:/openQA:/Leap:/15.2/openSUSE_Leap_15.2/ - ## Usage ### Clone diff --git a/external/os-autoinst-common/cpanfile b/external/os-autoinst-common/cpanfile new file mode 100644 index 00000000..bd53e7e6 --- /dev/null +++ b/external/os-autoinst-common/cpanfile @@ -0,0 +1,26 @@ +################################################## +# WARNING +# This file is autogenerated by tools/update-deps +# from dependencies.yaml +################################################## + +requires 'Module::CPANfile'; +requires 'Storable', '>= 3.06'; + +on 'test' => sub { + requires 'Test::Most'; + requires 'Test::Warnings'; + +}; + +on 'develop' => sub { + requires 'Code::TidyAll'; + requires 'Perl::Critic'; + requires 'Perl::Critic::Community'; + requires 'Perl::Tidy', '== 20230912'; + +}; + +feature 'coverage', 'coverage for CI' => sub { + +}; diff --git a/external/os-autoinst-common/dependencies.yaml b/external/os-autoinst-common/dependencies.yaml new file mode 100644 index 00000000..fc829cfd --- /dev/null +++ b/external/os-autoinst-common/dependencies.yaml @@ -0,0 +1,32 @@ +--- +# % is placeholder for section. +# e.g.: +# % => develop +# %_requires => develop_requires +targets: + # List all %_requires into a cpanfile + cpanfile: [main, develop, test] + cpanfile-targets: + # save %_require into cpanfile section + develop: develop + test: test + +main_requires: + # Needed until preaction/Log-Any#105 is solved. + perl(Storable): '>= 3.06' + perl(Module::CPANfile): + perl(version): + +develop_requires: + perl(Perl::Tidy): '== 20230912' + perl(Code::TidyAll): + perl(Perl::Critic): + perl(Perl::Critic::Community): + +cover_requires: + perl(Devel::Cover): + perl(Devel::Cover::Report::Codecov): + +test_requires: + perl(Test::Most): + perl(Test::Warnings): diff --git a/external/os-autoinst-common/lib/OpenQA/Test/PatchDeparse.pm b/external/os-autoinst-common/lib/OpenQA/Test/PatchDeparse.pm index 429c27fb..ee69f2f8 100644 --- a/external/os-autoinst-common/lib/OpenQA/Test/PatchDeparse.pm +++ b/external/os-autoinst-common/lib/OpenQA/Test/PatchDeparse.pm @@ -15,7 +15,8 @@ if ( ) { -#<<< do not let perltidy touch this +#<<< do not let perltidy nor perlcritic touch this +## no critic (TestingAndDebugging::ProhibitNoStrict ValuesAndExpressions::ProhibitInterpolationOfLiterals) # This is not our code, and formatting should stay the same for # better comparison with new versions of B::Deparse # <---- PATCH @@ -60,7 +61,7 @@ elsif ($B::Deparse::VERSION) { diag "Using B::Deparse v$B::Deparse::VERSION. If you see 'uninitialized' warnings, update patch in t/lib/OpenQA/Test/PatchDeparse.pm"; } - +## use critic 1; diff --git a/external/os-autoinst-common/lib/OpenQA/Test/TimeLimit.pm b/external/os-autoinst-common/lib/OpenQA/Test/TimeLimit.pm index 2b441352..2a2dc700 100644 --- a/external/os-autoinst-common/lib/OpenQA/Test/TimeLimit.pm +++ b/external/os-autoinst-common/lib/OpenQA/Test/TimeLimit.pm @@ -4,7 +4,7 @@ package OpenQA::Test::TimeLimit; use Test::Most; -my $SCALE_FACTOR = 1; +my $SCALE_FACTOR = $ENV{OPENQA_TEST_TIMEOUT_SCALE_FACTOR} // 1; sub import { my ($package, $limit) = @_; @@ -12,8 +12,8 @@ sub import { # disable timeout if requested by ENV variable or running within debugger return if ($ENV{OPENQA_TEST_TIMEOUT_DISABLE} or $INC{'perl5db.pl'}); $SCALE_FACTOR *= $ENV{OPENQA_TEST_TIMEOUT_SCALE_COVER} // 3 if Devel::Cover->can('report'); - $SCALE_FACTOR *= $ENV{OPENQA_TEST_TIMEOUT_SCALE_CI} // 2 if $ENV{CI}; - $limit *= $SCALE_FACTOR; + $SCALE_FACTOR *= $ENV{OPENQA_TEST_TIMEOUT_SCALE_CI} // 2 if $ENV{CI}; + $limit *= $SCALE_FACTOR; $SIG{ALRM} = sub { BAIL_OUT "test '$0' exceeds runtime limit of '$limit' seconds\n" }; alarm $limit; } diff --git a/external/os-autoinst-common/lib/perlcritic/Perl/Critic/Policy/ArgumentInUseStrictWarnings.pm b/external/os-autoinst-common/lib/perlcritic/Perl/Critic/Policy/ArgumentInUseStrictWarnings.pm new file mode 100644 index 00000000..4c24edbf --- /dev/null +++ b/external/os-autoinst-common/lib/perlcritic/Perl/Critic/Policy/ArgumentInUseStrictWarnings.pm @@ -0,0 +1,42 @@ +# Copyright SUSE LLC +# SPDX-License-Identifier: GPL-2.0-or-later + +package Perl::Critic::Policy::ArgumentInUseStrictWarnings; + +use strict; +use warnings; +use experimental 'signatures'; +use base 'Perl::Critic::Policy'; + +use Perl::Critic::Utils qw( :severities :classification :ppi ); + +our $VERSION = '0.0.1'; + +sub default_severity { return $SEVERITY_HIGH } +sub default_themes { return qw(openqa) } +sub applies_to { return qw(PPI::Statement::Include) } + +my $desc = q{use strict/warnings with arguments}; +my $expl = q{Remove argument from: %s.}; + +# check that use use strict and warnings don't have arguments. +sub violates ($self, $elem, $document) { + # skip if it's not a use + return unless $elem->type() eq 'use'; + # skip if it's not a pragma + return unless my $pragma = $elem->pragma(); + # skip if it's not warnings or strict + return unless ($pragma eq 'warnings' || $pragma eq 'strict'); + + my @args = $elem->arguments(); + # skip if it doesn't have arguments + return if scalar(@args) == 0; + + # allow promoting warnings to FATAL + return if scalar(grep { $_->content eq 'FATAL' } @args); + + # Report the problem. + return $self->violation($desc, sprintf($expl, $elem), $elem); +} + +1; diff --git a/external/os-autoinst-common/lib/perlcritic/Perl/Critic/Policy/HashKeyQuotes.pm b/external/os-autoinst-common/lib/perlcritic/Perl/Critic/Policy/HashKeyQuotes.pm new file mode 100644 index 00000000..d71f4410 --- /dev/null +++ b/external/os-autoinst-common/lib/perlcritic/Perl/Critic/Policy/HashKeyQuotes.pm @@ -0,0 +1,34 @@ +# Copyright SUSE LLC +# SPDX-License-Identifier: GPL-2.0-or-later + +package Perl::Critic::Policy::HashKeyQuotes; + +use strict; +use warnings; +use experimental 'signatures'; +use base 'Perl::Critic::Policy'; + +use Perl::Critic::Utils qw( :severities :classification :ppi ); + +our $VERSION = '0.0.1'; + +sub default_severity { return $SEVERITY_HIGH } +sub default_themes { return qw(openqa) } +sub applies_to { return qw(PPI::Token::Quote::Single PPI::Token::Quote::Double) } + +# check that hashes are not overly using quotes +# (os-autoinst coding style) +sub violates ($self, $elem, $document) { + #we only want the check hash keys + return if !is_hash_key($elem); + + my $c = $elem->content; + # special characters + return if $c =~ m/[- \/<>.=_:\\\$\|]/; + + my $desc = q{Hash key with quotes}; + my $expl = q{Avoid useless quotes}; + return $self->violation($desc, $expl, $elem); +} + +1; diff --git a/external/os-autoinst-common/lib/perlcritic/Perl/Critic/Policy/RedundantStrictWarning.pm b/external/os-autoinst-common/lib/perlcritic/Perl/Critic/Policy/RedundantStrictWarning.pm new file mode 100644 index 00000000..1f6a3b13 --- /dev/null +++ b/external/os-autoinst-common/lib/perlcritic/Perl/Critic/Policy/RedundantStrictWarning.pm @@ -0,0 +1,56 @@ +# Copyright SUSE LLC +# SPDX-License-Identifier: GPL-2.0-or-later + +package Perl::Critic::Policy::RedundantStrictWarning; + +use strict; +use warnings; +use version 0.77; +use experimental 'signatures'; + +use base 'Perl::Critic::Policy::TestingAndDebugging::RequireUseStrict'; +use Perl::Critic::Utils qw{ $EMPTY }; +use Perl::Critic::Utils::Constants qw{ :equivalent_modules }; + +our $VERSION = '0.0.1'; +my $policy_title = q{Superfluoux use of strict/warning}; +my $policy_explanation = q{%s is equivalent to 'use strict; use warnings;'}; + +sub default_themes { return qw(openqa) } + +sub supported_parameters { + return ( + { + name => 'equivalent_modules', + description => + q, + default_string => $EMPTY, + behavior => 'string list', + list_always_present_values => ['warnings', 'strict', @STRICT_EQUIVALENT_MODULES], + }, + ); +} + +# check that use strict/warnings is not present when equivalent modules are. +sub violates ($self, $, $doc) { + # Find all equivalents of use strict/warnings. + my $use_stmts = $doc->find($self->_generate_is_use_strict()); + + # Bail if there's none. + return unless $use_stmts; + + # Bail out if there's only one. TestingAndDebugging::RequireUseStrict will report + # that there's no use strict/warnings. + return if scalar @{$use_stmts} == 1; + + # If the 'use strict' or 'use warnings' statement is present as well as a + # module already providing that behavior, -> it violates. + return map { $self->_make_violation($_) } grep { !$_->pragma() } @{$use_stmts}; +} + +sub _make_violation ($self, $statement) { + return $self->violation($policy_title, sprintf($policy_explanation, $statement), $statement); +} + +1; + diff --git a/external/os-autoinst-common/tools/perlcritic b/external/os-autoinst-common/tools/perlcritic new file mode 100755 index 00000000..80b85b47 --- /dev/null +++ b/external/os-autoinst-common/tools/perlcritic @@ -0,0 +1,20 @@ +#!/usr/bin/env perl +# Copyright SUSE LLC +# SPDX-License-Identifier: GPL-2.0-or-later +# +# perlcritic with auto-injection of custom perlcritic rules. +use strict; +use warnings; +use experimental 'signatures'; +use FindBin '$Bin'; + +sub extra_include_paths (@extra_paths) { + my @paths = map { ("$Bin/../$_", "$Bin/../external/os-autoinst-common/$_") } @extra_paths; + + # Remove non existing paths + return grep { -e $_ } @paths; +} + +$ENV{PERL5LIB} = join(':', (extra_include_paths('lib/perlcritic'), $ENV{PERL5LIB} // '')); + +exec 'perlcritic', @ARGV; diff --git a/external/os-autoinst-common/tools/tidyall b/external/os-autoinst-common/tools/tidyall new file mode 100755 index 00000000..79b58ec9 --- /dev/null +++ b/external/os-autoinst-common/tools/tidyall @@ -0,0 +1,71 @@ +#!/usr/bin/env perl +# Copyright SUSE LLC +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Tidyall command with perltidy version constraint. +use strict; +use warnings; +use version; +use Perl::Tidy; +use Module::CPANfile; +use FindBin '$Bin'; + +=item perltidy_version + +Grabs the perltidy version from cpanfile using Module::CPANfile. + +=cut + +sub perltidy_version { + my $cpanfile_location; + # Try searching for a cpanfile in: + # - the current working directory + # - a directory above this command + # - the catch-all location in external/os-autoinst-common + my @locations = ('.', "$Bin/..", "$Bin/../external/os-autoinst-common"); + + foreach my $path (@locations) { + next unless -e "$path/cpanfile"; + $cpanfile_location = "$path/cpanfile" and last; + } + + my $version = Module::CPANfile->load($cpanfile_location) + ->prereq_for_module('Perl::Tidy') + ->requirement + ->version; + # Version requirements may contain qualifiers >=, ==, <, etc. The convention + # is to separate the qualifier from the actual version with a space. + # + # It's safe enough to assume that the last item is really the version. + # + # Additionally version will take care of 1.2.0 being equal to 1.2 + return version->new((split ' ', $version)[-1]); +} + +sub is_force_flag { $_ eq '--force' } + +my $required_version = perltidy_version(); +my $detected_version = version->new($Perl::Tidy::VERSION); +my @tidyall_argv = @ARGV; + +unless ($detected_version eq $required_version) { + print STDERR "Incorrect version of perltidy.\n"; + printf STDERR "- Detected: %s\n+ Required: %s\n\n", $detected_version, $required_version; + + my $force_run = grep { is_force_flag } @ARGV; + + unless ($force_run) { + printf STDERR "Please install the appropriate version of perltidy.\n"; + printf STDERR "If you want to proceed anyways, re run with --force flag.\n"; + exit 1; + } + + # tidyall does not know about the --force flag. + @tidyall_argv = grep { !is_force_flag } @tidyall_argv; + + print STDERR 'Proceeding to run with incorrect version of perltidy. '; + print STDERR "Results might not be consistent.\n"; + print STDERR "==================\n"; +} + +exec 'tidyall', @tidyall_argv; diff --git a/external/os-autoinst-common/tools/update-deps b/external/os-autoinst-common/tools/update-deps index fb70a58f..3b6bdb37 100755 --- a/external/os-autoinst-common/tools/update-deps +++ b/external/os-autoinst-common/tools/update-deps @@ -12,21 +12,23 @@ use Getopt::Long; use FindBin qw($Bin); GetOptions( - "help|h" => \my $help, - "specfile=s" => \my $specfile, - "dockerfile=s" => \my $dockerfile, + 'help|h' => \my $help, + cpanfile => \my $cpanfile, + 'specfile=s' => \my $specfile, + 'dockerfile=s' => \my $dockerfile, ); usage(0) if $help; -usage(1) unless $specfile; +usage(1) unless ($cpanfile || $specfile || $dockerfile); -my $scriptname = path(__FILE__)->to_rel("$Bin/.."); -my $yamlfile = "dependencies.yaml"; -my $file = "$Bin/../$yamlfile"; -my $cpanfile = "$Bin/../cpanfile"; +my $proj_root = "$Bin/.."; + +my $scriptname = path(__FILE__)->to_rel($proj_root); +my $dependencies_yaml_location = 'dependencies.yaml'; +my $file = "$proj_root/$dependencies_yaml_location"; +my $cpanfile_location = "$proj_root/cpanfile"; my $data = YAML::PP->new->load_file($file); -my $spec = path($specfile)->slurp; my $spectargets = $data->{targets}->{spec}; my $cpantargets = $data->{targets}->{cpanfile}; @@ -35,8 +37,8 @@ my $cpantarget_mapping = $data->{targets}->{'cpanfile-targets'}; my ($modules_by_target) = get_modules($data, $cpantargets, $cpantarget_mapping); -update_spec(); -update_cpanfile($modules_by_target); +update_spec() if $specfile; +update_cpanfile($modules_by_target) if $cpanfile; update_dockerfile($dockerfile) if $dockerfile; sub update_dockerfile { @@ -71,17 +73,18 @@ sub update_dockerfile { my $begin = '# AUTODEPS START'; my $end = '# AUTODEPS END'; my $run = <<"EOM"; -# This part is autogenerated by $scriptname from $yamlfile +# This part is autogenerated by $scriptname from $dependencies_yaml_location # hadolint ignore=DL3034,DL3037 RUN zypper in -y -C \\ $dep && zypper clean EOM $docker =~ s/($begin\n)(.*)($end\n)/$1$run$3/s; - path($dockerfile)->spurt($docker); + path($dockerfile)->spew($docker); say "Updated $dockerfile"; } sub update_spec { + my $spec = path($specfile)->slurp; for my $target (@$spectargets) { my $name = $target . '_requires'; @@ -101,7 +104,7 @@ sub update_spec { $specline .= " $version"; } } - my $comment = "# The following line is generated from $yamlfile"; + my $comment = "# The following line is generated from $dependencies_yaml_location"; if ($spec =~ s/^# .*generated.*\n^$prefix.*/$comment\n$specline/m) { next; } @@ -111,7 +114,7 @@ sub update_spec { } } - path($specfile)->spurt($spec); + path($specfile)->spew($spec); say "Updated $specfile"; } @@ -152,7 +155,7 @@ sub update_cpanfile { ################################################## # WARNING # This file is autogenerated by $scriptname -# from $yamlfile +# from $dependencies_yaml_location ################################################## EOM @@ -168,8 +171,8 @@ EOM $cover_requires .= ' ' . _requires_line($modules_by_target->{cover}, $module); } my $devel_requires = ''; - for my $module (sort keys %{$modules_by_target->{devel}}) { - $devel_requires .= ' ' . _requires_line($modules_by_target->{devel}, $module); + for my $module (sort keys %{$modules_by_target->{develop}}) { + $devel_requires .= ' ' . _requires_line($modules_by_target->{develop}, $module); } $cpan .= <<"EOM"; @@ -177,7 +180,7 @@ on 'test' => sub { $test_requires }; -on 'devel' => sub { +on 'develop' => sub { $devel_requires }; @@ -186,8 +189,8 @@ $cover_requires }; EOM - path($cpanfile)->spurt($cpan); - say "Updated $cpanfile"; + path($cpanfile_location)->spew($cpan); + say "Updated $cpanfile_location"; } sub usage { diff --git a/external/os-autoinst-common/xt/01-make-update-deps.t b/external/os-autoinst-common/xt/01-make-update-deps.t index 2d40642f..d6a5e7a1 100755 --- a/external/os-autoinst-common/xt/01-make-update-deps.t +++ b/external/os-autoinst-common/xt/01-make-update-deps.t @@ -7,7 +7,7 @@ use Test::Warnings; use FindBin '$Bin'; if (not -e "$Bin/../.git") { - pass("Skipping all tests, not in a git repository"); + pass('Skipping all tests, not in a git repository'); done_testing; exit; }