#!/usr/bin/perl use strict; use warnings; use IPC::Open2; use JSON; sub FindFactoryCommit { my ($package) = @_; # Execute osc cat and capture output my $osc_cmd = "osc cat openSUSE:Factory $package $package.changes"; open( my $osc_fh, "$osc_cmd |" ) or die "Failed to run osc: $!"; my $data = do { local $/; <$osc_fh> }; close($osc_fh); # Calculate size my $size = length($data); # Create blob header my $blob = "blob $size\0$data"; # Open a pipe to openssl to compute the hash my ( $reader, $writer ); my $pid = open2( $reader, $writer, "openssl sha256" ); # Send blob data print $writer $blob; close $writer; # Read the hash result and extract it my $hash_line = <$reader>; waitpid( $pid, 0 ); my ($hash) = $hash_line =~ /([a-fA-F0-9]{64})/; # Run git search command with the hash print("looking for hash: $hash\n"); my @hashes; my $git_cmd = "git -C $package rev-list --all pool/HEAD | while read commit; do git -C $package ls-tree \"\$commit\" | grep -q '^100644 blob $hash' && echo \"\$commit\"; done"; open( my $git_fh, "$git_cmd |" ) or die "Failed to run git search: $!"; while ( my $commit = <$git_fh> ) { chomp $commit; print "Found commit $commit\n"; push( @hashes, $commit ); } close($git_fh); return @hashes; } sub ListPackages { my ($project) = @_; open( my $osc_fh, "curl -s https://src.opensuse.org/openSUSE/Factory/raw/branch/main/pkgs/_meta/devel_packages | awk '{ if ( \$2 == \"$project\" ) print \$1 }' |" ) or die "Failed to run curl: $!"; my @packages = <$osc_fh>; chomp @packages; close($osc_fh); return @packages; } sub FactoryMd5 { my ($package) = @_; my $out = ""; if (system("osc ls openSUSE:Factory $package | grep -q build.specials.obscpio") == 0) { system("mkdir _extract") == 0 || die "_extract exists or can't make it. Aborting."; chdir("_extract") || die; system("osc cat openSUSE:Factory $package build.specials.obscpio | cpio -dium 2> /dev/null") == 0 || die; system("rm .* 2> /dev/null"); open( my $fh, "find -type f -exec /usr/bin/basename {} \\; | xargs md5sum | awk '{print \$1 FS \$2}' | grep -v d41d8cd98f00b204e9800998ecf8427e |") or die; while ( my $l = <$fh>) { $out = $out.$l; } close($fh); chdir("..") && system("rm -rf _extract") == 0 || die; } open( my $fh, "osc ls -v openSUSE:Factory $package | awk '{print \$1 FS \$7}' | grep -v -F '_scmsync.obsinfo\nbuild.specials.obscpio' |") or die; while (my $l = <$fh>) { $out = $out.$l; } close($fh); return $out; } # Read project from first argument sub Usage { die "Usage: $0 [org [package]]"; } my $project = shift or Usage(); my $org = shift; if (not defined($org)) { $org = `osc meta prj $project | grep scmsync | sed -e 's,^.*src.opensuse.org/\\(.*\\)/_ObsPrj.*,\\1,'`; chomp($org); } my @packages = ListPackages($project); my $pkg = shift; @packages = ($pkg) if defined $pkg; my @tomove; my @toremove; if ( ! -e $org ) { mkdir($org); } chdir($org); print "Verify packages in /pool for $org package in $project\n"; my $super_user = $ENV{SUPER}; if (defined($super_user)) { $super_user = "-G $super_user"; } else { $super_user = ""; } my @missing; # verify that packages in devel project is a fork from pool. for my $pkg ( sort(@packages) ) { my $data = `git obs api /repos/$org/$pkg 2> /dev/null`; if ( length($data) == 0 ) { print "***** Repo missing in $org: $pkg\n"; push(@missing, $pkg); next; } else { my $repo = decode_json($data); if ( !$repo->{parent} || $repo->{parent}->{owner}->{username} ne "pool" ) { if ( system("git obs api /repos/pool/$pkg > /dev/null 2> /dev/null") == 0 ) { print "=== $pkg NOT A FORK of exiting package\n"; push( @toremove, $pkg ); } else { print "$pkg NEEDS transfer\n"; push( @tomove, $pkg ); } } } } if ( scalar @missing > 0 ) { for my $pkg (@missing) { my $index = 0; $index++ until $packages[$index] eq $pkg; splice(@packages, $index, 1); } } if ( scalar @toremove > 0 ) { print "ABORTING. Need repos removed.\n"; print "@toremove\n"; exit(1); } if ( scalar @tomove > 0 ) { for my $pkg (@tomove) { system("git obs $super_user api -X POST --data '{\"reparent\": true, \"organization\": \"pool\"}' /repos/$org/$pkg/forks") == 0 and system("git clone gitea\@src.opensuse.org:pool/$pkg") == 0 and system("git -C $pkg checkout -B factory HEAD") == 0 and system("git -C $pkg push origin factory") == 0 and system("git obs $super_user api -X PATCH --data '{\"default_branch\": \"factory\"}' /repos/pool/$pkg") == 0 or die "Error in creating a pool repo"; system("for i in \$(git -C $pkg for-each-ref --format='%(refname:lstrip=3)' refs/remotes/origin/ | grep -v '\\(^HEAD\$\\|^factory\$\\)'); do git -C $pkg push origin :\$i; done") == 0 or die "failed to cull branches"; } } print "Verify complete.\n"; for my $package ( sort(@packages) ) { print " ----- PROCESSING $package\n"; my $url = "https://src.opensuse.org/$org/$package.git"; my $push_url = "gitea\@src.opensuse.org:pool/$package.git"; if ( not -e $package ) { print("cloning...\n"); system("git clone --origin pool $url") == 0 or die "Can't clone $org/$package"; } else { print("adding remote...\n"); system("git -C $package remote rm pool > /dev/null"); system("git -C $package remote add pool $url") == 0 or die "Can't add pool for $package"; } system("git -C $package remote set-url pool --push $push_url") == 0 or die "Can't add push remote for $package"; print("fetching remote...\n"); system("git -C $package fetch pool") == 0 or ( push( @tomove, $package ) and die "Can't fetch pool for $package" ); my @commits = FindFactoryCommit($package); my $Md5Hashes = FactoryMd5($package); my $c; my $match = 0; for my $commit (@commits) { if ( length($commit) != 64 ) { print("Failed to find factory commit. Aborting."); exit(1); } if ( system("git -C $package lfs fetch pool $commit") == 0 and system("git -C $package checkout -B factory $commit") == 0 and system("git -C $package lfs checkout") == 0 and chdir($package)) { open(my $fh, "|-", "md5sum -c --quiet") or die $!; print $fh $Md5Hashes; close $fh; if ($? >> 8 != 0) { chdir("..") || die; next; } open($fh, "|-", "awk '{print \$2}' | sort | bash -c \"diff <(ls -1 | sort) -\"") or die $!; print $fh $Md5Hashes; close $fh; my $ec = $? >> 8; chdir("..") || die; if ($ec == 0) { $c = $commit; $match = 1; last; } } } if ( !$match ) { die "Match not found. Aborting."; } system ("git -C $package push -f pool factory"); print "$package: $c\n"; }