Dynamic RunCommand: validate that the job's out exists, is a file (or points to a file) which is executable.

This commit is contained in:
Graham Christensen 2021-12-14 22:07:15 -05:00
parent c2be27e82b
commit 1a30a0c2f1
3 changed files with 213 additions and 50 deletions

View file

@ -41,6 +41,32 @@ sub isBuildEligibleForDynamicRunCommand {
my ($build) = @_; my ($build) = @_;
if ($build->get_column("job") =~ "^runCommandHook\..+") { if ($build->get_column("job") =~ "^runCommandHook\..+") {
my $out = $build->buildoutputs->find({name => "out"});
if (!defined $out) {
warn "DynamicRunCommand hook on " . $build->job . " (" . $build->id . ") rejected: no output named 'out'.";
return 0;
}
my $path = $out->path;
if (-l $path) {
$path = readlink($path);
}
if (! -e $path) {
warn "DynamicRunCommand hook on " . $build->job . " (" . $build->id . ") rejected: The 'out' output doesn't exist locally. This is a bug.";
return 0;
}
if (! -x $path) {
warn "DynamicRunCommand hook on " . $build->job . " (" . $build->id . ") rejected: The 'out' output is not executable.";
return 0;
}
if (! -f $path) {
warn "DynamicRunCommand hook on " . $build->job . " (" . $build->id . ") rejected: The 'out' output is not a regular file or symlink.";
return 0;
}
return 1; return 1;
} }
@ -109,9 +135,7 @@ sub fanoutToCommands {
# missing test cases: # missing test cases:
# #
# 1. is it enabled on the jobset? # 1. is it enabled on the jobset?
# 2. what if the result is a directory? # 2. what if the build failed?
# 3. what if the job doens't have an out?
# 4. what if the build failed?
if (isBuildEligibleForDynamicRunCommand($build)) { if (isBuildEligibleForDynamicRunCommand($build)) {
my $job = $build->get_column('job'); my $job = $build->get_column('job');
my $out = $build->buildoutputs->find({name => "out"}); my $out = $build->buildoutputs->find({name => "out"});

View file

@ -93,10 +93,8 @@ subtest "fanoutToCommandsWithDynamicRunCommandSupport" => sub {
}; };
subtest "isBuildEligibleForDynamicRunCommand" => sub { subtest "isBuildEligibleForDynamicRunCommand" => sub {
my $build = Hydra::Schema::Result::Builds->new({ subtest "Non-matches based on name alone ..." => sub {
"job" => "foo bar baz" my $build = $builds->{"foo-bar-baz"};
});
is( is(
Hydra::Plugin::RunCommand::isBuildEligibleForDynamicRunCommand($build), Hydra::Plugin::RunCommand::isBuildEligibleForDynamicRunCommand($build),
0, 0,
@ -116,13 +114,51 @@ subtest "isBuildEligibleForDynamicRunCommand" => sub {
0, 0,
"The job name does not match" "The job name does not match"
); );
};
$build->set_column("job", "runCommandHook.a"); subtest "On outputs ..." => sub {
is( is(
Hydra::Plugin::RunCommand::isBuildEligibleForDynamicRunCommand($build), Hydra::Plugin::RunCommand::isBuildEligibleForDynamicRunCommand($builds->{"runCommandHook.example"}),
1, 1,
"The job name does match" "out is an executable file"
); );
is(
Hydra::Plugin::RunCommand::isBuildEligibleForDynamicRunCommand($builds->{"runCommandHook.symlink"}),
1,
"out is a symlink to an executable file"
);
is(
Hydra::Plugin::RunCommand::isBuildEligibleForDynamicRunCommand($builds->{"runCommandHook.no-out"}),
0,
"No output named out"
);
is(
Hydra::Plugin::RunCommand::isBuildEligibleForDynamicRunCommand($builds->{"runCommandHook.out-is-directory"}),
0,
"out is a directory"
);
is(
Hydra::Plugin::RunCommand::isBuildEligibleForDynamicRunCommand($builds->{"runCommandHook.out-is-not-executable-file"}),
0,
"out is a file which is not not executable"
);
is(
Hydra::Plugin::RunCommand::isBuildEligibleForDynamicRunCommand($builds->{"runCommandHook.symlink-non-executable"}),
0,
"out is a symlink to a non-executable file"
);
is(
Hydra::Plugin::RunCommand::isBuildEligibleForDynamicRunCommand($builds->{"runCommandHook.symlink-directory"}),
0,
"out is a symlink to a directory"
);
};
}; };

View file

@ -1,27 +1,130 @@
with import ./config.nix; with import ./config.nix;
{ rec {
runCommandHook.example = mkDerivation foo-bar-baz = mkDerivation {
{ name = "foo-bar-baz";
name = "my-build-product";
builder = "/bin/sh"; builder = "/bin/sh";
outputs = [ "out" "bin" ]; outputs = [ "out" ];
args = [ args = [
( (
builtins.toFile "builder.sh" '' builtins.toFile "builder.sh" ''
#! /bin/sh #! /bin/sh
echo "$PATH" touch $out
mkdir $bin
echo "foo" > $bin/bar
metrics=$out/nix-support/hydra-metrics
mkdir -p "$(dirname "$metrics")"
echo "lineCoverage 18 %" >> "$metrics"
echo "maxResident 27 KiB" >> "$metrics"
'' ''
) )
]; ];
}; };
runCommandHook.example = mkDerivation {
name = "my-build-product";
builder = "/bin/sh";
outputs = [ "out" ];
args = [
(
builtins.toFile "builder.sh" ''
#! /bin/sh
touch $out
chmod +x $out
# ... dunno ...
''
)
];
};
runCommandHook.symlink = mkDerivation {
name = "symlink-out";
builder = "/bin/sh";
outputs = [ "out" ];
args = [
(
builtins.toFile "builder.sh" ''
#! /bin/sh
ln -s $1 $out
''
)
runCommandHook.example
];
};
runCommandHook.no-out = mkDerivation {
name = "no-out";
builder = "/bin/sh";
outputs = [ "bin" ];
args = [
(
builtins.toFile "builder.sh" ''
#! /bin/sh
mkdir $bin
''
)
];
};
runCommandHook.out-is-directory = mkDerivation {
name = "out-is-directory";
builder = "/bin/sh";
outputs = [ "out" ];
args = [
(
builtins.toFile "builder.sh" ''
#! /bin/sh
mkdir $out
''
)
];
};
runCommandHook.out-is-not-executable-file = mkDerivation {
name = "out-is-directory";
builder = "/bin/sh";
outputs = [ "out" ];
args = [
(
builtins.toFile "builder.sh" ''
#! /bin/sh
touch $out
''
)
];
};
runCommandHook.symlink-non-executable = mkDerivation {
name = "symlink-out";
builder = "/bin/sh";
outputs = [ "out" ];
args = [
(
builtins.toFile "builder.sh" ''
#! /bin/sh
ln -s $1 $out
''
)
runCommandHook.out-is-not-executable-file
];
};
runCommandHook.symlink-directory = mkDerivation {
name = "symlink-directory";
builder = "/bin/sh";
outputs = [ "out" ];
args = [
(
builtins.toFile "builder.sh" ''
#! /bin/sh
ln -s $1 $out
''
)
runCommandHook.out-is-directory
];
};
} }