diff --git a/doc/manual/src/projects.md b/doc/manual/src/projects.md index c4ea8f55..3b9b8eb1 100644 --- a/doc/manual/src/projects.md +++ b/doc/manual/src/projects.md @@ -462,3 +462,33 @@ notifications, add it to the path option of the Hydra services in your systemd.services.hydra-queue-runner.path = [ pkgs.ssmtp ]; systemd.services.hydra-server.path = [ pkgs.ssmtp ]; +Gitea Integration +----------------- + +Hydra can notify Git servers (such as [GitLab](https://gitlab.com/), [GitHub](https://github.com) +or [Gitea](https://gitea.io/en-us/)) about the result of a build from a Git checkout. + +This section describes how it can be implemented for `gitea`, but the approach for `gitlab` is +analogous: + +* [Obtain an API token for your user](https://docs.gitea.io/en-us/api-usage/#authentication) +* Add it to your `hydra.conf` like this: + ``` nix + { + services.hydra-dev.extraConfig = '' + + your_username=your_token + + ''; + } + ``` + +* For a jobset with a `Git`-input which points to a `gitea`-instance, add the following + additional inputs: + + | Type | Name | Value | + | -------------- | ------------------- | ----------------------------------- | + | `String value` | `gitea_repo_name` | *Name of the repository to build* | + | `String value` | `gitea_repo_owner` | *Owner of the repository* | + | `String value` | `gitea_status_repo` | *Name of the `Git checkout` input* | + diff --git a/src/lib/Hydra/Plugin/GiteaStatus.pm b/src/lib/Hydra/Plugin/GiteaStatus.pm new file mode 100644 index 00000000..aa1994cf --- /dev/null +++ b/src/lib/Hydra/Plugin/GiteaStatus.pm @@ -0,0 +1,88 @@ +package Hydra::Plugin::GiteaStatus; + +use strict; +use parent 'Hydra::Plugin'; + +use HTTP::Request; +use JSON; +use LWP::UserAgent; +use Hydra::Helper::CatalystUtils; +use List::Util qw(max); + +sub isEnabled { + my ($self) = @_; + return defined $self->{config}->{gitea_authorization}; +} + +sub toGiteaState { + # See https://try.gitea.io/api/swagger#/repository/repoCreateStatus + my ($status, $buildStatus) = @_; + if ($status == 0 || $status == 1) { + return "pending"; + } elsif ($buildStatus == 0) { + return "success"; + } else { + return "error"; + } +} + +sub common { + my ($self, $build, $dependents, $status) = @_; + my $baseurl = $self->{config}->{'base_uri'} || "http://localhost:3000"; + + # Find matching configs + foreach my $b ($build, @{$dependents}) { + my $jobName = showJobName $b; + my $evals = $build->jobsetevals; + my $ua = LWP::UserAgent->new(); + + # Don't send out "pending/running" status updates if the build is already finished + next if $status < 2 && $b->finished == 1; + + my $state = toGiteaState($status, $b->buildstatus); + my $body = encode_json( + { + state => $state, + target_url => "$baseurl/build/" . $b->id, + description => "Hydra build #" . $b->id . " of $jobName", + context => "Hydra " . $b->get_column('job'), + }); + + while (my $eval = $evals->next) { + my $giteastatusInput = $eval->jobsetevalinputs->find({ name => "gitea_status_repo" }); + next unless defined $giteastatusInput && defined $giteastatusInput->value; + my $i = $eval->jobsetevalinputs->find({ name => $giteastatusInput->value, altnr => 0 }); + next unless defined $i; + + my $repoOwner = $eval->jobsetevalinputs->find({ name => "gitea_repo_owner" })->value; + my $repoName = $eval->jobsetevalinputs->find({ name => "gitea_repo_name" })->value; + my $accessToken = $self->{config}->{gitea_authorization}->{$repoOwner}; + + my $rev = $i->revision; + my $domain = URI->new($i->uri)->host; + my $url = "http://$domain:3000/api/v1/repos/$repoOwner/$repoName/statuses/$rev"; + + print STDERR "GiteaStatus POSTing $state to $url\n"; + my $req = HTTP::Request->new('POST', $url); + $req->header('Content-Type' => 'application/json'); + $req->header('Authorization' => "token $accessToken"); + $req->content($body); + my $res = $ua->request($req); + print STDERR $res->status_line, ": ", $res->decoded_content, "\n" unless $res->is_success; + } + } +} + +sub buildQueued { + common(@_, [], 0); +} + +sub buildStarted { + common(@_, [], 1); +} + +sub buildFinished { + common(@_, 2); +} + +1;