Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
257b211832 | ||
|
|
1cb1e139c4 | ||
|
|
6b97e3ab7b | ||
|
|
cad08f87d2 | ||
|
|
3fef32b364 | ||
|
|
ae18a7b3ae | ||
|
|
b657bcdfb7 | ||
|
|
3b4c4972c2 | ||
|
|
b2fe3f5218 | ||
|
|
9911f0107f | ||
|
|
feebb61897 | ||
|
|
4bcbed2f1b | ||
|
|
987dad3371 | ||
|
|
d2db3c7446 | ||
|
|
97dcdae068 | ||
|
|
9a5bd39d4c | ||
|
|
f1deb22c02 | ||
|
|
d22d030503 | ||
|
|
18c0d76210 | ||
|
|
4a4a0f901c | ||
|
|
881462bb4e | ||
|
|
af72b694d8 | ||
|
|
c92342d12f | ||
|
|
df07670a21 | ||
|
|
51944a5fa5 | ||
|
|
341b2f1309 | ||
|
|
4dc0f11379 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,6 +3,7 @@
|
||||
/src/sql/hydra-postgresql.sql
|
||||
/src/sql/hydra-sqlite.sql
|
||||
/src/sql/tmp.sqlite
|
||||
.hydra-data
|
||||
result
|
||||
result-*
|
||||
outputs
|
||||
|
||||
27
README.md
27
README.md
@@ -72,17 +72,16 @@ Make sure **State** at the top of the page is set to "_Enabled_" and click on "_
|
||||
You can build Hydra via `nix-build` using the provided [default.nix](./default.nix):
|
||||
|
||||
```
|
||||
$ nix-build
|
||||
$ nix build
|
||||
```
|
||||
|
||||
### Development Environment
|
||||
|
||||
You can use the provided shell.nix to get a working development environment:
|
||||
```
|
||||
$ nix-shell
|
||||
$ autoreconfPhase
|
||||
$ configurePhase # NOTE: not ./configure
|
||||
$ make
|
||||
$ nix develop
|
||||
$ mesonConfigurePhase
|
||||
$ ninja
|
||||
```
|
||||
|
||||
### Executing Hydra During Development
|
||||
@@ -91,9 +90,9 @@ When working on new features or bug fixes you need to be able to run Hydra from
|
||||
can be done using [foreman](https://github.com/ddollar/foreman):
|
||||
|
||||
```
|
||||
$ nix-shell
|
||||
$ nix develop
|
||||
$ # hack hack
|
||||
$ make
|
||||
$ ninja -C build
|
||||
$ foreman start
|
||||
```
|
||||
|
||||
@@ -115,22 +114,24 @@ Start by following the steps in [Development Environment](#development-environme
|
||||
Then, you can run the tests and the perlcritic linter together with:
|
||||
|
||||
```console
|
||||
$ nix-shell
|
||||
$ make check
|
||||
$ nix develop
|
||||
$ ninja -C build test
|
||||
```
|
||||
|
||||
You can run a single test with:
|
||||
|
||||
```
|
||||
$ nix-shell
|
||||
$ yath test ./t/foo/bar.t
|
||||
$ nix develop
|
||||
$ cd build
|
||||
$ meson test --test-args=../t/Hydra/Event.t testsuite
|
||||
```
|
||||
|
||||
And you can run just perlcritic with:
|
||||
|
||||
```
|
||||
$ nix-shell
|
||||
$ make perlcritic
|
||||
$ nix develop
|
||||
$ cd build
|
||||
$ meson test perlcritic
|
||||
```
|
||||
|
||||
### JSON API
|
||||
|
||||
@@ -11,12 +11,6 @@ $ cd hydra
|
||||
To enter a shell in which all environment variables (such as `PERL5LIB`)
|
||||
and dependencies can be found:
|
||||
|
||||
```console
|
||||
$ nix-shell
|
||||
```
|
||||
|
||||
of when flakes are enabled:
|
||||
|
||||
```console
|
||||
$ nix develop
|
||||
```
|
||||
@@ -24,15 +18,15 @@ $ nix develop
|
||||
To build Hydra, you should then do:
|
||||
|
||||
```console
|
||||
[nix-shell]$ autoreconfPhase
|
||||
[nix-shell]$ configurePhase
|
||||
[nix-shell]$ make -j$(nproc)
|
||||
$ mesonConfigurePhase
|
||||
$ ninja
|
||||
```
|
||||
|
||||
You start a local database, the webserver, and other components with
|
||||
foreman:
|
||||
|
||||
```console
|
||||
$ ninja -C build
|
||||
$ foreman start
|
||||
```
|
||||
|
||||
@@ -47,18 +41,11 @@ $ ./src/script/hydra-server
|
||||
You can run Hydra's test suite with the following:
|
||||
|
||||
```console
|
||||
[nix-shell]$ make check
|
||||
[nix-shell]$ # to run as many tests as you have cores:
|
||||
[nix-shell]$ make check YATH_JOB_COUNT=$NIX_BUILD_CORES
|
||||
[nix-shell]$ # or run yath directly:
|
||||
[nix-shell]$ yath test
|
||||
[nix-shell]$ # to run as many tests as you have cores:
|
||||
[nix-shell]$ yath test -j $NIX_BUILD_CORES
|
||||
$ meson test
|
||||
# to run as many tests as you have cores:
|
||||
$ YATH_JOB_COUNT=$NIX_BUILD_CORES meson test
|
||||
```
|
||||
|
||||
When using `yath` instead of `make check`, ensure you have run `make`
|
||||
in the root of the repository at least once.
|
||||
|
||||
**Warning**: Currently, the tests can fail
|
||||
if run with high parallelism [due to an issue in
|
||||
`Test::PostgreSQL`](https://github.com/TJC/Test-postgresql/issues/40)
|
||||
@@ -75,7 +62,7 @@ will reload the page every time you save.
|
||||
To build Hydra and its dependencies:
|
||||
|
||||
```console
|
||||
$ nix-build release.nix -A build.x86_64-linux
|
||||
$ nix build .#packages.x86_64-linux.default
|
||||
```
|
||||
|
||||
## Development Tasks
|
||||
|
||||
44
flake.lock
generated
44
flake.lock
generated
@@ -1,30 +1,10 @@
|
||||
{
|
||||
"nodes": {
|
||||
"libgit2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1715853528,
|
||||
"narHash": "sha256-J2rCxTecyLbbDdsyBWn9w7r3pbKRMkI9E7RvRgAqBdY=",
|
||||
"owner": "libgit2",
|
||||
"repo": "libgit2",
|
||||
"rev": "36f7e21ad757a3dacc58cf7944329da6bc1d6e96",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "libgit2",
|
||||
"ref": "v1.8.1",
|
||||
"repo": "libgit2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix": {
|
||||
"inputs": {
|
||||
"flake-compat": [],
|
||||
"flake-parts": [],
|
||||
"git-hooks-nix": [],
|
||||
"libgit2": [
|
||||
"libgit2"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
@@ -32,16 +12,16 @@
|
||||
"nixpkgs-regression": []
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1739390454,
|
||||
"narHash": "sha256-mIpJgIwPS4o4xYhN1B+/fHESEXoxpu6nVoZTzZ0MfTg=",
|
||||
"lastModified": 1744029599,
|
||||
"narHash": "sha256-u6RhBWQ1XohTZ4Ub5ml1PTcaxQgtqFNng6Sohy1rojw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nix",
|
||||
"rev": "d652513e4519ed4eb48c92f8670e5a71c7793fc3",
|
||||
"rev": "d0f98c76f962147610489e84c10033ca92e9c532",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "2.25-maintenance",
|
||||
"ref": "2.27-maintenance",
|
||||
"repo": "nix",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -49,38 +29,38 @@
|
||||
"nix-eval-jobs": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1739426028,
|
||||
"narHash": "sha256-1dZLPw+nlFQzzswfyTxW+8VF1AJ4ZvoYvLTjlHiz1SA=",
|
||||
"lastModified": 1744009874,
|
||||
"narHash": "sha256-PypQspB7h7EENe4RQQUQj2Ay8J1+O49AKNO9JbAU4Ek=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-eval-jobs",
|
||||
"rev": "6d4fd5a93d7bc953ffa4dcd6d53ad7056a71eff7",
|
||||
"rev": "62f9c9e8d00d2ff6ab27a6197ab459a8e0808e59",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"ref": "v2.27.0",
|
||||
"repo": "nix-eval-jobs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1726688310,
|
||||
"narHash": "sha256-Xc9lEtentPCEtxc/F1e6jIZsd4MPDYv4Kugl9WtXlz0=",
|
||||
"lastModified": 1739461644,
|
||||
"narHash": "sha256-1o1qR0KYozYGRrnqytSpAhVBYLNBHX+Lv6I39zGRzKM=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "dbebdd67a6006bb145d98c8debf9140ac7e651d0",
|
||||
"rev": "97a719c9f0a07923c957cf51b20b329f9fb9d43f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-24.05-small",
|
||||
"ref": "nixos-24.11-small",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"libgit2": "libgit2",
|
||||
"nix": "nix",
|
||||
"nix-eval-jobs": "nix-eval-jobs",
|
||||
"nixpkgs": "nixpkgs"
|
||||
|
||||
19
flake.nix
19
flake.nix
@@ -1,17 +1,11 @@
|
||||
{
|
||||
description = "A Nix-based continuous build system";
|
||||
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05-small";
|
||||
|
||||
inputs.libgit2 = {
|
||||
url = "github:libgit2/libgit2/v1.8.1";
|
||||
flake = false;
|
||||
};
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11-small";
|
||||
|
||||
inputs.nix = {
|
||||
url = "github:NixOS/nix/2.25-maintenance";
|
||||
url = "github:NixOS/nix/2.27-maintenance";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.libgit2.follows = "libgit2";
|
||||
|
||||
# hide nix dev tooling from our lock file
|
||||
inputs.flake-parts.follows = "";
|
||||
@@ -22,7 +16,7 @@
|
||||
};
|
||||
|
||||
inputs.nix-eval-jobs = {
|
||||
url = "github:nix-community/nix-eval-jobs";
|
||||
url = "github:nix-community/nix-eval-jobs/v2.27.0";
|
||||
# We want to control the deps precisely
|
||||
flake = false;
|
||||
};
|
||||
@@ -87,7 +81,12 @@
|
||||
inherit (nixpkgs.lib) fileset;
|
||||
inherit (self.packages.${system}) nix-eval-jobs;
|
||||
rawSrc = self;
|
||||
nix = nix.packages.${system}.nix;
|
||||
inherit (nix.packages.${system})
|
||||
nix-util
|
||||
nix-store
|
||||
nix-main
|
||||
nix-cli
|
||||
;
|
||||
nix-perl-bindings = nix.hydraJobs.perlBindings.${system};
|
||||
};
|
||||
default = self.packages.${system}.hydra;
|
||||
|
||||
14
meson.build
14
meson.build
@@ -8,22 +8,22 @@ project('hydra', 'cpp',
|
||||
],
|
||||
)
|
||||
|
||||
nix_util_dep = dependency('nix-util', required: true)
|
||||
nix_store_dep = dependency('nix-store', required: true)
|
||||
nix_main_dep = dependency('nix-main', required: true)
|
||||
nix_expr_dep = dependency('nix-expr', required: true)
|
||||
nix_flake_dep = dependency('nix-flake', required: true)
|
||||
nix_cmd_dep = dependency('nix-cmd', required: true)
|
||||
|
||||
# Nix need extra flags not provided in its pkg-config files.
|
||||
nix_dep = declare_dependency(
|
||||
dependencies: [
|
||||
nix_util_dep,
|
||||
nix_store_dep,
|
||||
nix_main_dep,
|
||||
nix_expr_dep,
|
||||
nix_flake_dep,
|
||||
nix_cmd_dep,
|
||||
],
|
||||
compile_args: ['-include', 'nix/config.h'],
|
||||
compile_args: [
|
||||
'-include', 'nix/config-util.hh',
|
||||
'-include', 'nix/config-store.hh',
|
||||
'-include', 'nix/config-main.hh',
|
||||
],
|
||||
)
|
||||
|
||||
pqxx_dep = dependency('libpqxx', required: true)
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
systemd.services.hydra-send-stats.enable = false;
|
||||
|
||||
services.postgresql.enable = true;
|
||||
services.postgresql.package = pkgs.postgresql_12;
|
||||
|
||||
# The following is to work around the following error from hydra-server:
|
||||
# [error] Caught exception in engine "Cannot determine local time zone"
|
||||
|
||||
@@ -468,7 +468,7 @@ in
|
||||
elif [[ $compression == zstd ]]; then
|
||||
compression="zstd --rm"
|
||||
fi
|
||||
find ${baseDir}/build-logs -type f -name "*.drv" -mtime +3 -size +0c | xargs -r "$compression" --force --quiet
|
||||
find ${baseDir}/build-logs -ignore_readdir_race -type f -name "*.drv" -mtime +3 -size +0c | xargs -r "$compression" --force --quiet
|
||||
'';
|
||||
startAt = "Sun 01:45";
|
||||
};
|
||||
|
||||
20
package.nix
20
package.nix
@@ -8,7 +8,10 @@
|
||||
|
||||
, perlPackages
|
||||
|
||||
, nix
|
||||
, nix-util
|
||||
, nix-store
|
||||
, nix-main
|
||||
, nix-cli
|
||||
, nix-perl-bindings
|
||||
, git
|
||||
|
||||
@@ -162,7 +165,7 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
nukeReferences
|
||||
pkg-config
|
||||
mdbook
|
||||
nix
|
||||
nix-cli
|
||||
perlDeps
|
||||
perl
|
||||
unzip
|
||||
@@ -172,7 +175,9 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
libpqxx
|
||||
openssl
|
||||
libxslt
|
||||
nix
|
||||
nix-util
|
||||
nix-store
|
||||
nix-main
|
||||
perlDeps
|
||||
perl
|
||||
boost
|
||||
@@ -199,13 +204,14 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
glibcLocales
|
||||
libressl.nc
|
||||
python3
|
||||
nix-cli
|
||||
];
|
||||
|
||||
hydraPath = lib.makeBinPath (
|
||||
[
|
||||
subversion
|
||||
openssh
|
||||
nix
|
||||
nix-cli
|
||||
coreutils
|
||||
findutils
|
||||
pixz
|
||||
@@ -235,7 +241,7 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
shellHook = ''
|
||||
pushd $(git rev-parse --show-toplevel) >/dev/null
|
||||
|
||||
PATH=$(pwd)/src/hydra-evaluator:$(pwd)/src/script:$(pwd)/src/hydra-queue-runner:$PATH
|
||||
PATH=$(pwd)/build/src/hydra-evaluator:$(pwd)/build/src/script:$(pwd)/build/src/hydra-queue-runner:$PATH
|
||||
PERL5LIB=$(pwd)/src/lib:$PERL5LIB
|
||||
export HYDRA_HOME="$(pwd)/src/"
|
||||
mkdir -p .hydra-data
|
||||
@@ -266,7 +272,7 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
--prefix PATH ':' $out/bin:$hydraPath \
|
||||
--set HYDRA_RELEASE ${version} \
|
||||
--set HYDRA_HOME $out/libexec/hydra \
|
||||
--set NIX_RELEASE ${nix.name or "unknown"} \
|
||||
--set NIX_RELEASE ${nix-cli.name or "unknown"} \
|
||||
--set NIX_EVAL_JOBS_RELEASE ${nix-eval-jobs.name or "unknown"}
|
||||
done
|
||||
'';
|
||||
@@ -274,5 +280,5 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
dontStrip = true;
|
||||
|
||||
meta.description = "Build of Hydra on ${stdenv.system}";
|
||||
passthru = { inherit perlDeps nix; };
|
||||
passthru = { inherit perlDeps; };
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "state.hh"
|
||||
#include "hydra-build-result.hh"
|
||||
#include "globals.hh"
|
||||
#include "parsed-derivations.hh"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
@@ -463,14 +464,17 @@ Step::ptr State::createStep(ref<Store> destStore,
|
||||
it's not runnable yet, and other threads won't make it
|
||||
runnable while step->created == false. */
|
||||
step->drv = std::make_unique<Derivation>(localStore->readDerivation(drvPath));
|
||||
step->parsedDrv = std::make_unique<ParsedDerivation>(drvPath, *step->drv);
|
||||
{
|
||||
auto parsedDrv = ParsedDerivation{drvPath, *step->drv};
|
||||
step->drvOptions = std::make_unique<DerivationOptions>(DerivationOptions::fromParsedDerivation(parsedDrv));
|
||||
}
|
||||
|
||||
step->preferLocalBuild = step->parsedDrv->willBuildLocally(*localStore);
|
||||
step->preferLocalBuild = step->drvOptions->willBuildLocally(*localStore, *step->drv);
|
||||
step->isDeterministic = getOr(step->drv->env, "isDetermistic", "0") == "1";
|
||||
|
||||
step->systemType = step->drv->platform;
|
||||
{
|
||||
StringSet features = step->requiredSystemFeatures = step->parsedDrv->getRequiredSystemFeatures();
|
||||
StringSet features = step->requiredSystemFeatures = step->drvOptions->getRequiredSystemFeatures(*step->drv);
|
||||
if (step->preferLocalBuild)
|
||||
features.insert("local");
|
||||
if (!features.empty()) {
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
|
||||
#include "db.hh"
|
||||
|
||||
#include "parsed-derivations.hh"
|
||||
#include "derivations.hh"
|
||||
#include "derivation-options.hh"
|
||||
#include "pathlocks.hh"
|
||||
#include "pool.hh"
|
||||
#include "build-result.hh"
|
||||
@@ -167,7 +168,7 @@ struct Step
|
||||
|
||||
nix::StorePath drvPath;
|
||||
std::unique_ptr<nix::Derivation> drv;
|
||||
std::unique_ptr<nix::ParsedDerivation> parsedDrv;
|
||||
std::unique_ptr<nix::DerivationOptions> drvOptions;
|
||||
std::set<std::string> requiredSystemFeatures;
|
||||
bool preferLocalBuild;
|
||||
bool isDeterministic;
|
||||
|
||||
@@ -6,6 +6,7 @@ use base 'Catalyst::View::TT';
|
||||
use Template::Plugin::HTML;
|
||||
use Hydra::Helper::Nix;
|
||||
use Time::Seconds;
|
||||
use Digest::SHA qw(sha1_hex);
|
||||
|
||||
__PACKAGE__->config(
|
||||
TEMPLATE_EXTENSION => '.tt',
|
||||
@@ -25,8 +26,14 @@ __PACKAGE__->config(
|
||||
makeNameTextForJobset
|
||||
relativeDuration
|
||||
stripSSHUser
|
||||
metricDivId
|
||||
/]);
|
||||
|
||||
sub metricDivId {
|
||||
my ($self, $c, $text) = @_;
|
||||
return "metric-" . sha1_hex($text);
|
||||
}
|
||||
|
||||
sub buildLogExists {
|
||||
my ($self, $c, $build) = @_;
|
||||
return 1 if defined $c->config->{log_prefix};
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
|
||||
<h3>Metric: <a [% HTML.attributes(href => c.uri_for('/job' project.name jobset.name job 'metric' metric.name)) %]><tt>[%HTML.escape(metric.name)%]</tt></a></h3>
|
||||
|
||||
[% id = "metric-" _ metric.name;
|
||||
id = id.replace('\.', '_');
|
||||
[% id = metricDivId(metric.name);
|
||||
INCLUDE createChart dataUrl=c.uri_for('/job' project.name jobset.name job 'metric' metric.name); %]
|
||||
|
||||
[% END %]
|
||||
|
||||
@@ -773,6 +773,9 @@ sub checkJobsetWrapped {
|
||||
my $jobsetChanged = 0;
|
||||
my %buildMap;
|
||||
|
||||
my @jobs;
|
||||
push @jobs, $_ while defined($_ = $jobsIter->());
|
||||
|
||||
$db->txn_do(sub {
|
||||
my $prevEval = getPrevJobsetEval($db, $jobset, 1);
|
||||
|
||||
@@ -796,7 +799,7 @@ sub checkJobsetWrapped {
|
||||
|
||||
my @jobsWithConstituents;
|
||||
|
||||
while (defined(my $job = $jobsIter->())) {
|
||||
foreach my $job (@jobs) {
|
||||
if ($jobsetsJobset) {
|
||||
die "The .jobsets jobset must only have a single job named 'jobsets'"
|
||||
unless $job->{attr} eq "jobsets";
|
||||
|
||||
@@ -6,27 +6,55 @@ use Hydra::Helper::Exec;
|
||||
|
||||
my $ctx = test_context();
|
||||
|
||||
my $jobsetCtx = $ctx->makeJobset(
|
||||
expression => 'constituents-broken.nix',
|
||||
);
|
||||
my $jobset = $jobsetCtx->{"jobset"};
|
||||
subtest "broken constituents expression" => sub {
|
||||
my $jobsetCtx = $ctx->makeJobset(
|
||||
expression => 'constituents-broken.nix',
|
||||
);
|
||||
my $jobset = $jobsetCtx->{"jobset"};
|
||||
|
||||
my ($res, $stdout, $stderr) = captureStdoutStderr(60,
|
||||
("hydra-eval-jobset", $jobsetCtx->{"project"}->name, $jobset->name)
|
||||
);
|
||||
isnt($res, 0, "hydra-eval-jobset exits non-zero");
|
||||
ok(utf8::decode($stderr), "Stderr output is UTF8-clean");
|
||||
like(
|
||||
$stderr,
|
||||
qr/aggregate job ‘mixed_aggregate’ failed with the error: "constituentA": does not exist/,
|
||||
"The stderr record includes a relevant error message"
|
||||
);
|
||||
my ($res, $stdout, $stderr) = captureStdoutStderr(60,
|
||||
("hydra-eval-jobset", $jobsetCtx->{"project"}->name, $jobset->name)
|
||||
);
|
||||
isnt($res, 0, "hydra-eval-jobset exits non-zero");
|
||||
ok(utf8::decode($stderr), "Stderr output is UTF8-clean");
|
||||
like(
|
||||
$stderr,
|
||||
qr/aggregate job 'mixed_aggregate' references non-existent job 'constituentA'/,
|
||||
"The stderr record includes a relevant error message"
|
||||
);
|
||||
|
||||
$jobset->discard_changes({ '+columns' => {'errormsg' => 'errormsg'} }); # refresh from DB
|
||||
like(
|
||||
$jobset->errormsg,
|
||||
qr/aggregate job ‘mixed_aggregate’ failed with the error: "constituentA": does not exist/,
|
||||
"The jobset records a relevant error message"
|
||||
);
|
||||
$jobset->discard_changes({ '+columns' => {'errormsg' => 'errormsg'} }); # refresh from DB
|
||||
like(
|
||||
$jobset->errormsg,
|
||||
qr/aggregate job ‘mixed_aggregate’ failed with the error: constituentA: does not exist/,
|
||||
"The jobset records a relevant error message"
|
||||
);
|
||||
};
|
||||
|
||||
subtest "no matches" => sub {
|
||||
my $jobsetCtx = $ctx->makeJobset(
|
||||
expression => 'constituents-no-matches.nix',
|
||||
);
|
||||
my $jobset = $jobsetCtx->{"jobset"};
|
||||
|
||||
my ($res, $stdout, $stderr) = captureStdoutStderr(60,
|
||||
("hydra-eval-jobset", $jobsetCtx->{"project"}->name, $jobset->name)
|
||||
);
|
||||
isnt($res, 0, "hydra-eval-jobset exits non-zero");
|
||||
ok(utf8::decode($stderr), "Stderr output is UTF8-clean");
|
||||
like(
|
||||
$stderr,
|
||||
qr/aggregate job 'non_match_aggregate' references constituent glob pattern 'tests\.\*' with no matches/,
|
||||
"The stderr record includes a relevant error message"
|
||||
);
|
||||
|
||||
$jobset->discard_changes({ '+columns' => {'errormsg' => 'errormsg'} }); # refresh from DB
|
||||
like(
|
||||
$jobset->errormsg,
|
||||
qr/aggregate job ‘non_match_aggregate’ failed with the error: tests\.\*: constituent glob pattern had no matches/,
|
||||
qr/in job ‘non_match_aggregate’:\ntests\.\*: constituent glob pattern had no matches/,
|
||||
"The jobset records a relevant error message"
|
||||
);
|
||||
};
|
||||
|
||||
done_testing;
|
||||
|
||||
138
t/evaluator/evaluate-constituents-globbing.t
Normal file
138
t/evaluator/evaluate-constituents-globbing.t
Normal file
@@ -0,0 +1,138 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
use Setup;
|
||||
use Test2::V0;
|
||||
use Hydra::Helper::Exec;
|
||||
use Data::Dumper;
|
||||
|
||||
my $ctx = test_context();
|
||||
|
||||
subtest "general glob testing" => sub {
|
||||
my $jobsetCtx = $ctx->makeJobset(
|
||||
expression => 'constituents-glob.nix',
|
||||
);
|
||||
my $jobset = $jobsetCtx->{"jobset"};
|
||||
|
||||
my ($res, $stdout, $stderr) = captureStdoutStderr(60,
|
||||
("hydra-eval-jobset", $jobsetCtx->{"project"}->name, $jobset->name)
|
||||
);
|
||||
is($res, 0, "hydra-eval-jobset exits zero");
|
||||
|
||||
my $builds = {};
|
||||
for my $build ($jobset->builds) {
|
||||
$builds->{$build->job} = $build;
|
||||
}
|
||||
|
||||
subtest "basic globbing works" => sub {
|
||||
ok(defined $builds->{"ok_aggregate"}, "'ok_aggregate' is part of the jobset evaluation");
|
||||
my @constituents = $builds->{"ok_aggregate"}->constituents->all;
|
||||
is(2, scalar @constituents, "'ok_aggregate' has two constituents");
|
||||
|
||||
my @sortedConstituentNames = sort (map { $_->nixname } @constituents);
|
||||
|
||||
is($sortedConstituentNames[0], "empty-dir-A", "first constituent of 'ok_aggregate' is 'empty-dir-A'");
|
||||
is($sortedConstituentNames[1], "empty-dir-B", "second constituent of 'ok_aggregate' is 'empty-dir-B'");
|
||||
};
|
||||
|
||||
subtest "transitivity is OK" => sub {
|
||||
ok(defined $builds->{"indirect_aggregate"}, "'indirect_aggregate' is part of the jobset evaluation");
|
||||
my @constituents = $builds->{"indirect_aggregate"}->constituents->all;
|
||||
is(1, scalar @constituents, "'indirect_aggregate' has one constituent");
|
||||
is($constituents[0]->nixname, "direct_aggregate", "'indirect_aggregate' has 'direct_aggregate' as single constituent");
|
||||
};
|
||||
};
|
||||
|
||||
subtest "* selects all except current aggregate" => sub {
|
||||
my $jobsetCtx = $ctx->makeJobset(
|
||||
expression => 'constituents-glob-all.nix',
|
||||
);
|
||||
my $jobset = $jobsetCtx->{"jobset"};
|
||||
|
||||
my ($res, $stdout, $stderr) = captureStdoutStderr(60,
|
||||
("hydra-eval-jobset", $jobsetCtx->{"project"}->name, $jobset->name)
|
||||
);
|
||||
|
||||
subtest "no eval errors" => sub {
|
||||
ok(utf8::decode($stderr), "Stderr output is UTF8-clean");
|
||||
ok(
|
||||
$stderr !~ "aggregate job ‘ok_aggregate’ has a constituent .* that doesn't correspond to a Hydra build",
|
||||
"Catchall wildcard must not select itself as constituent"
|
||||
);
|
||||
|
||||
$jobset->discard_changes; # refresh from DB
|
||||
is(
|
||||
$jobset->errormsg,
|
||||
"",
|
||||
"eval-errors non-empty"
|
||||
);
|
||||
};
|
||||
|
||||
my $builds = {};
|
||||
for my $build ($jobset->builds) {
|
||||
$builds->{$build->job} = $build;
|
||||
}
|
||||
|
||||
subtest "two constituents" => sub {
|
||||
ok(defined $builds->{"ok_aggregate"}, "'ok_aggregate' is part of the jobset evaluation");
|
||||
my @constituents = $builds->{"ok_aggregate"}->constituents->all;
|
||||
is(2, scalar @constituents, "'ok_aggregate' has two constituents");
|
||||
|
||||
my @sortedConstituentNames = sort (map { $_->nixname } @constituents);
|
||||
|
||||
is($sortedConstituentNames[0], "empty-dir-A", "first constituent of 'ok_aggregate' is 'empty-dir-A'");
|
||||
is($sortedConstituentNames[1], "empty-dir-B", "second constituent of 'ok_aggregate' is 'empty-dir-B'");
|
||||
};
|
||||
};
|
||||
|
||||
subtest "trivial cycle check" => sub {
|
||||
my $jobsetCtx = $ctx->makeJobset(
|
||||
expression => 'constituents-cycle.nix',
|
||||
);
|
||||
my $jobset = $jobsetCtx->{"jobset"};
|
||||
|
||||
my ($res, $stdout, $stderr) = captureStdoutStderr(60,
|
||||
("hydra-eval-jobset", $jobsetCtx->{"project"}->name, $jobset->name)
|
||||
);
|
||||
|
||||
ok(
|
||||
$stderr =~ "Found dependency cycle between jobs 'indirect_aggregate' and 'ok_aggregate'",
|
||||
"Dependency cycle error is on stderr"
|
||||
);
|
||||
|
||||
ok(utf8::decode($stderr), "Stderr output is UTF8-clean");
|
||||
|
||||
$jobset->discard_changes; # refresh from DB
|
||||
like(
|
||||
$jobset->errormsg,
|
||||
qr/Dependency cycle: indirect_aggregate <-> ok_aggregate/,
|
||||
"eval-errors non-empty"
|
||||
);
|
||||
|
||||
is(0, $jobset->builds->count, "No builds should be scheduled");
|
||||
};
|
||||
|
||||
subtest "cycle check with globbing" => sub {
|
||||
my $jobsetCtx = $ctx->makeJobset(
|
||||
expression => 'constituents-cycle-glob.nix',
|
||||
);
|
||||
my $jobset = $jobsetCtx->{"jobset"};
|
||||
|
||||
my ($res, $stdout, $stderr) = captureStdoutStderr(60,
|
||||
("hydra-eval-jobset", $jobsetCtx->{"project"}->name, $jobset->name)
|
||||
);
|
||||
|
||||
ok(utf8::decode($stderr), "Stderr output is UTF8-clean");
|
||||
|
||||
$jobset->discard_changes; # refresh from DB
|
||||
like(
|
||||
$jobset->errormsg,
|
||||
qr/aggregate job ‘indirect_aggregate’ failed with the error: Dependency cycle: indirect_aggregate <-> packages.constituentA/,
|
||||
"packages.constituentA error missing"
|
||||
);
|
||||
|
||||
# on this branch of Hydra, hydra-eval-jobset fails hard if an aggregate
|
||||
# job is broken.
|
||||
is(0, $jobset->builds->count, "Zero jobs are scheduled");
|
||||
};
|
||||
|
||||
done_testing;
|
||||
14
t/jobs/config.nix
Normal file
14
t/jobs/config.nix
Normal file
@@ -0,0 +1,14 @@
|
||||
rec {
|
||||
path = "/nix/store/l9mg93sgx50y88p5rr6x1vib6j1rjsds-coreutils-9.1/bin";
|
||||
|
||||
mkDerivation = args:
|
||||
derivation ({
|
||||
system = builtins.currentSystem;
|
||||
PATH = path;
|
||||
} // args);
|
||||
mkContentAddressedDerivation = args: mkDerivation ({
|
||||
__contentAddressed = true;
|
||||
outputHashMode = "recursive";
|
||||
outputHashAlgo = "sha256";
|
||||
} // args);
|
||||
}
|
||||
34
t/jobs/constituents-cycle-glob.nix
Normal file
34
t/jobs/constituents-cycle-glob.nix
Normal file
@@ -0,0 +1,34 @@
|
||||
with import ./config.nix;
|
||||
{
|
||||
packages.constituentA = mkDerivation {
|
||||
name = "empty-dir-A";
|
||||
builder = ./empty-dir-builder.sh;
|
||||
_hydraAggregate = true;
|
||||
_hydraGlobConstituents = true;
|
||||
constituents = [ "*_aggregate" ];
|
||||
};
|
||||
|
||||
packages.constituentB = mkDerivation {
|
||||
name = "empty-dir-B";
|
||||
builder = ./empty-dir-builder.sh;
|
||||
};
|
||||
|
||||
ok_aggregate = mkDerivation {
|
||||
name = "direct_aggregate";
|
||||
_hydraAggregate = true;
|
||||
_hydraGlobConstituents = true;
|
||||
constituents = [
|
||||
"packages.*"
|
||||
];
|
||||
builder = ./empty-dir-builder.sh;
|
||||
};
|
||||
|
||||
indirect_aggregate = mkDerivation {
|
||||
name = "indirect_aggregate";
|
||||
_hydraAggregate = true;
|
||||
constituents = [
|
||||
"ok_aggregate"
|
||||
];
|
||||
builder = ./empty-dir-builder.sh;
|
||||
};
|
||||
}
|
||||
21
t/jobs/constituents-cycle.nix
Normal file
21
t/jobs/constituents-cycle.nix
Normal file
@@ -0,0 +1,21 @@
|
||||
with import ./config.nix;
|
||||
{
|
||||
ok_aggregate = mkDerivation {
|
||||
name = "direct_aggregate";
|
||||
_hydraAggregate = true;
|
||||
_hydraGlobConstituents = true;
|
||||
constituents = [
|
||||
"indirect_aggregate"
|
||||
];
|
||||
builder = ./empty-dir-builder.sh;
|
||||
};
|
||||
|
||||
indirect_aggregate = mkDerivation {
|
||||
name = "indirect_aggregate";
|
||||
_hydraAggregate = true;
|
||||
constituents = [
|
||||
"ok_aggregate"
|
||||
];
|
||||
builder = ./empty-dir-builder.sh;
|
||||
};
|
||||
}
|
||||
22
t/jobs/constituents-glob-all.nix
Normal file
22
t/jobs/constituents-glob-all.nix
Normal file
@@ -0,0 +1,22 @@
|
||||
with import ./config.nix;
|
||||
{
|
||||
packages.constituentA = mkDerivation {
|
||||
name = "empty-dir-A";
|
||||
builder = ./empty-dir-builder.sh;
|
||||
};
|
||||
|
||||
packages.constituentB = mkDerivation {
|
||||
name = "empty-dir-B";
|
||||
builder = ./empty-dir-builder.sh;
|
||||
};
|
||||
|
||||
ok_aggregate = mkDerivation {
|
||||
name = "direct_aggregate";
|
||||
_hydraAggregate = true;
|
||||
_hydraGlobConstituents = true;
|
||||
constituents = [
|
||||
"*"
|
||||
];
|
||||
builder = ./empty-dir-builder.sh;
|
||||
};
|
||||
}
|
||||
31
t/jobs/constituents-glob.nix
Normal file
31
t/jobs/constituents-glob.nix
Normal file
@@ -0,0 +1,31 @@
|
||||
with import ./config.nix;
|
||||
{
|
||||
packages.constituentA = mkDerivation {
|
||||
name = "empty-dir-A";
|
||||
builder = ./empty-dir-builder.sh;
|
||||
};
|
||||
|
||||
packages.constituentB = mkDerivation {
|
||||
name = "empty-dir-B";
|
||||
builder = ./empty-dir-builder.sh;
|
||||
};
|
||||
|
||||
ok_aggregate = mkDerivation {
|
||||
name = "direct_aggregate";
|
||||
_hydraAggregate = true;
|
||||
_hydraGlobConstituents = true;
|
||||
constituents = [
|
||||
"packages.*"
|
||||
];
|
||||
builder = ./empty-dir-builder.sh;
|
||||
};
|
||||
|
||||
indirect_aggregate = mkDerivation {
|
||||
name = "indirect_aggregate";
|
||||
_hydraAggregate = true;
|
||||
constituents = [
|
||||
"ok_aggregate"
|
||||
];
|
||||
builder = ./empty-dir-builder.sh;
|
||||
};
|
||||
}
|
||||
20
t/jobs/constituents-no-matches.nix
Normal file
20
t/jobs/constituents-no-matches.nix
Normal file
@@ -0,0 +1,20 @@
|
||||
with import ./config.nix;
|
||||
{
|
||||
non_match_aggregate = mkDerivation {
|
||||
name = "mixed_aggregate";
|
||||
_hydraAggregate = true;
|
||||
_hydraGlobConstituents = true;
|
||||
constituents = [
|
||||
"tests.*"
|
||||
];
|
||||
builder = ./empty-dir-builder.sh;
|
||||
};
|
||||
|
||||
# Without a second job no jobset is attempted to be created
|
||||
# (the only job would be broken)
|
||||
# and thus the constituent validation is never reached.
|
||||
dummy = mkDerivation {
|
||||
name = "dummy";
|
||||
builder = ./empty-dir-builder.sh;
|
||||
};
|
||||
}
|
||||
24
t/jobs/declarative/project.json
Normal file
24
t/jobs/declarative/project.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"enabled": 1,
|
||||
"hidden": false,
|
||||
"description": "declarative-jobset-example",
|
||||
"nixexprinput": "src",
|
||||
"nixexprpath": "declarative/generator.nix",
|
||||
"checkinterval": 300,
|
||||
"schedulingshares": 100,
|
||||
"enableemail": false,
|
||||
"emailoverride": "",
|
||||
"keepnr": 3,
|
||||
"inputs": {
|
||||
"src": {
|
||||
"type": "path",
|
||||
"value": "/home/ma27/Projects/hydra-cppnix/t/jobs",
|
||||
"emailresponsible": false
|
||||
},
|
||||
"jobspath": {
|
||||
"type": "string",
|
||||
"value": "/home/ma27/Projects/hydra-cppnix/t/jobs",
|
||||
"emailresponsible": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ testenv.prepend('PERL5LIB',
|
||||
separator: ':'
|
||||
)
|
||||
testenv.prepend('PATH',
|
||||
fs.parent(find_program('nix').full_path()),
|
||||
fs.parent(hydra_evaluator.full_path()),
|
||||
fs.parent(hydra_queue_runner.full_path()),
|
||||
meson.project_source_root() / 'src/script',
|
||||
|
||||
@@ -22,11 +22,11 @@ is(nrQueuedBuildsForJobset($jobset), 0, "Evaluating jobs/broken-constituent.nix
|
||||
|
||||
like(
|
||||
$jobset->errormsg,
|
||||
qr/^"does-not-exist": does not exist$/m,
|
||||
qr/^does-not-exist: does not exist$/m,
|
||||
"Evaluating jobs/broken-constituent.nix should log an error for does-not-exist");
|
||||
like(
|
||||
$jobset->errormsg,
|
||||
qr/^"does-not-evaluate": "error: assertion 'false' failed/m,
|
||||
qr/^does-not-evaluate: error: assertion 'false' failed/m,
|
||||
"Evaluating jobs/broken-constituent.nix should log an error for does-not-evaluate");
|
||||
|
||||
done_testing;
|
||||
|
||||
Reference in New Issue
Block a user