From c015c4ea3fde951f2467f3356df1a432387e1cbe Mon Sep 17 00:00:00 2001 From: Faye Chun Date: Wed, 18 Dec 2024 04:27:22 -0500 Subject: [PATCH] Add a plugin to poll Gitea pull requests Based off the existing GithubPulls.pm and GitlabPulls.pm plugins. Also adds an integration test for the new 'giteapulls' input type to the existing 'gitea' test. --- nixos-tests.nix | 73 +++++++++++++++++++++----- src/lib/Hydra/Plugin/GiteaPulls.pm | 84 ++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 14 deletions(-) create mode 100644 src/lib/Hydra/Plugin/GiteaPulls.pm diff --git a/nixos-tests.nix b/nixos-tests.nix index 9efe68c81..2de75eeb5 100644 --- a/nixos-tests.nix +++ b/nixos-tests.nix @@ -145,10 +145,18 @@ in git -C /tmp/repo add . git config --global user.email test@localhost git config --global user.name test + + # Create initial commit git -C /tmp/repo commit -m 'Initial import' git -C /tmp/repo remote add origin gitea@machine:root/repo - GIT_SSH_COMMAND='ssh -i $HOME/.ssh/privk -o StrictHostKeyChecking=no' \ - git -C /tmp/repo push origin master + export GIT_SSH_COMMAND='ssh -i $HOME/.ssh/privk -o StrictHostKeyChecking=no' + git -C /tmp/repo push origin master + git -C /tmp/repo log >&2 + + # Create PR branch + git -C /tmp/repo checkout -b pr + git -C /tmp/repo commit --allow-empty -m 'Additional change' + git -C /tmp/repo push origin pr git -C /tmp/repo log >&2 ''; @@ -185,7 +193,7 @@ in cat >data.json < $out; exit 0"]; + { pulls, ... }: + + let + genDrv = name: builtins.derivation { + inherit name; + system = "${system}"; + builder = "/bin/sh"; + allowSubstitutes = false; + preferLocalBuild = true; + args = ["-c" "echo success > $out; exit 0"]; }; - } + + prs = builtins.fromJSON (builtins.readFile pulls); + prJobNames = map (n: "pr-''${n}") (builtins.attrNames prs); + prJobset = builtins.listToAttrs ( + map ( + name: { + inherit name; + value = genDrv name; + } + ) prJobNames + ); + in { + trivial = genDrv "trivial"; + } // prJobset ''; in '' @@ -279,18 +308,34 @@ in + '| jq .buildstatus | xargs test 0 -eq' ) + machine.sleep(3) + data = machine.succeed( - 'curl -Lf -s "http://localhost:3001/api/v1/repos/root/repo/statuses/$(cd /tmp/repo && git show | head -n1 | awk "{print \\$2}")" ' + 'curl -Lf -s "http://localhost:3001/api/v1/repos/root/repo/statuses/$(cd /tmp/repo && git show master | head -n1 | awk "{print \\$2}")?sort=leastindex" ' + "-H 'Accept: application/json' -H 'Content-Type: application/json' " + f"-H 'Authorization: token ${api_token}'" ) response = json.loads(data) - assert len(response) == 2, "Expected exactly three status updates for latest commit (queued, finished)!" + assert len(response) == 2, "Expected exactly two status updates for latest commit (queued, finished)!" assert response[0]['status'] == "success", "Expected finished status to be success!" assert response[1]['status'] == "pending", "Expected queued status to be pending!" + # giteapulls test + + machine.succeed( + "curl --fail -X POST http://localhost:3001/api/v1/repos/root/repo/pulls " + + "-H 'Accept: application/json' -H 'Content-Type: application/json' " + + f"-H 'Authorization: token ${api_token}'" + + ' -d \'{"title":"Test PR", "base":"master", "head": "pr"}\''' + ) + + machine.wait_until_succeeds( + 'curl -Lf -s http://localhost:3000/build/2 -H "Accept: application/json" ' + + '| jq .buildstatus | xargs test 0 -eq' + ) + machine.shutdown() ''; }); diff --git a/src/lib/Hydra/Plugin/GiteaPulls.pm b/src/lib/Hydra/Plugin/GiteaPulls.pm new file mode 100644 index 000000000..9bd81498e --- /dev/null +++ b/src/lib/Hydra/Plugin/GiteaPulls.pm @@ -0,0 +1,84 @@ +# Allow building based on Gitea pull requests. +# +# Example input: +# "pulls": { +# "type": "giteapulls", +# "value": "example.com alice repo" +# "emailresponsible": false +# } + +package Hydra::Plugin::GiteaPulls; + +use strict; +use warnings; +use parent 'Hydra::Plugin'; +use HTTP::Request; +use LWP::UserAgent; +use JSON::MaybeXS; +use Hydra::Helper::CatalystUtils; +use File::Temp; +use POSIX qw(strftime); + +sub supportedInputTypes { + my ($self, $inputTypes) = @_; + $inputTypes->{'giteapulls'} = 'Open Gitea Pull Requests'; +} + +sub _iterate { + my ($url, $auth, $pulls, $ua) = @_; + + my $req = HTTP::Request->new('GET', $url); + $req->header('Authorization' => $auth) if defined $auth; + + my $res = $ua->request($req); + my $content = $res->decoded_content; + die "Error pulling from the gitea pulls API: $content\n" + unless $res->is_success; + + my $pulls_list = decode_json $content; + + foreach my $pull (@$pulls_list) { + $pulls->{$pull->{number}} = $pull; + } + + # TODO Make Link header parsing more robust!!! + my @links = split ',', ($res->header("Link") // ""); + my $next = ""; + foreach my $link (@links) { + my ($url, $rel) = split ";", $link; + if (trim($rel) eq 'rel="next"') { + $next = substr trim($url), 1, -1; + last; + } + } + _iterate($next, $auth, $pulls, $ua) unless $next eq ""; +} + +sub fetchInput { + my ($self, $type, $name, $value, $project, $jobset) = @_; + return undef if $type ne "giteapulls"; + + my ($baseUrl, $owner, $repo, $proto) = split ' ', $value; + if (not defined $proto) { # the protocol handler is exposed as an option in order to do integration testing + $proto = "https" + } + my $auth = $self->{config}->{gitea_authorization}->{$owner}; + + my $ua = LWP::UserAgent->new(); + my %pulls; + _iterate("$proto://$baseUrl/api/v1/repos/$owner/$repo/pulls?limit=100", $auth, \%pulls, $ua); + + my $tempdir = File::Temp->newdir("gitea-pulls" . "XXXXX", TMPDIR => 1); + my $filename = "$tempdir/gitea-pulls.json"; + open(my $fh, ">", $filename) or die "Cannot open $filename for writing: $!"; + print $fh encode_json \%pulls; + close $fh; + + my $storePath = trim(`nix-store --add "$filename"` + or die "cannot copy path $filename to the Nix store.\n"); + chomp $storePath; + my $timestamp = time; + return { storePath => $storePath, revision => strftime "%Y%m%d%H%M%S", gmtime($timestamp) }; +} + +1;