From 41669746574ba887c99b37499415ecb4a7212444 Mon Sep 17 00:00:00 2001 From: Austin Seipp Date: Wed, 30 Apr 2014 18:50:43 -0500 Subject: [PATCH] hydra: add Coverity Scan plugin This adds a Hydra plugin for users to submit their open source projects to the Coverity Scan system for analysis. First, add a section to your Hydra config, including the access token, project name, and email, and a regex specifying jobs to upload: project = testrix jobs = foobar:.*:coverity.* email = aseipp@pobox.com token = ${builtins.readFile ./coverity-token} This will upload the scan results for any job whose name matches 'coverity.*' in any jobset in the Hydra 'foobar' project, for the Coverity Scan project named 'testrix'. Note that one upload will occur per job matched by the regular expression - so be careful with how many builds you upload. The jobs which are matched by the jobs specification must have a file in their output path of the form: $out/tarballs/...-cov-int.(xz|lzma|zip|bz2|tgz) The file must have the 'cov-int' directory produced by `cov-build` in the root. (You can also output something into $out/nix-support/hydra-build-products for the Hydra UI.) This file will be found in the store, and uploaded to the service directly using your access credentials. Note the exact extension: don't use .tar.xz, only use .xz specifically. Signed-off-by: Austin Seipp --- src/lib/Hydra/Plugin/CoverityScan.pm | 122 +++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 src/lib/Hydra/Plugin/CoverityScan.pm diff --git a/src/lib/Hydra/Plugin/CoverityScan.pm b/src/lib/Hydra/Plugin/CoverityScan.pm new file mode 100644 index 00000000..ca0c5dab --- /dev/null +++ b/src/lib/Hydra/Plugin/CoverityScan.pm @@ -0,0 +1,122 @@ +package Hydra::Plugin::CoverityScan; + +use strict; +use feature 'switch'; +use parent 'Hydra::Plugin'; +use File::Basename; +use LWP::UserAgent; +use Hydra::Helper::CatalystUtils; + +sub buildFinished { + my ($self, $b, $dependents) = @_; + + my $cfg = $self->{config}->{coverityscan}; + my @config = defined $cfg ? ref $cfg eq "ARRAY" ? @$cfg : ($cfg) : (); + + # Scan the job and see if it matches any of the Coverity Scan projects + my $proj; + my $jobName = showJobName $b; + foreach my $p (@config) { + next unless $jobName =~ /^$p->{jobs}$/; + + # If build is cancelled or aborted, do not upload build + next if $b->buildstatus == 4 || $b->buildstatus == 3; + + # Otherwise, select this Coverity project + $proj = $p; last; + } + + # Bail if there's no matching project + return unless defined $proj; + + # Compile submission information + my $project = $proj->{project}; + my $email = $proj->{email}; + my $token = $proj->{token}; + my $scanurl = $proj->{scanurl} || "http://scan5.coverity.com/cgi-bin/upload.py"; + + # Sanity checks + die "coverity project name not configured" unless defined $project; + die "email must be specified for Coverity project '".$project."'" + unless defined $email; + die "access token must be specified for Coverity project '".$project."'" + unless defined $token; + + # Get tarball locations + my $storePath = ($b->buildoutputs)[0]->path; + my $tarballs = "$storePath/tarballs"; + my $covTarball; + + opendir TARBALLS, $tarballs or die; + while (readdir TARBALLS) { + next unless $_ =~ /.*-coverity-int\.(tgz|lzma|xz|bz2|zip)$/; + $covTarball = "$tarballs/$_"; last; + } + closedir TARBALLS; + + unless (defined $covTarball) { + print STDERR "CoverityScan.pm: Coverity tarball not found in $tarballs; skipping upload...\n"; + return; + } + + # Find the file mimetype + my @exts = qw(.xz .bz2 .lzma .zip .tgz); + my ($dir, $file, $ext) = fileparse($covTarball, @exts); + my $mimetype; + given ($ext) { + when ('.xz') { $mimetype = "application/x-xz"; } + when ('.lzma') { $mimetype = "application/x-xz"; } + when ('.zip') { $mimetype = "application/zip"; } + when ('.bz2') { $mimetype = "application/x-bzip2"; } + when ('.tgz') { $mimetype = "application/x-gzip"; } + default { die "couldn't parse extension of $covTarball"; } + } + + die "couldn't detect mimetype of $covTarball" unless defined $mimetype; + + # Parse version number from tarball + my $pkgNameRE = "(?:(?:[A-Za-z0-9]|(?:-[^0-9]))+)"; + my $versionRE = "(?:[A-Za-z0-9\.\-]+)"; + + my $shortName = basename($covTarball); + my $version = $2 if $shortName =~ /^($pkgNameRE)-($versionRE)-coverity-int.*$/; + + die "CoverityScan.pm: Couldn't parse build version for upload! ($shortName)" + unless defined $version; + + # Submit build + my $jobid = $b->id; + my $desc = "Hydra Coverity Build ($jobName) - $jobid:$version"; + + print STDERR "uploading $desc ($shortName) to Coverity Scan\n"; + + my $ua = LWP::UserAgent->new(); + my $resp = $ua->post($scanurl, + Content_Type => 'form-data', + Content => [ + project => $project, + email => $email, + token => $token, + version => $version, + description => $desc, + file => [ $covTarball, $shortName, + Content_Type => $mimetype, + ], + ], + ); + + # The Coverity HTTP endpoint doesn't handle errors very well, and always + # returns a 200 :( + my $results = $resp->decoded_content; + if ($results =~ /ERROR!/) { + print STDERR "CoverityScan.pm: upload error - ", $resp->decoded_content, "\n"; + return; + } + + # Just for sanity, in case things change later + unless ($results =~ /Your request has been submitted/) { + print STDERR "CoverityScan.pm: upload error, didn't find expected response - ", $resp->decoded_content, "\n"; + } +} + +1;