diff --git a/t/buildinfo_provenance.t b/native/buildinfo_provenance.t similarity index 79% rename from t/buildinfo_provenance.t rename to native/buildinfo_provenance.t index 99303a1..225c6cc 100644 --- a/t/buildinfo_provenance.t +++ b/native/buildinfo_provenance.t @@ -7,13 +7,14 @@ use File::Temp qw(tempdir); use FindBin qw($RealBin); use Test::More; -use lib "$RealBin/../lib", "$RealBin/lib"; +use lib "$RealBin/../lib", "$RealBin/../t/lib"; use XCAT::BuildUtils qw(capture_command command_exists digest_file read_binary write_binary); use XCAT::GenesisReleaseTest qw(run_capture); plan skip_all => 'Linux RPM repository tools required' - unless $^O eq 'linux' && !grep { !command_exists($_) } qw(git rpm rpmbuild createrepo_c unshare); + unless $^O eq 'linux' && !grep { !command_exists($_) } qw(git rpm rpmbuild createrepo_c unshare gpg gpgconf rpmsign); +my $parent_pid = $$; my $tmp = tempdir(CLEANUP => 1); my @namespace = $> == 0 ? () : ('unshare', '--user', '--map-root-user'); plan skip_all => 'An unprivileged user namespace is required for the collector root check' @@ -41,9 +42,22 @@ mkdir -p %{buildroot}/usr/share/provenance-fixture SPEC is(run_capture("$tmp/rpm-build.log", 'rpmbuild', '--quiet', '-bb', '--define', "_topdir $top", "$top/SPECS/provenance-fixture.spec"), 0, 'build an isolated RPM fixture') - or BAIL_OUT(read_binary("$tmp/rpm-build.log")); + or die(read_binary("$tmp/rpm-build.log")); my $fixture = "$top/RPMS/noarch/provenance-fixture-1-1.noarch.rpm"; -my $fixture_hash = digest_file($fixture); + +my $key_home = "$tmp/gnupg"; +make_path($key_home); +chmod 0700, $key_home; +my $key_name = 'provenance@example.invalid'; +die read_binary("$tmp/key.log") if run_capture("$tmp/key.log", 'gpg', '--homedir', $key_home, + '--batch', '--pinentry-mode', 'loopback', '--passphrase', '', '--quick-generate-key', + $key_name, 'rsa2048', 'sign', '0'); +END { + local $?; + run_capture("$tmp/key-cleanup.log", 'gpgconf', '--homedir', $key_home, '--kill', 'gpg-agent') + if defined($parent_pid) && $$ == $parent_pid && defined($key_home) && -d $key_home; +} +my $payload_hash = capture_command('rpm', '-qp', '--qf', '%{SIGMD5}', $fixture); for my $case (qw(checkout export missing empty)) { my $root = "$tmp/$case source"; @@ -81,7 +95,8 @@ for my $case (qw(checkout export missing empty)) { '--repo-dep', $repo, '--run-id', 'provenance', '--build-timestamp', $epoch, '--skip-build', '--skip-genesis', '--skip-xcat-dep', '--skip-perl', '--skip-createrepo', '--skip-tarball', '--no-verify-repo', - '--collect-dir', "$top/RPMS/noarch"); + '--collect-dir', "$top/RPMS/noarch", + '--gpg-sign', '--gpg-home', $key_home, '--gpg-key-name', $key_name); is($status, 0, "$case $target full collector succeeds") or diag(read_binary($log)); my $subdir = $target =~ /^openeuler/ ? "openeuler24.03sp3/$arch" : "rh10/$arch"; my $metadata_path = "$repo/$subdir/buildinfo.txt"; @@ -92,8 +107,8 @@ for my $case (qw(checkout export missing empty)) { is($metadata{COMMIT_ID}, substr($expected, 0, 7), "$case $target preserves the short identity contract"); is($metadata{SOURCE_DATE_EPOCH}, "$epoch", "$case $target retains the explicit epoch"); is($metadata{TARGET}, $subdir, "$case $target retains the target repository path"); - is(digest_file("$repo/$subdir/provenance-fixture-1-1.noarch.rpm"), $fixture_hash, - "$case $target collection preserves the RPM bytes"); + is(capture_command('rpm', '-qp', '--qf', '%{SIGMD5}', "$repo/$subdir/provenance-fixture-1-1.noarch.rpm"), $payload_hash, + "$case $target signing preserves the RPM payload"); } } diff --git a/native/fixtures/mock-configs.py b/native/fixtures/mock-configs.py new file mode 100644 index 0000000..a2150f5 --- /dev/null +++ b/native/fixtures/mock-configs.py @@ -0,0 +1,23 @@ +import configparser +import json +from pathlib import Path +import shutil +import sys +import tempfile +from mockbuild.config import load_config + +source = Path(sys.argv[1]).resolve() +with tempfile.TemporaryDirectory() as directory: + config_path = Path(directory) + (config_path / 'templates').mkdir() + for parent in ('openeuler-20.03-sp4.tpl', 'openeuler-22.03-sp4.tpl', 'openeuler-24.03.tpl'): + shutil.copyfile(Path('/etc/mock/templates') / parent, config_path / 'templates' / parent) + shutil.copyfile(source / 'templates/openeuler-lts-xcat.tpl', config_path / 'templates/openeuler-lts-xcat.tpl') + result = {} + for wrapper in sorted(source.glob('openeuler-*.cfg')): + config = load_config(str(config_path), str(wrapper)) + repos = configparser.ConfigParser(interpolation=None) + repos.read_string(config['dnf.conf']) + result[wrapper.stem] = {key: config[key] for key in ('root', 'target_arch', 'legal_host_arches', 'releasever', 'dist', 'use_bootstrap_image')} + result[wrapper.stem]['repos'] = {section: dict(repos[section]) for section in repos.sections()} + print(json.dumps(result)) diff --git a/native/fixtures/power-mock.py b/native/fixtures/power-mock.py new file mode 100644 index 0000000..9abe53e --- /dev/null +++ b/native/fixtures/power-mock.py @@ -0,0 +1,28 @@ +#!/usr/bin/python3 +import json, os, pathlib, shutil, sys +import mockbuild +from mockbuild.util import load_config +args = sys.argv[1:] +call = {'mock': args} +def value(name): return args[args.index(name)+1] +if '--rebuild' in args or '--buildsrpm' in args: + load_config('/etc/mock', value('-r'), None, 'native-contract', + str(pathlib.Path(mockbuild.__file__).parent)) + call['config_rc'] = 0 +if '--rebuild' in args: + name = pathlib.Path(value('--rebuild')).name.split('-1-1.oe2403')[0] + call['name'] = name +if '--buildsrpm' in args: + call['spec'] = pathlib.Path(value('--spec')).read_text() +with open(os.environ['NATIVE_CALLS'], 'a') as f: f.write(json.dumps(call) + '\n') +if '--buildsrpm' in args: + dest = pathlib.Path(value('--resultdir')); dest.mkdir(parents=True, exist_ok=True) + source = json.loads(pathlib.Path(os.environ['NATIVE_OUTPUTS']).read_text())['native-leaf'][1] + shutil.copyfile(source, dest / pathlib.Path(source).name) + sys.exit(0) +if '--rebuild' not in args: sys.exit(0) +if name == os.environ.get('NATIVE_FAIL'): sys.exit(42) +dest = pathlib.Path(value('--resultdir')); dest.mkdir(parents=True, exist_ok=True) +if name == os.environ.get('NATIVE_EMPTY'): sys.exit(0) +fixtures = json.loads(pathlib.Path(os.environ['NATIVE_OUTPUTS']).read_text()) +for source in fixtures[name]: shutil.copyfile(source, dest / pathlib.Path(source).name) diff --git a/native/fixtures/power-wget.pl b/native/fixtures/power-wget.pl new file mode 100644 index 0000000..a84b69b --- /dev/null +++ b/native/fixtures/power-wget.pl @@ -0,0 +1,15 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use File::Copy qw(copy); +use JSON::PP qw(decode_json encode_json); + +open(my $input, '<', $ENV{NATIVE_DOWNLOADS}) or die $!; +my $downloads = decode_json(do { local $/; <$input> }); +close($input) or die $!; +my ($output) = grep { $ARGV[$_] eq '-O' } 0 .. $#ARGV - 1; +die 'wget fixture requires -O' unless defined($output); +open(my $trace, '>>', $ENV{NATIVE_CALLS}) or die $!; +print {$trace} encode_json({wget => $ARGV[-1]}) . "\n" or die $!; +close($trace) or die $!; +copy($downloads->{$ARGV[-1]}, $ARGV[$output + 1]) or die $!; diff --git a/native/fixtures/srpm-mock.pl b/native/fixtures/srpm-mock.pl new file mode 100644 index 0000000..d14d218 --- /dev/null +++ b/native/fixtures/srpm-mock.pl @@ -0,0 +1,50 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use Digest::SHA qw(sha256_hex); +use File::Basename qw(basename); +use File::Copy qw(copy); +use File::Path qw(make_path); +use JSON::PP qw(encode_json); + +sub option { + my ($name) = @_; + for my $i (0 .. $#ARGV - 1) { return $ARGV[$i + 1] if $ARGV[$i] eq $name; } + return; +} +sub contents { + open(my $file, '<', $_[0]) or die $!; + binmode($file); + return do { local $/; <$file> }; +} +my %entry = (argv => \@ARGV); +my $config = option('-r'); +$entry{config} = contents($config) if defined($config) && -f $config; +$entry{spec} = contents(option('--spec')) if defined(option('--spec')); +if (my $source = option('--rebuild')) { + $entry{source} = $source; + $entry{sha256} = sha256_hex(contents($source)); +} +open(my $trace, '>>', $ENV{SCP_CALLS}) or die $!; +print {$trace} encode_json(\%entry) . "\n" or die $!; +close($trace) or die $!; +exit 0 if grep { /^--scrub=/ } @ARGV; +if (grep { $_ eq '--buildsrpm' } @ARGV) { + my $dest = option('--resultdir'); + make_path($dest); + copy($ENV{SCP_FIXTURE_SOURCE}, "$dest/python3-scp-0.14.5-1.src.rpm") or die $!; + exit 0; +} +if ($ENV{SCP_MUTATE_SOURCE}) { + open(my $source, '>>', $ENV{SCP_MUTATE_SOURCE}) or die $!; + print {$source} 'changed after staging' or die $!; + close($source) or die $!; +} +exit 43 if ($ENV{SCP_BUILD_STATUS} // '43') ne '0'; +if (($ENV{SCP_EMPTY_OUTPUT} // '') ne '1') { + my $dest = option('--resultdir'); + make_path($dest); + for my $key (qw(SCP_FIXTURE_BINARY SCP_FIXTURE_SOURCE)) { + copy($ENV{$key}, "$dest/" . basename($ENV{$key})) or die $!; + } +} diff --git a/t/goconserver-openeuler.t b/native/goconserver-openeuler.t similarity index 100% rename from t/goconserver-openeuler.t rename to native/goconserver-openeuler.t diff --git a/native/mock-configs.t b/native/mock-configs.t new file mode 100644 index 0000000..26985c0 --- /dev/null +++ b/native/mock-configs.t @@ -0,0 +1,43 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use FindBin qw($RealBin); +use JSON::PP qw(decode_json); +use Test::More; + +plan skip_all => 'native Mock Python library and templates required' + if system('python3 -c "import mockbuild.config" >/dev/null 2>&1') != 0 + || !-f '/etc/mock/templates/openeuler-24.03.tpl'; + +my @cells = ( + ['20.03sp4', '20.03-LTS-SP4', '20.03LTS_SP4', 'x86_64'], + ['22.03sp4', '22.03-LTS-SP4', '22.03LTS_SP4', 'x86_64'], + ['24.03sp1', '24.03-LTS-SP1', '24.03LTS_SP1', 'x86_64'], + ['24.03sp3', '24.03-LTS-SP3', '24.03LTS_SP3', 'x86_64'], + ['24.03sp4', '24.03-LTS-SP4', '24.03LTS_SP4', 'x86_64'], + ['24.03', '24.03-LTS', '24.03LTS', 'ppc64le'], +); +open(my $pipe, '-|', 'python3', "$RealBin/fixtures/mock-configs.py", "$RealBin/../mock-configs") or die $!; +my $json = do {local $/; <$pipe>}; +close($pipe) or die "native mock config loader failed: $?"; +my $configs = decode_json($json); +for my $cell (@cells) { + my ($version, $release, $releasever, $arch) = @$cell; + my $target = "openeuler-$version-$arch"; + my $config = $configs->{$target}; + is($config->{root}, $target, "$target selects its own buildroot"); + is($config->{target_arch}, $arch, "$target selects its native architecture"); + is_deeply($config->{legal_host_arches}, [$arch], "$target requires a native host"); + is($config->{releasever}, $releasever, "$target retains the release package convention"); + is($config->{dist}, '', "$target retains the native empty dist macro"); + ok(!$config->{use_bootstrap_image}, "$target constructs its bootstrap from signed native RPMs"); + my $repos = $config->{repos}; + my @names = $arch eq 'ppc64le' ? ('OS') : ('OS', 'everything', 'update'); + is_deeply([sort grep {$_ ne 'main'} keys %$repos], [sort @names], "$target selects only published native repositories"); + is($repos->{main}{gpgcheck}, '1', "$target requires native package signatures"); + my $base = "https://repo.openeuler.org/openEuler-$release"; + is_deeply([map {$repos->{$_}{baseurl}} @names], [map {"$base/$_/$arch/"} @names], "$target pins repository URLs to its exact release"); + is_deeply([map {$repos->{$_}{gpgkey}} @names], [map {"$base/OS/$arch/RPM-GPG-KEY-openEuler"} @names], "$target uses the release signing key"); + ok(!grep({$repos->{$_}{gpgcheck} ne '1' || $repos->{$_}{skip_if_unavailable} ne '0'} @names), "$target fails on unsigned packages or unavailable repositories"); +} +done_testing(); diff --git a/t/openeuler-power-inputs.t b/native/openeuler-power-inputs.t similarity index 89% rename from t/openeuler-power-inputs.t rename to native/openeuler-power-inputs.t index 3efebce..e74c8ee 100644 --- a/t/openeuler-power-inputs.t +++ b/native/openeuler-power-inputs.t @@ -9,7 +9,7 @@ use FindBin qw($RealBin); use JSON::PP; use Test::More; -use lib "$RealBin/..", "$RealBin/../lib", "$RealBin/lib"; +use lib "$RealBin/..", "$RealBin/../lib", "$RealBin/../t/lib"; use MockBuildUtils qw(read_manifest); use XCAT::BuildUtils qw(capture_command command_exists digest_file read_binary write_binary); use XCAT::GenesisReleaseTest qw(run_capture dies_like); @@ -22,6 +22,7 @@ plan skip_all => 'Set XCAT_TEST_BUILD_USER to an unprivileged fixture builder' i my $build_uid = $> == 0 ? getpwnam($build_user) : $>; plan skip_all => 'The fixture builder must be unprivileged' unless defined($build_uid) && $build_uid != 0; my @rpm_user = $> == 0 ? ('runuser', '-u', $build_user, '--') : (); +my $parent_pid = $$; my $tmp = tempdir(CLEANUP => !$ENV{XCAT_TEST_KEEP}); diag("native input fixtures: $tmp"); my $repo = abs_path("$RealBin/.."); @@ -32,7 +33,7 @@ my $epoch = 1788718796; my $host_arch = capture_command('uname', '-m'); my %manifest = read_manifest("$repo/packages-manifest.conf"); my $production_plan = eval { load_inputs($repo, $manifest{$target}); }; -ok($production_plan, 'the shipped full POWER manifest has an executable native input plan') or BAIL_OUT($@); +ok($production_plan, 'the shipped full POWER manifest has an executable native input plan') or die($@); is($production_plan->{nodes}{'xCAT-genesis-base'}{build_uid}, 0, 'the shipped Genesis owner declares its root assembly exception'); my %homes; my %keys; @@ -46,14 +47,15 @@ for my $key (qw(publisher build foreign)) { chmod 0700, $home; is(run_capture("$tmp/key-$key.log", 'gpg', '--homedir', $home, '--batch', '--pinentry-mode', 'loopback', '--passphrase', '', '--quick-generate-key', "$key\@example.invalid", 'rsa2048', 'sign', '0'), 0, - "create private $key key") or BAIL_OUT(read_binary("$tmp/key-$key.log")); + "create private $key key") or die(read_binary("$tmp/key-$key.log")); my $listing = capture_command('gpg', '--homedir', $home, '--with-colons', '--list-keys'); ($keys{$key}) = $listing =~ /^fpr:::::::::([0-9A-F]+):/m; write_binary("$home/public.asc", capture_command('gpg', '--homedir', $home, '--armor', '--export', $keys{$key})); } END { + local $?; for my $home (values %homes) { - run_capture("$home/cleanup.log", 'gpgconf', '--homedir', $home, '--kill', 'gpg-agent') if -d $home; + run_capture("$home/cleanup.log", 'gpgconf', '--homedir', $home, '--kill', 'gpg-agent') if defined($parent_pid) && $$ == $parent_pid && -d $home; } } @@ -84,7 +86,7 @@ SPEC write_binary("$tmp/rpmbuild/SPECS/$name.spec", $spec); is(run_capture("$tmp/fixture-$name.log", @rpm_user, 'rpmbuild', '-ba', '--define', "_topdir $tmp/rpmbuild", "$tmp/rpmbuild/SPECS/$name.spec"), 0, "build real $name fixture with nonroot check") - or BAIL_OUT(read_binary("$tmp/fixture-$name.log")); + or die(read_binary("$tmp/fixture-$name.log")); $rpm{$name} = "$tmp/rpmbuild/RPMS/$arch/$name-1-1.oe2403.$arch.rpm"; $rpm{"$name-src"} = "$tmp/rpmbuild/SRPMS/$name-1-1.oe2403.src.rpm"; } @@ -96,7 +98,7 @@ sub signed_copy { local $ENV{GNUPGHOME} = $homes{$key}; is(run_capture("$tmp/sign-$name.log", 'rpmsign', '--define', "_gpg_name $keys{$key}", '--define', '__gpg /usr/bin/gpg', '--addsign', $dest), 0, "sign $name with $key key") - or BAIL_OUT(read_binary("$tmp/sign-$name.log")); + or die(read_binary("$tmp/sign-$name.log")); return $dest; } my %signed; @@ -105,50 +107,8 @@ for my $name (qw(native-leaf-src native-child-src publisher-package publisher-el } my $foreign = signed_copy($rpm{'native-leaf-src'}, 'foreign-source', 'foreign'); -write_binary("$tmp/bin/wget", <<'PY'); -#!/usr/bin/python3 -import json, os, pathlib, shutil, sys -args = sys.argv[1:] -source = json.loads(pathlib.Path(os.environ['NATIVE_DOWNLOADS']).read_text())[args[-1]] -with open(os.environ['NATIVE_CALLS'], 'a') as f: f.write(json.dumps({'wget': args[-1]}) + '\n') -shutil.copyfile(source, args[args.index('-O')+1]) -PY -write_binary("$tmp/bin/mock", <<'PY'); -#!/usr/bin/python3 -import json, os, pathlib, shutil, subprocess, sys -args = sys.argv[1:] -call = {'mock': args} -def value(name): return args[args.index(name)+1] -if '--rebuild' in args or '--buildsrpm' in args: - loader = '''import json, pathlib, sys, mockbuild -from mockbuild.util import load_config -config = load_config('/etc/mock', sys.argv[1], None, 'native-contract', str(pathlib.Path(mockbuild.__file__).parent)) -print(json.dumps({'uid': config['chrootuid'], 'dnf': config['dnf.conf']})) -''' - config = subprocess.run([sys.executable, '-c', loader, value('-r')], - text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - call['config_rc'] = config.returncode - if config.returncode: - print(config.stdout) - sys.exit(config.returncode) -if '--rebuild' in args: - name = pathlib.Path(value('--rebuild')).name.split('-1-1.oe2403')[0] - call['name'] = name -if '--buildsrpm' in args: - call['spec'] = pathlib.Path(value('--spec')).read_text() -with open(os.environ['NATIVE_CALLS'], 'a') as f: f.write(json.dumps(call) + '\n') -if '--buildsrpm' in args: - dest = pathlib.Path(value('--resultdir')); dest.mkdir(parents=True, exist_ok=True) - source = json.loads(pathlib.Path(os.environ['NATIVE_OUTPUTS']).read_text())['native-leaf'][1] - shutil.copyfile(source, dest / pathlib.Path(source).name) - sys.exit(0) -if '--rebuild' not in args: sys.exit(0) -if name == os.environ.get('NATIVE_FAIL'): sys.exit(42) -dest = pathlib.Path(value('--resultdir')); dest.mkdir(parents=True, exist_ok=True) -if name == os.environ.get('NATIVE_EMPTY'): sys.exit(0) -fixtures = json.loads(pathlib.Path(os.environ['NATIVE_OUTPUTS']).read_text()) -for source in fixtures[name]: shutil.copyfile(source, dest / pathlib.Path(source).name) -PY +copy("$RealBin/fixtures/power-wget.pl", "$tmp/bin/wget") or die $!; +copy("$RealBin/fixtures/power-mock.py", "$tmp/bin/mock") or die $!; chmod 0755, "$tmp/bin/wget", "$tmp/bin/mock"; local $ENV{PATH} = "$tmp/bin:$ENV{PATH}"; local $ENV{NATIVE_DOWNLOADS} = "$tmp/downloads.json"; diff --git a/t/openeuler-srpm.t b/native/openeuler-srpm.t similarity index 91% rename from t/openeuler-srpm.t rename to native/openeuler-srpm.t index 85911de..860a109 100644 --- a/t/openeuler-srpm.t +++ b/native/openeuler-srpm.t @@ -10,12 +10,13 @@ use FindBin qw($RealBin); use JSON::PP qw(decode_json); use Test::More; -use lib "$RealBin/../lib", "$RealBin/lib"; +use lib "$RealBin/../lib", "$RealBin/../t/lib"; use XCAT::BuildUtils qw(capture_command command_exists digest_file read_binary write_binary); use XCAT::GenesisReleaseTest qw(run_capture); plan skip_all => 'Linux RPM tools and user namespaces required' - unless $^O eq 'linux' && !grep { !command_exists($_) } qw(rpm rpmkeys rpmbuild createrepo_c unshare python3 gpg gpgconf); + unless $^O eq 'linux' && !grep { !command_exists($_) } qw(rpm rpmkeys rpmbuild createrepo_c unshare gpg gpgconf); +my $parent_pid = $$; my $tmp = tempdir(CLEANUP => 1); my @namespace = $> == 0 ? () : ('unshare', '--user', '--map-root-user'); plan skip_all => 'User namespace unavailable for the collector root check' @@ -40,10 +41,11 @@ if ($ENV{XCAT_TEST_GENESIS_SIGNING_ONLY}) { is(run_capture("$tmp/key.log", 'gpg', '--homedir', $key_home, '--batch', '--pinentry-mode', 'loopback', '--passphrase', '', '--quick-generate-key', $key_name, 'rsa2048', 'sign', '0'), 0, 'create a private ephemeral signing identity for the repository gate') - or BAIL_OUT(read_binary("$tmp/key.log")); + or die(read_binary("$tmp/key.log")); END { + local $?; run_capture("$tmp/key-cleanup.log", 'gpgconf', '--homedir', $key_home, '--kill', 'gpg-agent') - if defined($key_home) && -d $key_home; + if defined($parent_pid) && $$ == $parent_pid && defined($key_home) && -d $key_home; } write_binary("$tmp/fixture/SPECS/python3-scp.spec", <<'SPEC'); Name: python3-scp @@ -62,40 +64,8 @@ printf 'fixture\n' > %{buildroot}/usr/share/scp-contract/payload SPEC is(run_capture("$tmp/fixture.log", 'rpmbuild', '--quiet', '-ba', '--define', "_topdir $tmp/fixture", "$tmp/fixture/SPECS/python3-scp.spec"), 0, 'build real RPM fixtures for the command boundary') - or BAIL_OUT(read_binary("$tmp/fixture.log")); -write_binary("$tmp/bin/mock", <<'PYTHON'); -#!/usr/bin/python3 -import hashlib, json, os, pathlib, shutil, sys -args = sys.argv[1:] -entry = {'argv': args} -def option(name): - return args[args.index(name) + 1] -if '-r' in args and pathlib.Path(option('-r')).is_file(): - entry['config'] = pathlib.Path(option('-r')).read_text() -if '--spec' in args: - entry['spec'] = pathlib.Path(option('--spec')).read_text() -if '--rebuild' in args: - src = pathlib.Path(option('--rebuild')) - entry['source'] = str(src) - entry['sha256'] = hashlib.sha256(src.read_bytes()).hexdigest() -with open(os.environ['SCP_CALLS'], 'a') as stream: - stream.write(json.dumps(entry) + '\n') -if any(x.startswith('--scrub=') for x in args): - sys.exit(0) -if '--buildsrpm' in args: - dest = pathlib.Path(option('--resultdir')); dest.mkdir(parents=True, exist_ok=True) - shutil.copyfile(os.environ['SCP_FIXTURE_SOURCE'], dest / 'python3-scp-0.14.5-1.src.rpm') - sys.exit(0) -if os.environ.get('SCP_MUTATE_SOURCE'): - with open(os.environ['SCP_MUTATE_SOURCE'], 'ab') as stream: - stream.write(b'changed after staging') -if os.environ.get('SCP_BUILD_STATUS', '43') != '0': - sys.exit(43) -if os.environ.get('SCP_EMPTY_OUTPUT') != '1': - dest = pathlib.Path(option('--resultdir')); dest.mkdir(parents=True, exist_ok=True) - for key in ('SCP_FIXTURE_BINARY', 'SCP_FIXTURE_SOURCE'): - source = pathlib.Path(os.environ[key]); shutil.copyfile(source, dest / source.name) -PYTHON + or die(read_binary("$tmp/fixture.log")); +copy("$RealBin/fixtures/srpm-mock.pl", "$tmp/bin/mock") or die $!; chmod 0755, "$tmp/bin/mock"; sub scenario { diff --git a/t/xnba-release-suffix.t b/native/xnba-release-suffix.t similarity index 99% rename from t/xnba-release-suffix.t rename to native/xnba-release-suffix.t index c004d32..ca36c80 100644 --- a/t/xnba-release-suffix.t +++ b/native/xnba-release-suffix.t @@ -11,7 +11,7 @@ use JSON::PP qw(encode_json); use Test::More; use Text::ParseWords qw(shellwords); -use lib "$RealBin/../lib", "$RealBin/lib"; +use lib "$RealBin/../lib", "$RealBin/../t/lib"; use XCAT::BuildUtils qw(capture_command command_exists digest_file read_binary write_binary); use XCAT::GenesisReleaseTest qw(run_capture); diff --git a/t/openeuler.t b/t/openeuler.t index d18f46d..e32ce6f 100644 --- a/t/openeuler.t +++ b/t/openeuler.t @@ -59,7 +59,7 @@ is_deeply([install_deps_command('rocky')], ['dnf', '-y', 'install', install_deps exec($^X, "$RealBin/../mockbuild-all.pl", '--verify-repo', $repo, '--repo-root', $tmp) or die $!; } waitpid($pid, 0); - isnt($? >> 8, 0, "$version/$arch empty repository fails the full publication gate"); + isnt((($? & 127) ? 128 + ($? & 127) : $? >> 8), 0, "$version/$arch empty repository fails the full publication gate"); open(my $output, '<', "$tmp/output") or die $!; my $text = do {local $/; <$output>}; close($output); @@ -67,61 +67,4 @@ is_deeply([install_deps_command('rocky')], ['dnf', '-y', 'install', install_deps } } -SKIP: { - skip 'native mock Python library and templates required', 66 - if system('python3 -c "import mockbuild.config" >/dev/null 2>&1') != 0 - || !-f '/etc/mock/templates/openeuler-24.03.tpl'; - my $tmp = tempdir(CLEANUP => 1); - my $loader = "$tmp/load.py"; - open(my $fh, '>', $loader) or die $!; - print {$fh} <<'PYTHON'; -import configparser -import json -from pathlib import Path -import shutil -import sys -import tempfile -from mockbuild.config import load_config - -source = Path(sys.argv[1]).resolve() -with tempfile.TemporaryDirectory() as directory: - config_path = Path(directory) - (config_path / 'templates').mkdir() - for parent in ('openeuler-20.03-sp4.tpl', 'openeuler-22.03-sp4.tpl', 'openeuler-24.03.tpl'): - shutil.copyfile(Path('/etc/mock/templates') / parent, config_path / 'templates' / parent) - shutil.copyfile(source / 'templates/openeuler-lts-xcat.tpl', config_path / 'templates/openeuler-lts-xcat.tpl') - result = {} - for wrapper in sorted(source.glob('openeuler-*.cfg')): - config = load_config(str(config_path), str(wrapper)) - repos = configparser.ConfigParser(interpolation=None) - repos.read_string(config['dnf.conf']) - result[wrapper.stem] = {key: config[key] for key in ('root', 'target_arch', 'legal_host_arches', 'releasever', 'dist', 'use_bootstrap_image')} - result[wrapper.stem]['repos'] = {section: dict(repos[section]) for section in repos.sections()} - print(json.dumps(result)) -PYTHON - close($fh) or die $!; - open(my $pipe, '-|', 'python3', $loader, "$RealBin/../mock-configs") or die $!; - my $json = do {local $/; <$pipe>}; - close($pipe) or die "native mock config loader failed: $?"; - my $configs = decode_json($json); - for my $cell (@cells) { - my ($version, $release, $releasever, $arch) = @$cell; - my $target = "openeuler-$version-$arch"; - my $config = $configs->{$target}; - is($config->{root}, $target, "$target selects its own buildroot"); - is($config->{target_arch}, $arch, "$target selects its native architecture"); - is_deeply($config->{legal_host_arches}, [$arch], "$target requires a native host"); - is($config->{releasever}, $releasever, "$target retains the release package convention"); - is($config->{dist}, '', "$target retains the native empty dist macro"); - ok(!$config->{use_bootstrap_image}, "$target constructs its bootstrap from signed native RPMs"); - my $repos = $config->{repos}; - my @names = $arch eq 'ppc64le' ? ('OS') : ('OS', 'everything', 'update'); - is_deeply([sort grep {$_ ne 'main'} keys %$repos], [sort @names], "$target selects only published native repositories"); - is($repos->{main}{gpgcheck}, '1', "$target requires native package signatures"); - my $base = "https://repo.openeuler.org/openEuler-$release"; - is_deeply([map {$repos->{$_}{baseurl}} @names], [map {"$base/$_/$arch/"} @names], "$target pins repository URLs to its exact release"); - is_deeply([map {$repos->{$_}{gpgkey}} @names], [map {"$base/OS/$arch/RPM-GPG-KEY-openEuler"} @names], "$target uses the release signing key"); - ok(!grep({$repos->{$_}{gpgcheck} ne '1' || $repos->{$_}{skip_if_unavailable} ne '0'} @names), "$target fails on unsigned packages or unavailable repositories"); - } -} done_testing();