From 878c0f240e950c4d7bf8ebc60299bc0db80e6686 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 30 Jan 2024 13:50:25 -0500 Subject: [PATCH 1/8] Switch (back) to Nix master MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-creating `nix-next` after using it in #1354. Flake lock file updates: • Updated input 'nix': 'github:NixOS/nix/8df68a213fc52a57b02a57005b0e06cc8de40ce3' (2024-01-25) → 'github:NixOS/nix/75ebb90a70f6320c1c7a1fca87a0a8adb0716143' (2024-01-30) --- flake.lock | 7 +++---- flake.nix | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/flake.lock b/flake.lock index d8b2e4a94..f04d657b0 100644 --- a/flake.lock +++ b/flake.lock @@ -42,16 +42,15 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1706637536, - "narHash": "sha256-fjx+nCOzuSxGWfhwWWc8hCsLFZAjZLDDUcbBtldRqbk=", + "lastModified": 1706629374, + "narHash": "sha256-KyAiLGxJ39fSY0cuq8EWAZQ4vaDdqAItSRP4+vjYvq8=", "owner": "NixOS", "repo": "nix", - "rev": "8f42912c80c0a03f62f6a3d28a3af05a9762565d", + "rev": "75ebb90a70f6320c1c7a1fca87a0a8adb0716143", "type": "github" }, "original": { "owner": "NixOS", - "ref": "2.20-maintenance", "repo": "nix", "type": "github" } diff --git a/flake.nix b/flake.nix index 306ed292b..4dc7d25ea 100644 --- a/flake.nix +++ b/flake.nix @@ -2,7 +2,7 @@ description = "A Nix-based continuous build system"; inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05-small"; - inputs.nix.url = "github:NixOS/nix/2.20-maintenance"; + inputs.nix.url = "github:NixOS/nix"; inputs.nix.inputs.nixpkgs.follows = "nixpkgs"; # TODO get rid of this once https://github.com/NixOS/nix/pull/9546 is From e499509595859d670432c0d98162119ecd552666 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Mon, 12 Feb 2024 17:12:49 +0100 Subject: [PATCH 2/8] Switch to new Nix bindings, update Nix for that Implements support for Nix's new Perl bindings[1]. The current state basically does `openStore()`, but always uses `auto` and doesn't support stores at other URIs. Even though the stores are cached inside the Perl implementation, I decided to instantiate those once in the Nix helper module. That way store openings aren't cluttered across the entire codebase. Also, there are two stores used later on - MACHINE_LOCAL_STORE for `auto`, BINARY_CACHE_STORE for the one from `store_uri` in `hydra.conf` - and using consistent names should make the intent clearer then. This doesn't contain any behavioral changes, i.e. the build product availability issue from #1352 isn't fixed. This patch only contains the migration to the new API. [1] https://github.com/NixOS/nix/pull/9863 --- flake.lock | 6 +++--- src/hydra-eval-jobs/hydra-eval-jobs.cc | 6 +++--- src/hydra-evaluator/hydra-evaluator.cc | 2 +- src/hydra-queue-runner/queue-monitor.cc | 2 +- src/lib/Hydra/Base/Controller/NixChannel.pm | 3 +-- src/lib/Hydra/Controller/Build.pm | 18 ++++++++---------- src/lib/Hydra/Controller/Root.pm | 2 +- src/lib/Hydra/Helper/Nix.pm | 5 ++++- src/lib/Hydra/Plugin/BazaarInput.pm | 7 +++---- src/lib/Hydra/Plugin/DarcsInput.pm | 7 +++---- src/lib/Hydra/Plugin/GitInput.pm | 6 +++--- src/lib/Hydra/Plugin/MercurialInput.pm | 7 +++---- src/lib/Hydra/Plugin/PathInput.pm | 5 ++--- src/lib/Hydra/Plugin/SubversionInput.pm | 9 ++++----- src/lib/Hydra/View/NARInfo.pm | 9 ++++----- src/script/hydra-eval-jobset | 8 ++++---- src/script/hydra-update-gc-roots | 5 ++--- t/s3-backup-test.pl | 1 - 18 files changed, 50 insertions(+), 58 deletions(-) diff --git a/flake.lock b/flake.lock index f04d657b0..4b4f00767 100644 --- a/flake.lock +++ b/flake.lock @@ -42,11 +42,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1706629374, - "narHash": "sha256-KyAiLGxJ39fSY0cuq8EWAZQ4vaDdqAItSRP4+vjYvq8=", + "lastModified": 1707750141, + "narHash": "sha256-9qSzGQs/Rjf2i3UQjyaZUznZzYDHkLYro/1FTT1easg=", "owner": "NixOS", "repo": "nix", - "rev": "75ebb90a70f6320c1c7a1fca87a0a8adb0716143", + "rev": "c4ebb82da4eade975e874da600dc50e9dec610cb", "type": "github" }, "original": { diff --git a/src/hydra-eval-jobs/hydra-eval-jobs.cc b/src/hydra-eval-jobs/hydra-eval-jobs.cc index 1d7de74b5..d56197198 100644 --- a/src/hydra-eval-jobs/hydra-eval-jobs.cc +++ b/src/hydra-eval-jobs/hydra-eval-jobs.cc @@ -185,7 +185,7 @@ static void worker( !experimentalFeatureSettings.isEnabled(Xp::CaDerivations)); if (drv->querySystem() == "unknown") - throw EvalError("derivation must have a 'system' attribute"); + state.error("derivation must have a 'system' attribute").debugThrow(); auto drvPath = state.store->printStorePath(drv->requireDrvPath()); @@ -208,7 +208,7 @@ static void worker( if (a && state.forceBool(*a->value, a->pos, "while evaluating the `_hydraAggregate` attribute")) { auto a = v->attrs->get(state.symbols.create("constituents")); if (!a) - throw EvalError("derivation must have a ‘constituents’ attribute"); + state.error("derivation must have a ‘constituents’ attribute").debugThrow(); NixStringContext context; state.coerceToString(a->pos, *a->value, context, "while evaluating the `constituents` attribute", true, false); @@ -274,7 +274,7 @@ static void worker( else if (v->type() == nNull) ; - else throw TypeError("attribute '%s' is %s, which is not supported", attrPath, showType(*v)); + else state.error("attribute '%s' is %s, which is not supported", attrPath, showType(*v)).debugThrow(); } catch (EvalError & e) { auto msg = e.msg(); diff --git a/src/hydra-evaluator/hydra-evaluator.cc b/src/hydra-evaluator/hydra-evaluator.cc index 75506ff85..9312d085a 100644 --- a/src/hydra-evaluator/hydra-evaluator.cc +++ b/src/hydra-evaluator/hydra-evaluator.cc @@ -38,7 +38,7 @@ class JobsetId { friend bool operator!= (const JobsetId & lhs, const JobsetName & rhs); std::string display() const { - return str(format("%1%:%2% (jobset#%3%)") % project % jobset % id); + return boost::str(boost::format("%1%:%2% (jobset#%3%)") % project % jobset % id); } }; bool operator==(const JobsetId & lhs, const JobsetId & rhs) diff --git a/src/hydra-queue-runner/queue-monitor.cc b/src/hydra-queue-runner/queue-monitor.cc index 203f9f1d9..06b06512b 100644 --- a/src/hydra-queue-runner/queue-monitor.cc +++ b/src/hydra-queue-runner/queue-monitor.cc @@ -294,7 +294,7 @@ bool State::getQueuedBuilds(Connection & conn, try { createBuild(build); } catch (Error & e) { - e.addTrace({}, hintfmt("while loading build %d: ", build->id)); + e.addTrace({}, HintFmt("while loading build %d: ", build->id)); throw; } diff --git a/src/lib/Hydra/Base/Controller/NixChannel.pm b/src/lib/Hydra/Base/Controller/NixChannel.pm index 3f8e96096..a5bc2784e 100644 --- a/src/lib/Hydra/Base/Controller/NixChannel.pm +++ b/src/lib/Hydra/Base/Controller/NixChannel.pm @@ -4,7 +4,6 @@ use strict; use warnings; use base 'Hydra::Base::Controller::REST'; use List::SomeUtils qw(any); -use Nix::Store; use Hydra::Helper::Nix; use Hydra::Helper::CatalystUtils; @@ -30,7 +29,7 @@ sub getChannelData { my $outputs = {}; foreach my $output (@outputs) { my $outPath = $output->get_column("outpath"); - next if $checkValidity && !isValidPath($outPath); + next if $checkValidity && !$MACHINE_LOCAL_STORE->isValidPath($outPath); $outputs->{$output->get_column("outname")} = $outPath; push @storePaths, $outPath; # Put the system type in the manifest (for top-level diff --git a/src/lib/Hydra/Controller/Build.pm b/src/lib/Hydra/Controller/Build.pm index c3869838d..4e249d140 100644 --- a/src/lib/Hydra/Controller/Build.pm +++ b/src/lib/Hydra/Controller/Build.pm @@ -10,8 +10,6 @@ use File::Basename; use File::LibMagic; use File::stat; use Data::Dump qw(dump); -use Nix::Store; -use Nix::Config; use List::SomeUtils qw(all); use Encode; use JSON::PP; @@ -82,9 +80,9 @@ sub build_GET { # false because `$_->path` will be empty $c->stash->{available} = $c->stash->{isLocalStore} - ? all { $_->path && isValidPath($_->path) } $build->buildoutputs->all + ? all { $_->path && $MACHINE_LOCAL_STORE->isValidPath($_->path) } $build->buildoutputs->all : 1; - $c->stash->{drvAvailable} = isValidPath $build->drvpath; + $c->stash->{drvAvailable} = $MACHINE_LOCAL_STORE->isValidPath($build->drvpath); if ($build->finished && $build->iscachedbuild) { my $path = ($build->buildoutputs)[0]->path or undef; @@ -308,7 +306,7 @@ sub output : Chained('buildChain') PathPart Args(1) { error($c, "This build is not finished yet.") unless $build->finished; my $output = $build->buildoutputs->find({name => $outputName}); notFound($c, "This build has no output named ‘$outputName’") unless defined $output; - gone($c, "Output is no longer available.") unless isValidPath $output->path; + gone($c, "Output is no longer available.") unless $MACHINE_LOCAL_STORE->isValidPath($output->path); $c->response->header('Content-Disposition', "attachment; filename=\"build-${\$build->id}-${\$outputName}.nar.bz2\""); $c->stash->{current_view} = 'NixNAR'; @@ -425,7 +423,7 @@ sub getDependencyGraph { }; $$done{$path} = $node; my @refs; - foreach my $ref (queryReferences($path)) { + foreach my $ref ($MACHINE_LOCAL_STORE->queryReferences($path)) { next if $ref eq $path; next unless $runtime || $ref =~ /\.drv$/; getDependencyGraph($self, $c, $runtime, $done, $ref); @@ -433,7 +431,7 @@ sub getDependencyGraph { } # Show in reverse topological order to flatten the graph. # Should probably do a proper BFS. - my @sorted = reverse topoSortPaths(@refs); + my @sorted = reverse $MACHINE_LOCAL_STORE->topoSortPaths(@refs); $node->{refs} = [map { $$done{$_} } @sorted]; } @@ -446,7 +444,7 @@ sub build_deps : Chained('buildChain') PathPart('build-deps') { my $build = $c->stash->{build}; my $drvPath = $build->drvpath; - error($c, "Derivation no longer available.") unless isValidPath $drvPath; + error($c, "Derivation no longer available.") unless $MACHINE_LOCAL_STORE->isValidPath($drvPath); $c->stash->{buildTimeGraph} = getDependencyGraph($self, $c, 0, {}, $drvPath); @@ -461,7 +459,7 @@ sub runtime_deps : Chained('buildChain') PathPart('runtime-deps') { requireLocalStore($c); - error($c, "Build outputs no longer available.") unless all { isValidPath($_) } @outPaths; + error($c, "Build outputs no longer available.") unless all { $MACHINE_LOCAL_STORE->isValidPath($_) } @outPaths; my $done = {}; $c->stash->{runtimeGraph} = [ map { getDependencyGraph($self, $c, 1, $done, $_) } @outPaths ]; @@ -481,7 +479,7 @@ sub nix : Chained('buildChain') PathPart('nix') CaptureArgs(0) { if (isLocalStore) { foreach my $out ($build->buildoutputs) { notFound($c, "Path " . $out->path . " is no longer available.") - unless isValidPath($out->path); + unless $MACHINE_LOCAL_STORE->isValidPath($out->path); } } diff --git a/src/lib/Hydra/Controller/Root.pm b/src/lib/Hydra/Controller/Root.pm index 548cfac34..162f347ef 100644 --- a/src/lib/Hydra/Controller/Root.pm +++ b/src/lib/Hydra/Controller/Root.pm @@ -395,7 +395,7 @@ sub narinfo :Path :Args(StrMatch[NARINFO_REGEX]) { my ($hash) = $narinfo =~ NARINFO_REGEX; die("Hash length was not 32") if length($hash) != 32; - my $path = queryPathFromHashPart($hash); + my $path = $MACHINE_LOCAL_STORE->queryPathFromHashPart($hash); if (!$path) { $c->response->status(404); diff --git a/src/lib/Hydra/Helper/Nix.pm b/src/lib/Hydra/Helper/Nix.pm index 71a8a7d77..900514cbf 100644 --- a/src/lib/Hydra/Helper/Nix.pm +++ b/src/lib/Hydra/Helper/Nix.pm @@ -40,8 +40,11 @@ our @EXPORT = qw( registerRoot restartBuilds run + $MACHINE_LOCAL_STORE ); +our $MACHINE_LOCAL_STORE = Nix::Store->new(); + sub getHydraHome { my $dir = $ENV{"HYDRA_HOME"} or die "The HYDRA_HOME directory does not exist!\n"; @@ -494,7 +497,7 @@ sub restartBuilds { $builds = $builds->search({ finished => 1 }); foreach my $build ($builds->search({}, { columns => ["drvpath"] })) { - next if !isValidPath($build->drvpath); + next if !$MACHINE_LOCAL_STORE->isValidPath($build->drvpath); registerRoot $build->drvpath; } diff --git a/src/lib/Hydra/Plugin/BazaarInput.pm b/src/lib/Hydra/Plugin/BazaarInput.pm index 230d108b8..b35ed7c8c 100644 --- a/src/lib/Hydra/Plugin/BazaarInput.pm +++ b/src/lib/Hydra/Plugin/BazaarInput.pm @@ -7,7 +7,6 @@ use Digest::SHA qw(sha256_hex); use File::Path; use Hydra::Helper::Exec; use Hydra::Helper::Nix; -use Nix::Store; sub supportedInputTypes { my ($self, $inputTypes) = @_; @@ -38,9 +37,9 @@ sub fetchInput { (my $cachedInput) = $self->{db}->resultset('CachedBazaarInputs')->search( {uri => $uri, revision => $revision}); - addTempRoot($cachedInput->storepath) if defined $cachedInput; + $MACHINE_LOCAL_STORE->addTempRoot($cachedInput->storepath) if defined $cachedInput; - if (defined $cachedInput && isValidPath($cachedInput->storepath)) { + if (defined $cachedInput && $MACHINE_LOCAL_STORE->isValidPath($cachedInput->storepath)) { $storePath = $cachedInput->storepath; $sha256 = $cachedInput->sha256hash; } else { @@ -58,7 +57,7 @@ sub fetchInput { ($sha256, $storePath) = split ' ', $stdout; # FIXME: time window between nix-prefetch-bzr and addTempRoot. - addTempRoot($storePath); + $MACHINE_LOCAL_STORE->addTempRoot($storePath); $self->{db}->txn_do(sub { $self->{db}->resultset('CachedBazaarInputs')->create( diff --git a/src/lib/Hydra/Plugin/DarcsInput.pm b/src/lib/Hydra/Plugin/DarcsInput.pm index b7f3db550..a8df63964 100644 --- a/src/lib/Hydra/Plugin/DarcsInput.pm +++ b/src/lib/Hydra/Plugin/DarcsInput.pm @@ -7,7 +7,6 @@ use Digest::SHA qw(sha256_hex); use File::Path; use Hydra::Helper::Exec; use Hydra::Helper::Nix; -use Nix::Store; sub supportedInputTypes { my ($self, $inputTypes) = @_; @@ -58,7 +57,7 @@ sub fetchInput { {uri => $uri, revision => $revision}, {rows => 1}); - if (defined $cachedInput && isValidPath($cachedInput->storepath)) { + if (defined $cachedInput && $MACHINE_LOCAL_STORE->isValidPath($cachedInput->storepath)) { $storePath = $cachedInput->storepath; $sha256 = $cachedInput->sha256hash; $revision = $cachedInput->revision; @@ -75,8 +74,8 @@ sub fetchInput { die "darcs changes --count failed" if $? != 0; system "rm", "-rf", "$tmpDir/export/_darcs"; - $storePath = addToStore("$tmpDir/export", 1, "sha256"); - $sha256 = queryPathHash($storePath); + $storePath = $MACHINE_LOCAL_STORE->addToStore("$tmpDir/export", 1, "sha256"); + $sha256 = $MACHINE_LOCAL_STORE->queryPathHash($storePath); $sha256 =~ s/sha256://; $self->{db}->txn_do(sub { diff --git a/src/lib/Hydra/Plugin/GitInput.pm b/src/lib/Hydra/Plugin/GitInput.pm index e5fc7de92..0de021282 100644 --- a/src/lib/Hydra/Plugin/GitInput.pm +++ b/src/lib/Hydra/Plugin/GitInput.pm @@ -186,9 +186,9 @@ sub fetchInput { {uri => $uri, branch => $branch, revision => $revision, isdeepclone => defined($deepClone) ? 1 : 0}, {rows => 1}); - addTempRoot($cachedInput->storepath) if defined $cachedInput; + $MACHINE_LOCAL_STORE->addTempRoot($cachedInput->storepath) if defined $cachedInput; - if (defined $cachedInput && isValidPath($cachedInput->storepath)) { + if (defined $cachedInput && $MACHINE_LOCAL_STORE->isValidPath($cachedInput->storepath)) { $storePath = $cachedInput->storepath; $sha256 = $cachedInput->sha256hash; $revision = $cachedInput->revision; @@ -217,7 +217,7 @@ sub fetchInput { ($sha256, $storePath) = split ' ', grab(cmd => ["nix-prefetch-git", $clonePath, $revision], chomp => 1); # FIXME: time window between nix-prefetch-git and addTempRoot. - addTempRoot($storePath); + $MACHINE_LOCAL_STORE->addTempRoot($storePath); $self->{db}->txn_do(sub { $self->{db}->resultset('CachedGitInputs')->update_or_create( diff --git a/src/lib/Hydra/Plugin/MercurialInput.pm b/src/lib/Hydra/Plugin/MercurialInput.pm index 921262ad7..85bd2c708 100644 --- a/src/lib/Hydra/Plugin/MercurialInput.pm +++ b/src/lib/Hydra/Plugin/MercurialInput.pm @@ -7,7 +7,6 @@ use Digest::SHA qw(sha256_hex); use File::Path; use Hydra::Helper::Nix; use Hydra::Helper::Exec; -use Nix::Store; use Fcntl qw(:flock); sub supportedInputTypes { @@ -68,9 +67,9 @@ sub fetchInput { (my $cachedInput) = $self->{db}->resultset('CachedHgInputs')->search( {uri => $uri, branch => $branch, revision => $revision}); - addTempRoot($cachedInput->storepath) if defined $cachedInput; + $MACHINE_LOCAL_STORE->addTempRoot($cachedInput->storepath) if defined $cachedInput; - if (defined $cachedInput && isValidPath($cachedInput->storepath)) { + if (defined $cachedInput && $MACHINE_LOCAL_STORE->isValidPath($cachedInput->storepath)) { $storePath = $cachedInput->storepath; $sha256 = $cachedInput->sha256hash; } else { @@ -85,7 +84,7 @@ sub fetchInput { ($sha256, $storePath) = split ' ', $stdout; # FIXME: time window between nix-prefetch-hg and addTempRoot. - addTempRoot($storePath); + $MACHINE_LOCAL_STORE->addTempRoot($storePath); $self->{db}->txn_do(sub { $self->{db}->resultset('CachedHgInputs')->update_or_create( diff --git a/src/lib/Hydra/Plugin/PathInput.pm b/src/lib/Hydra/Plugin/PathInput.pm index d122ff573..c923a03c3 100644 --- a/src/lib/Hydra/Plugin/PathInput.pm +++ b/src/lib/Hydra/Plugin/PathInput.pm @@ -5,7 +5,6 @@ use warnings; use parent 'Hydra::Plugin'; use POSIX qw(strftime); use Hydra::Helper::Nix; -use Nix::Store; sub supportedInputTypes { my ($self, $inputTypes) = @_; @@ -30,7 +29,7 @@ sub fetchInput { {srcpath => $uri, lastseen => {">", $timestamp - $timeout}}, {rows => 1, order_by => "lastseen DESC"}); - if (defined $cachedInput && isValidPath($cachedInput->storepath)) { + if (defined $cachedInput && $MACHINE_LOCAL_STORE->isValidPath($cachedInput->storepath)) { $storePath = $cachedInput->storepath; $sha256 = $cachedInput->sha256hash; $timestamp = $cachedInput->timestamp; @@ -46,7 +45,7 @@ sub fetchInput { } chomp $storePath; - $sha256 = (queryPathInfo($storePath, 0))[1] or die; + $sha256 = ($MACHINE_LOCAL_STORE->queryPathInfo($storePath, 0))[1] or die; ($cachedInput) = $self->{db}->resultset('CachedPathInputs')->search( {srcpath => $uri, sha256hash => $sha256}); diff --git a/src/lib/Hydra/Plugin/SubversionInput.pm b/src/lib/Hydra/Plugin/SubversionInput.pm index 456c6892a..83c1f39d2 100644 --- a/src/lib/Hydra/Plugin/SubversionInput.pm +++ b/src/lib/Hydra/Plugin/SubversionInput.pm @@ -7,7 +7,6 @@ use Digest::SHA qw(sha256_hex); use Hydra::Helper::Exec; use Hydra::Helper::Nix; use IPC::Run; -use Nix::Store; sub supportedInputTypes { my ($self, $inputTypes) = @_; @@ -45,7 +44,7 @@ sub fetchInput { (my $cachedInput) = $self->{db}->resultset('CachedSubversionInputs')->search( {uri => $uri, revision => $revision}); - addTempRoot($cachedInput->storepath) if defined $cachedInput; + $MACHINE_LOCAL_STORE->addTempRoot($cachedInput->storepath) if defined $cachedInput; if (defined $cachedInput && isValidPath($cachedInput->storepath)) { $storePath = $cachedInput->storepath; @@ -62,16 +61,16 @@ sub fetchInput { die "error checking out Subversion repo at `$uri':\n$stderr" if $res; if ($type eq "svn-checkout") { - $storePath = addToStore($wcPath, 1, "sha256"); + $storePath = $MACHINE_LOCAL_STORE->addToStore($wcPath, 1, "sha256"); } else { # Hm, if the Nix Perl bindings supported filters in # addToStore(), then we wouldn't need to make a copy here. my $tmpDir = File::Temp->newdir("hydra-svn-export.XXXXXX", CLEANUP => 1, TMPDIR => 1) or die; (system "svn", "export", $wcPath, "$tmpDir/source", "--quiet") == 0 or die "svn export failed"; - $storePath = addToStore("$tmpDir/source", 1, "sha256"); + $storePath = $MACHINE_LOCAL_STORE->addToStore("$tmpDir/source", 1, "sha256"); } - $sha256 = queryPathHash($storePath); $sha256 =~ s/sha256://; + $sha256 = $MACHINE_LOCAL_STORE->queryPathHash($storePath); $sha256 =~ s/sha256://; $self->{db}->txn_do(sub { $self->{db}->resultset('CachedSubversionInputs')->update_or_create( diff --git a/src/lib/Hydra/View/NARInfo.pm b/src/lib/Hydra/View/NARInfo.pm index 44db78b14..bf8711a4a 100644 --- a/src/lib/Hydra/View/NARInfo.pm +++ b/src/lib/Hydra/View/NARInfo.pm @@ -6,8 +6,7 @@ use File::Basename; use Hydra::Helper::CatalystUtils; use MIME::Base64; use Nix::Manifest; -use Nix::Store; -use Nix::Utils; +use Hydra::Helper::Nix; use base qw/Catalyst::View/; sub process { @@ -17,7 +16,7 @@ sub process { $c->response->content_type('text/x-nix-narinfo'); # !!! check MIME type - my ($deriver, $narHash, $time, $narSize, $refs) = queryPathInfo($storePath, 1); + my ($deriver, $narHash, $time, $narSize, $refs) = $MACHINE_LOCAL_STORE->queryPathInfo($storePath, 1); my $info; $info .= "StorePath: $storePath\n"; @@ -28,8 +27,8 @@ sub process { $info .= "References: " . join(" ", map { basename $_ } @{$refs}) . "\n"; if (defined $deriver) { $info .= "Deriver: " . basename $deriver . "\n"; - if (isValidPath($deriver)) { - my $drv = derivationFromPath($deriver); + if ($MACHINE_LOCAL_STORE->isValidPath($deriver)) { + my $drv = $MACHINE_LOCAL_STORE->derivationFromPath($deriver); $info .= "System: $drv->{platform}\n"; } } diff --git a/src/script/hydra-eval-jobset b/src/script/hydra-eval-jobset index 7ed7ebe83..72a386f57 100755 --- a/src/script/hydra-eval-jobset +++ b/src/script/hydra-eval-jobset @@ -85,14 +85,14 @@ sub attrsToSQL { # Fetch a store path from 'eval_substituter' if not already present. sub getPath { my ($path) = @_; - return 1 if isValidPath($path); + return 1 if $MACHINE_LOCAL_STORE->isValidPath($path); my $substituter = $config->{eval_substituter}; system("nix", "--experimental-features", "nix-command", "copy", "--from", $substituter, "--", $path) if defined $substituter; - return isValidPath($path); + return $MACHINE_LOCAL_STORE->isValidPath($path); } @@ -143,7 +143,7 @@ sub fetchInputBuild { , version => $version , outputName => $mainOutput->name }; - if (isValidPath($prevBuild->drvpath)) { + if ($MACHINE_LOCAL_STORE->isValidPath($prevBuild->drvpath)) { $result->{drvPath} = $prevBuild->drvpath; } @@ -233,7 +233,7 @@ sub fetchInputEval { my $out = $build->buildoutputs->find({ name => "out" }); next unless defined $out; # FIXME: Should we fail if the path is not valid? - next unless isValidPath($out->path); + next unless $MACHINE_LOCAL_STORE->isValidPath($out->path); $jobs->{$build->get_column('job')} = $out->path; } diff --git a/src/script/hydra-update-gc-roots b/src/script/hydra-update-gc-roots index 11eba7a6a..130cbb682 100755 --- a/src/script/hydra-update-gc-roots +++ b/src/script/hydra-update-gc-roots @@ -5,7 +5,6 @@ use warnings; use File::Path; use File::stat; use File::Basename; -use Nix::Store; use Hydra::Config; use Hydra::Schema; use Hydra::Helper::Nix; @@ -47,7 +46,7 @@ sub keepBuild { $build->finished && ($build->buildstatus == 0 || $build->buildstatus == 6)) { foreach my $path (split / /, $build->get_column('outpaths')) { - if (isValidPath($path)) { + if ($MACHINE_LOCAL_STORE->isValidPath($path)) { addRoot $path; } else { print STDERR " warning: output ", $path, " has disappeared\n" if $build->finished; @@ -55,7 +54,7 @@ sub keepBuild { } } if (!$build->finished || ($keepFailedDrvs && $build->buildstatus != 0)) { - if (isValidPath($build->drvpath)) { + if ($MACHINE_LOCAL_STORE->isValidPath($build->drvpath)) { addRoot $build->drvpath; } else { print STDERR " warning: derivation ", $build->drvpath, " has disappeared\n"; diff --git a/t/s3-backup-test.pl b/t/s3-backup-test.pl index 9332ab15b..a367981fe 100644 --- a/t/s3-backup-test.pl +++ b/t/s3-backup-test.pl @@ -3,7 +3,6 @@ use File::Basename; use Hydra::Model::DB; use Hydra::Helper::Nix; -use Nix::Store; use Cwd; my $db = Hydra::Model::DB->new; From 1665aed5e302f213db048a38af44021b5e815d80 Mon Sep 17 00:00:00 2001 From: Rick van Schijndel Date: Wed, 3 Apr 2024 22:45:53 +0200 Subject: [PATCH 3/8] t: content-addressed: add test for caDependingOnFailingCA This uncovers an issue with the front-end. --- t/content-addressed/basic.t | 4 ++-- t/jobs/content-addressed.nix | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/t/content-addressed/basic.t b/t/content-addressed/basic.t index 6597e727e..4f92b1dc9 100644 --- a/t/content-addressed/basic.t +++ b/t/content-addressed/basic.t @@ -27,13 +27,13 @@ my $project = $db->resultset('Projects')->create({name => "tests", displayname = my $jobset = createBaseJobset("content-addressed", "content-addressed.nix", $ctx{jobsdir}); ok(evalSucceeds($jobset), "Evaluating jobs/content-addressed.nix should exit with return code 0"); -is(nrQueuedBuildsForJobset($jobset), 5, "Evaluating jobs/content-addressed.nix should result in 4 builds"); +is(nrQueuedBuildsForJobset($jobset), 6, "Evaluating jobs/content-addressed.nix should result in 6 builds"); for my $build (queuedBuildsForJobset($jobset)) { ok(runBuild($build), "Build '".$build->job."' from jobs/content-addressed.nix should exit with code 0"); my $newbuild = $db->resultset('Builds')->find($build->id); is($newbuild->finished, 1, "Build '".$build->job."' from jobs/content-addressed.nix should be finished."); - my $expected = $build->job eq "fails" ? 1 : $build->job =~ /with_failed/ ? 6 : 0; + my $expected = $build->job eq "fails" ? 1 : $build->job =~ /with_failed/ ? 6 : $build->job =~ /FailingCA/ ? 2 : 0; is($newbuild->buildstatus, $expected, "Build '".$build->job."' from jobs/content-addressed.nix should have buildstatus $expected."); my $response = request("/build/".$build->id); diff --git a/t/jobs/content-addressed.nix b/t/jobs/content-addressed.nix index 65496df5e..03bb56e72 100644 --- a/t/jobs/content-addressed.nix +++ b/t/jobs/content-addressed.nix @@ -25,6 +25,13 @@ rec { FOO = empty_dir; }; + caDependingOnFailingCA = + cfg.mkContentAddressedDerivation { + name = "ca-depending-on-failing-ca"; + builder = ./dir-with-file-builder.sh; + FOO = fails; + }; + nonCaDependingOnCA = cfg.mkDerivation { name = "non-ca-depending-on-ca"; From 71986632ced0dcaa0bf8262d2f41e52d3216c39d Mon Sep 17 00:00:00 2001 From: Rick van Schijndel Date: Wed, 3 Apr 2024 22:47:22 +0200 Subject: [PATCH 4/8] hydra-server: findLog: fix issue with ca-derivations enabled When content addressed derivations are built on the hydra server, one may run into an issue where some builds suddenly don't load anymore. This seems to be caused by outPaths that are NULL (which is allowed for ca-derivations). Filter them out to prevent querying the database for them, which is not supported by the database abstraction layer that's currently in use. On my instance this appears to resolve the issue. I feel like I might be doing this at the wrong abstraction layer, but on the other hand -- it seems to resolve it and it also doesn't really look like it will hurt anything. The test added in a previous commit uncovers this issue, and this commit resolves it. So I'm happy with this patch for now. The issue I was seeing on my server: hydra-server[2549]: [error] Couldn't render template "undef error - DBIx::Class::SQLMaker::ClassicExtensions::puke(): Fatal: NULL-within-IN not implemented: The upcoming SQL::Abstract::Classic 2.0 will emit the logically correct SQL instead of raising this exception. at /nix/store/-hydra-unstable-2024-03-08_nix_2_20/libexec/hydra/lib/Hydra/Helper/Nix.pm line 190 See also short discussion here: https://github.com/NixOS/nixpkgs/pull/297392#issuecomment-2035366263 --- src/lib/Hydra/Helper/Nix.pm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/Hydra/Helper/Nix.pm b/src/lib/Hydra/Helper/Nix.pm index 71a8a7d77..7b16b1bec 100644 --- a/src/lib/Hydra/Helper/Nix.pm +++ b/src/lib/Hydra/Helper/Nix.pm @@ -187,6 +187,10 @@ sub findLog { return undef if scalar @outPaths == 0; + # Filter out any NULLs. Content-addressed derivations + # that haven't built yet or failed to build may have a NULL outPath. + @outPaths = grep {defined} @outPaths; + my @steps = $c->model('DB::BuildSteps')->search( { path => { -in => [@outPaths] } }, { select => ["drvpath"] From 3f913a771d1cdfba004a3639fc109e57bf3dae8e Mon Sep 17 00:00:00 2001 From: Rick van Schijndel Date: Wed, 3 Apr 2024 22:55:00 +0200 Subject: [PATCH 5/8] t: content-addressed: add a comment about a misleading testcase --- t/content-addressed/basic.t | 2 ++ 1 file changed, 2 insertions(+) diff --git a/t/content-addressed/basic.t b/t/content-addressed/basic.t index 4f92b1dc9..aefb457b8 100644 --- a/t/content-addressed/basic.t +++ b/t/content-addressed/basic.t @@ -55,6 +55,8 @@ for my $build (queuedBuildsForJobset($jobset)) { } +# XXX: deststoredir is undefined: Use of uninitialized value $ctx{"deststoredir"} in concatenation (.) or string at t/content-addressed/basic.t line 58. +# XXX: This test seems to not do what it seems to be doing. See documentation: https://metacpan.org/pod/Test2::V0#isnt($got,-$do_not_want,-$name) isnt(<$ctx{deststoredir}/realisations/*>, "", "The destination store should have the realisations of the built derivations registered"); done_testing; From ab1f64aa4d8e89225375b5d45c28d7f3492fcc7a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 12 Apr 2024 12:25:49 -0400 Subject: [PATCH 6/8] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nix': 'github:NixOS/nix/c4ebb82da4eade975e874da600dc50e9dec610cb' (2024-02-12) → 'github:NixOS/nix/60824fa97c588a0faf68ea61260a47e388b0a4e5' (2024-04-11) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/a1982c92d8980a0114372973cbdfe0a307f1bdea' (2024-01-12) → 'github:NixOS/nixpkgs/1d6a23f11e44d0fb64b3237569b87658a9eb5643' (2024-04-11) • Removed input 'nixpkgs-for-fileset' --- flake.lock | 34 +++++++++------------------------- flake.nix | 13 ++++--------- 2 files changed, 13 insertions(+), 34 deletions(-) diff --git a/flake.lock b/flake.lock index 4b4f00767..966431f9c 100644 --- a/flake.lock +++ b/flake.lock @@ -42,47 +42,32 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1707750141, - "narHash": "sha256-9qSzGQs/Rjf2i3UQjyaZUznZzYDHkLYro/1FTT1easg=", + "lastModified": 1712849398, + "narHash": "sha256-10z/SoidVl9/lh56cMLj7ntJZHtVrumFvmn1YEqXmaM=", "owner": "NixOS", "repo": "nix", - "rev": "c4ebb82da4eade975e874da600dc50e9dec610cb", + "rev": "60824fa97c588a0faf68ea61260a47e388b0a4e5", "type": "github" }, "original": { "owner": "NixOS", + "ref": "2.21-maintenance", "repo": "nix", "type": "github" } }, "nixpkgs": { "locked": { - "lastModified": 1705033721, - "narHash": "sha256-K5eJHmL1/kev6WuqyqqbS1cdNnSidIZ3jeqJ7GbrYnQ=", + "lastModified": 1712848736, + "narHash": "sha256-CzZwhqyLlebljv1zFS2KWVH/3byHND0LfaO1jKsGuVo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "a1982c92d8980a0114372973cbdfe0a307f1bdea", + "rev": "1d6a23f11e44d0fb64b3237569b87658a9eb5643", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-23.05-small", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-for-fileset": { - "locked": { - "lastModified": 1706098335, - "narHash": "sha256-r3dWjT8P9/Ah5m5ul4WqIWD8muj5F+/gbCdjiNVBKmU=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "a77ab169a83a4175169d78684ddd2e54486ac651", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-23.11", + "ref": "nixos-23.11-small", "repo": "nixpkgs", "type": "github" } @@ -106,8 +91,7 @@ "root": { "inputs": { "nix": "nix", - "nixpkgs": "nixpkgs", - "nixpkgs-for-fileset": "nixpkgs-for-fileset" + "nixpkgs": "nixpkgs" } } }, diff --git a/flake.nix b/flake.nix index 4dc7d25ea..fd6ad586f 100644 --- a/flake.nix +++ b/flake.nix @@ -1,16 +1,11 @@ { description = "A Nix-based continuous build system"; - inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05-small"; - inputs.nix.url = "github:NixOS/nix"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11-small"; + inputs.nix.url = "github:NixOS/nix/2.21-maintenance"; inputs.nix.inputs.nixpkgs.follows = "nixpkgs"; - # TODO get rid of this once https://github.com/NixOS/nix/pull/9546 is - # mered and we upgrade or Nix, so the main `nixpkgs` input is at least - # 23.11 and has `lib.fileset`. - inputs.nixpkgs-for-fileset.url = "github:NixOS/nixpkgs/nixos-23.11"; - - outputs = { self, nixpkgs, nix, nixpkgs-for-fileset }: + outputs = { self, nixpkgs, nix }: let systems = [ "x86_64-linux" "aarch64-linux" ]; forEachSystem = nixpkgs.lib.genAttrs systems; @@ -67,7 +62,7 @@ }; hydra = final.callPackage ./package.nix { - inherit (nixpkgs-for-fileset.lib) fileset; + inherit (nixpkgs.lib) fileset; rawSrc = self; }; }; From b72528be5074f3e62e9ae2c2ae8ef9c07a0b4dd3 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 21 Apr 2024 16:14:24 +0200 Subject: [PATCH 7/8] web: serveFile: also serve a CSP putting served HTML in its own origin --- src/lib/Hydra/Controller/Build.pm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/Hydra/Controller/Build.pm b/src/lib/Hydra/Controller/Build.pm index f85871693..de2c204d9 100644 --- a/src/lib/Hydra/Controller/Build.pm +++ b/src/lib/Hydra/Controller/Build.pm @@ -234,6 +234,9 @@ sub serveFile { } elsif ($ls->{type} eq "regular") { + # Have the hosted data considered its own origin to avoid being a giant + # XSS hole. + $c->response->header('Content-Security-Policy' => 'sandbox allow-scripts'); $c->stash->{'plain'} = { data => grab(cmd => ["nix", "--experimental-features", "nix-command", "store", "cat", "--store", getStoreUri(), "$path"]) }; From 879ceb5cdc88096004882402d333f27d2afac577 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 21 May 2024 18:23:35 -0400 Subject: [PATCH 8/8] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nix': 'github:NixOS/nix/60824fa97c588a0faf68ea61260a47e388b0a4e5' (2024-04-11) → 'github:NixOS/nix/1ebc34e9c54b740ea4f4466443047d709dccf5b2' (2024-05-16) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 966431f9c..a4be74335 100644 --- a/flake.lock +++ b/flake.lock @@ -42,11 +42,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1712849398, - "narHash": "sha256-10z/SoidVl9/lh56cMLj7ntJZHtVrumFvmn1YEqXmaM=", + "lastModified": 1715845907, + "narHash": "sha256-1OigUcZGDInTVZJBTioo9vwRt70yvcfAkSRUeAD/mfg=", "owner": "NixOS", "repo": "nix", - "rev": "60824fa97c588a0faf68ea61260a47e388b0a4e5", + "rev": "1ebc34e9c54b740ea4f4466443047d709dccf5b2", "type": "github" }, "original": {