merge/resync-2025-09-14 #8
@@ -212,7 +212,7 @@ sub checkPath {
|
|||||||
sub serveFile {
|
sub serveFile {
|
||||||
my ($c, $path) = @_;
|
my ($c, $path) = @_;
|
||||||
|
|
||||||
my $res = run(cmd => ["nix", "--experimental-features", "nix-command",
|
my $res = runCommand(cmd => ["nix", "--experimental-features", "nix-command",
|
||||||
"ls-store", "--store", getStoreUri(), "--json", "$path"]);
|
"ls-store", "--store", getStoreUri(), "--json", "$path"]);
|
||||||
|
|
||||||
if ($res->{status}) {
|
if ($res->{status}) {
|
||||||
|
@@ -44,7 +44,7 @@ our @EXPORT = qw(
|
|||||||
readNixFile
|
readNixFile
|
||||||
registerRoot
|
registerRoot
|
||||||
restartBuilds
|
restartBuilds
|
||||||
run
|
runCommand
|
||||||
$MACHINE_LOCAL_STORE
|
$MACHINE_LOCAL_STORE
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -466,7 +466,7 @@ sub readIntoSocket{
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
sub run {
|
sub runCommand {
|
||||||
my (%args) = @_;
|
my (%args) = @_;
|
||||||
my $res = { stdout => "", stderr => "" };
|
my $res = { stdout => "", stderr => "" };
|
||||||
my $stdin = "";
|
my $stdin = "";
|
||||||
@@ -506,7 +506,7 @@ sub run {
|
|||||||
|
|
||||||
sub grab {
|
sub grab {
|
||||||
my (%args) = @_;
|
my (%args) = @_;
|
||||||
my $res = run(%args, grabStderr => 0);
|
my $res = runCommand(%args, grabStderr => 0);
|
||||||
if ($res->{status}) {
|
if ($res->{status}) {
|
||||||
my $msgloc = "(in an indeterminate location)";
|
my $msgloc = "(in an indeterminate location)";
|
||||||
if (defined $args{dir}) {
|
if (defined $args{dir}) {
|
||||||
|
@@ -32,7 +32,7 @@ sub fetchInput {
|
|||||||
my $stdout = ""; my $stderr = ""; my $res;
|
my $stdout = ""; my $stderr = ""; my $res;
|
||||||
if (! -d $clonePath) {
|
if (! -d $clonePath) {
|
||||||
# Clone the repository.
|
# Clone the repository.
|
||||||
$res = run(timeout => 600,
|
$res = runCommand(timeout => 600,
|
||||||
cmd => ["darcs", "get", "--lazy", $uri, $clonePath],
|
cmd => ["darcs", "get", "--lazy", $uri, $clonePath],
|
||||||
dir => $ENV{"TMPDIR"});
|
dir => $ENV{"TMPDIR"});
|
||||||
die "Error getting darcs repo at `$uri':\n$stderr" if $res->{status};
|
die "Error getting darcs repo at `$uri':\n$stderr" if $res->{status};
|
||||||
|
@@ -137,8 +137,8 @@ sub fetchInput {
|
|||||||
my $res;
|
my $res;
|
||||||
if (! -d $clonePath) {
|
if (! -d $clonePath) {
|
||||||
# Clone everything and fetch the branch.
|
# Clone everything and fetch the branch.
|
||||||
$res = run(cmd => ["git", "init", $clonePath]);
|
$res = runCommand(cmd => ["git", "init", $clonePath]);
|
||||||
$res = run(cmd => ["git", "remote", "add", "origin", "--", $uri], dir => $clonePath) unless $res->{status};
|
$res = runCommand(cmd => ["git", "remote", "add", "origin", "--", $uri], dir => $clonePath) unless $res->{status};
|
||||||
die "error creating git repo in `$clonePath':\n$res->{stderr}" if $res->{status};
|
die "error creating git repo in `$clonePath':\n$res->{stderr}" if $res->{status};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,9 +146,9 @@ sub fetchInput {
|
|||||||
# the remote branch for whatever the repository state is. This command mirrors
|
# the remote branch for whatever the repository state is. This command mirrors
|
||||||
# only one branch of the remote repository.
|
# only one branch of the remote repository.
|
||||||
my $localBranch = _isHash($branch) ? "_hydra_tmp" : $branch;
|
my $localBranch = _isHash($branch) ? "_hydra_tmp" : $branch;
|
||||||
$res = run(cmd => ["git", "fetch", "-fu", "origin", "+$branch:$localBranch"], dir => $clonePath,
|
$res = runCommand(cmd => ["git", "fetch", "-fu", "origin", "+$branch:$localBranch"], dir => $clonePath,
|
||||||
timeout => $cfg->{timeout});
|
timeout => $cfg->{timeout});
|
||||||
$res = run(cmd => ["git", "fetch", "-fu", "origin"], dir => $clonePath, timeout => $cfg->{timeout}) if $res->{status};
|
$res = runCommand(cmd => ["git", "fetch", "-fu", "origin"], dir => $clonePath, timeout => $cfg->{timeout}) if $res->{status};
|
||||||
die "error fetching latest change from git repo at `$uri':\n$res->{stderr}" if $res->{status};
|
die "error fetching latest change from git repo at `$uri':\n$res->{stderr}" if $res->{status};
|
||||||
|
|
||||||
# If deepClone is defined, then we look at the content of the repository
|
# If deepClone is defined, then we look at the content of the repository
|
||||||
@@ -156,16 +156,16 @@ sub fetchInput {
|
|||||||
if (defined $deepClone) {
|
if (defined $deepClone) {
|
||||||
|
|
||||||
# Is the target branch a topgit branch?
|
# Is the target branch a topgit branch?
|
||||||
$res = run(cmd => ["git", "ls-tree", "-r", "$branch", ".topgit"], dir => $clonePath);
|
$res = runCommand(cmd => ["git", "ls-tree", "-r", "$branch", ".topgit"], dir => $clonePath);
|
||||||
|
|
||||||
if ($res->{stdout} ne "") {
|
if ($res->{stdout} ne "") {
|
||||||
# Checkout the branch to look at its content.
|
# Checkout the branch to look at its content.
|
||||||
$res = run(cmd => ["git", "checkout", "--force", "$branch"], dir => $clonePath);
|
$res = runCommand(cmd => ["git", "checkout", "--force", "$branch"], dir => $clonePath);
|
||||||
die "error checking out Git branch '$branch' at `$uri':\n$res->{stderr}" if $res->{status};
|
die "error checking out Git branch '$branch' at `$uri':\n$res->{stderr}" if $res->{status};
|
||||||
|
|
||||||
# This is a TopGit branch. Fetch all the topic branches so
|
# This is a TopGit branch. Fetch all the topic branches so
|
||||||
# that builders can run "tg patch" and similar.
|
# that builders can run "tg patch" and similar.
|
||||||
$res = run(cmd => ["tg", "remote", "--populate", "origin"], dir => $clonePath, timeout => $cfg->{timeout});
|
$res = runCommand(cmd => ["tg", "remote", "--populate", "origin"], dir => $clonePath, timeout => $cfg->{timeout});
|
||||||
print STDERR "warning: `tg remote --populate origin' failed:\n$res->{stderr}" if $res->{status};
|
print STDERR "warning: `tg remote --populate origin' failed:\n$res->{stderr}" if $res->{status};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
74
t/Hydra/Controller/Build/download.t
Normal file
74
t/Hydra/Controller/Build/download.t
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Setup;
|
||||||
|
use Test2::V0;
|
||||||
|
use Catalyst::Test ();
|
||||||
|
use HTTP::Request::Common;
|
||||||
|
|
||||||
|
my %ctx = test_init();
|
||||||
|
|
||||||
|
Catalyst::Test->import('Hydra');
|
||||||
|
|
||||||
|
my $db = Hydra::Model::DB->new;
|
||||||
|
hydra_setup($db);
|
||||||
|
|
||||||
|
my $project = $db->resultset('Projects')->create({name => "tests", displayname => "", owner => "root"});
|
||||||
|
|
||||||
|
# Create a simple Nix expression that uses the existing build-product-simple.sh
|
||||||
|
my $jobsdir = $ctx{jobsdir};
|
||||||
|
my $nixfile = "$jobsdir/simple.nix";
|
||||||
|
open(my $fh, '>', $nixfile) or die "Cannot create simple.nix: $!";
|
||||||
|
print $fh <<"EOF";
|
||||||
|
with import ./config.nix;
|
||||||
|
{
|
||||||
|
simple = mkDerivation {
|
||||||
|
name = "build-product-simple";
|
||||||
|
builder = ./build-product-simple.sh;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
close($fh);
|
||||||
|
|
||||||
|
# Create a jobset that uses the simple build
|
||||||
|
my $jobset = createBaseJobset("simple", "simple.nix", $ctx{jobsdir});
|
||||||
|
|
||||||
|
ok(evalSucceeds($jobset), "Evaluating simple.nix should succeed");
|
||||||
|
is(nrQueuedBuildsForJobset($jobset), 1, "Should have 1 build queued");
|
||||||
|
|
||||||
|
my $build = (queuedBuildsForJobset($jobset))[0];
|
||||||
|
ok(runBuild($build), "Build should succeed");
|
||||||
|
|
||||||
|
$build->discard_changes();
|
||||||
|
|
||||||
|
subtest "Test downloading build products (regression test for #1520)" => sub {
|
||||||
|
# Get the build URL
|
||||||
|
my $build_id = $build->id;
|
||||||
|
|
||||||
|
# First, check that the build has products
|
||||||
|
my @products = $build->buildproducts;
|
||||||
|
ok(scalar @products >= 1, "Build should have at least 1 product");
|
||||||
|
|
||||||
|
# Find the doc product (created by build-product-simple.sh)
|
||||||
|
my ($doc_product) = grep { $_->type eq "doc" } @products;
|
||||||
|
ok($doc_product, "Should have a doc product");
|
||||||
|
|
||||||
|
if ($doc_product) {
|
||||||
|
# Test downloading via the download endpoint
|
||||||
|
# This tests the serveFile function which was broken in #1520
|
||||||
|
my $download_url = "/build/$build_id/download/" . $doc_product->productnr . "/text.txt";
|
||||||
|
my $response = request(GET $download_url);
|
||||||
|
|
||||||
|
# The key test: should not return 500 error with "Can't use string ("1") as a HASH ref"
|
||||||
|
isnt($response->code, 500, "Download should not return 500 error (regression test for #1520)");
|
||||||
|
is($response->code, 200, "Download should succeed with 200")
|
||||||
|
or diag("Response code: " . $response->code . ", Content: " . $response->content);
|
||||||
|
|
||||||
|
like($response->header('Content-Security-Policy') // '', qr/\bsandbox\b/, 'CSP header present with sandbox');
|
||||||
|
|
||||||
|
# Check that we get actual content
|
||||||
|
ok(length($response->content) > 0, "Should receive file content");
|
||||||
|
is($response->content, "Hello\n", "Should get expected content");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
done_testing();
|
@@ -58,24 +58,23 @@ if (!defined($pid = fork())) {
|
|||||||
ok(sendNotifications(), "Sent notifications");
|
ok(sendNotifications(), "Sent notifications");
|
||||||
|
|
||||||
kill('INT', $pid);
|
kill('INT', $pid);
|
||||||
|
waitpid($pid, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
# We expect $ctx{jobsdir}/server.py to create the file at $filename, but the time it
|
# We expect $ctx{jobsdir}/server.py to create the file at $filename, but the time it
|
||||||
# takes to do so is non-deterministic. We need to give it _some_ time to hopefully
|
# takes to do so is non-deterministic. We need to give it _some_ time to hopefully
|
||||||
# settle -- but not too much that it drastically slows things down.
|
# settle -- but not too much that it drastically slows things down.
|
||||||
for my $i (1..10) {
|
for my $i (1..10) {
|
||||||
if (! -f $filename) {
|
last if -f $filename;
|
||||||
diag("$filename does not yet exist");
|
diag("$filename does not yet exist");
|
||||||
sleep(1);
|
sleep(1);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
open(my $fh, "<", $filename) or die ("Can't open(): $!\n");
|
open(my $fh, "<", $filename) or die ("Can't open(): $!\n");
|
||||||
my $i = 0;
|
my $request_uri = <$fh>;
|
||||||
my $uri = <$fh>;
|
|
||||||
my $data = <$fh>;
|
my $data = <$fh>;
|
||||||
|
|
||||||
ok(index($uri, "gitea/api/v1/repos/root/foo/statuses") != -1, "Correct URL");
|
ok(index($request_uri, "gitea/api/v1/repos/root/foo/statuses") != -1, "Correct URL");
|
||||||
|
|
||||||
my $json = JSON->new;
|
my $json = JSON->new;
|
||||||
my $content;
|
my $content;
|
||||||
|
Reference in New Issue
Block a user