Skip to content

Commit

Permalink
Add GitHub action for cloning an openQA job mentioned in a PR descrip…
Browse files Browse the repository at this point in the history
…tion

This GitHub action allows to clone a job when creating a PR by mentioning
it in the PR description via `@openqa: Clone https://…`.

Related ticket: https://progress.opensuse.org/issues/130934
  • Loading branch information
Martchus committed Feb 21, 2024
1 parent 42cd82a commit eca5837
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 0 deletions.
14 changes: 14 additions & 0 deletions actions/clone-job/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
name: 'Clone job'
description: 'Clone openQA job mentioned in PR description'

runs:
using: composite
image: registry.opensuse.org/devel/openqa/containers/tumbleweed:client
steps:
- uses: actions/checkout@v4
with:
repository: os-autoinst/scripts
path: scripts
- name: Clone and monitor job mentioned in PR description
run: scripts/openqa-clone-and-monitor-job-from-pr
56 changes: 56 additions & 0 deletions openqa-clone-and-monitor-job-from-pr
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env perl

# Note that this script is intended for execution via the GitHub action defined in
# actions/clone-job/action.yaml.

package openqa_clone_and_monitor_job_from_pr;

use Mojo::Base -strict, -signatures;
use Mojo::JSON qw(decode_json);

my $expected_url = $ENV{OPENQA_HOST} // 'https://openqa.opensuse.org';
my $group_id = $ENV{OPENQA_SCHEDULE_GROUP_ID} // 118;
my $pr_body = $ENV{GH_PR_BODY} or die 'GH_PR_BODY must be set';
my $gh_repo = $ENV{GH_REPO} or die 'GH_REPO must be set';
my $gh_ref = $ENV{GH_REF} or die 'GH_REF must be set';
my $gh_srv = $ENV{GITHUB_SERVER_URL} or die 'GITHUB_SERVER_URL must be set';
my $api_key = $ENV{OPENQA_API_KEY} or die 'OPENQA_API_KEY must be set';
my $api_secret = $ENV{OPENQA_API_SECRET} or die 'OPENQA_API_SECRET must be set';
my $screts = "--apikey '$api_key' --apisecret '$api_secret'";
my $vars = "BUILD='$gh_repo.git#$gh_ref' _GROUP_ID='$group_id' CASEDIR='$gh_srv/$gh_repo.git#$gh_ref'";

sub _parse_urls ($text) {
my @urls;
while ($text =~ /(\@openqa:?\s+Clone\s+(https?:[^\s]+))/ig) {
push @urls, $2 if index($2, $expected_url) == 0;
}
return \@urls if @urls;
print 'No test cloned; the PR description does not contain '; # uncoverable statement
print "a command like '\@openqa: Clone $expected_url/tests/<JOB_ID>'.\n"; # uncoverable statement
exit 0;
}

sub _run_cmd ($name, $args, $capture_output = 0) {
my $res = $capture_output ? `$name $args` : system "$name $args";
if ($? == -1) { die "failed to execute '$name $args': $!\n" }
elsif ($? & 127) { die printf("'$name' received signal %d\n", $? & 127) }
elsif ($? >> 8) { die printf("'$name' exited with %d\n", $? >> 8) }
return $res;
}

sub _clone_job ($url) {
my $args = "$screts --json-output --skip-chained-deps --within-instance '$url' $vars";
my $json = _run_cmd 'openqa-clone-job', $args, 1;
return values %{decode_json($json)};
}

sub run () {
my $urls = _parse_urls($pr_body);
my @job_ids = map { _clone_job $_ } @$urls;
my @quoted_url_list = join(', ', map { "'$_'" } @$urls);
my @job_url_list = map { "- $expected_url/tests/$_\n" } @job_ids;
print "Cloned @quoted_url_list into:\n @job_url_list";
_run_cmd 'openqa-cli', "monitor --host '$expected_url' $screts @job_ids";
}

$ENV{TEST_MODE} ? 1 : run;
46 changes: 46 additions & 0 deletions test/05-clone-and-monitor.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env perl

use Mojo::Base -strict, -signatures;

use FindBin;
use Test::More;
use Test::Output qw(combined_like);
use Test::MockModule;

$ENV{TEST_MODE} = 1;
$ENV{GH_REPO} = 'foo';
$ENV{GH_REF} = 'bar';
$ENV{OPENQA_HOST} = 'http://127.0.0.1:9526';
$ENV{OPENQA_API_KEY} = 'key';
$ENV{OPENQA_API_SECRET} = 'secret';
$ENV{GITHUB_SERVER_URL} = 'gh-srv-url';
$ENV{GH_PR_BODY} = 'Merge my changes
@openqa: Clone http://127.0.0.1:9526/tests/4240
@openqa: Clone http://127.0.0.1:9526/tests/4239
footnote';

require "$FindBin::RealBin/../openqa-clone-and-monitor-job-from-pr";

my $mock = Test::MockModule->new('openqa_clone_and_monitor_job_from_pr', no_auto => 1);
my @invoked_commands;
my @clone_responses = (qq('{"4240" : 4246}'), qq('{"4239" : 4245}'));
$mock->redefine(_run_cmd => sub ($name, $args, $capture_output = 0) {
my $orig = $mock->original('_run_cmd');
my $resp = $name eq 'openqa-clone-job' ? shift @clone_responses : $args;
push @invoked_commands, "$name $args";
$orig->('echo', $resp, $capture_output);
});

combined_like { openqa_clone_and_monitor_job_from_pr::run() }
qr(Cloned.*4240.*4239.*into:.*monitor --host http://127\.0\.0\.1:9526 --apikey key --apisecret secret 4246 4245)s,
'monitor command invoked as expected';

my $expected_secrets = "--apikey 'key' --apisecret 'secret'";
my $expected_clone_options = "$expected_secrets --json-output --skip-chained-deps --within-instance";
my $expected_vars = "BUILD='foo.git#bar' _GROUP_ID='118' CASEDIR='gh-srv-url/foo.git#bar'";
my @expected_invocations;
push @expected_invocations, "openqa-clone-job $expected_clone_options 'http://127.0.0.1:9526/tests/$_' $expected_vars" for 4240, 4239;
push @expected_invocations, "openqa-cli monitor --host 'http://127.0.0.1:9526' $expected_secrets 4246 4245";
is_deeply \@invoked_commands, \@expected_invocations, 'expected commands invoked' or diag explain \@invoked_commands;

done_testing;

0 comments on commit eca5837

Please sign in to comment.