diff --git a/flake.lock b/flake.lock index 2ddc7c16..3c4c0f75 100644 --- a/flake.lock +++ b/flake.lock @@ -16,58 +16,58 @@ "type": "github" } }, - "lowdown-src": { + "libgit2": { "flake": false, "locked": { - "lastModified": 1633514407, - "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", - "owner": "kristapsdz", - "repo": "lowdown", - "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "lastModified": 1697646580, + "narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=", + "owner": "libgit2", + "repo": "libgit2", + "rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5", "type": "github" }, "original": { - "owner": "kristapsdz", - "repo": "lowdown", + "owner": "libgit2", + "repo": "libgit2", "type": "github" } }, "nix": { "inputs": { "flake-compat": "flake-compat", - "lowdown-src": "lowdown-src", + "libgit2": "libgit2", "nixpkgs": [ "nixpkgs" ], "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1706208340, - "narHash": "sha256-wNyHUEIiKKVs6UXrUzhP7RSJQv0A8jckgcuylzftl8k=", + "lastModified": 1715805674, + "narHash": "sha256-0CIBMECsA3ISJZrJcOTzi6wa3QENTKGLtOpYIOoxwxo=", "owner": "NixOS", "repo": "nix", - "rev": "2c4bb93ba5a97e7078896ebc36385ce172960e4e", + "rev": "ab48ea416a203e9ccefb70aa634e27477e4c1ac4", "type": "github" }, "original": { "owner": "NixOS", - "ref": "2.19-maintenance", + "ref": "2.20-maintenance", "repo": "nix", "type": "github" } }, "nixpkgs": { "locked": { - "lastModified": 1701615100, - "narHash": "sha256-7VI84NGBvlCTduw2aHLVB62NvCiZUlALLqBe5v684Aw=", + "lastModified": 1705033721, + "narHash": "sha256-K5eJHmL1/kev6WuqyqqbS1cdNnSidIZ3jeqJ7GbrYnQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e9f06adb793d1cca5384907b3b8a4071d5d7cb19", + "rev": "a1982c92d8980a0114372973cbdfe0a307f1bdea", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-23.05", + "ref": "nixos-23.05-small", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index 30aa61c0..31a38416 100644 --- a/flake.nix +++ b/flake.nix @@ -1,8 +1,8 @@ { description = "A Nix-based continuous build system"; - inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05"; - inputs.nix.url = "github:NixOS/nix/2.19-maintenance"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05-small"; + inputs.nix.url = "github:NixOS/nix/2.20-maintenance"; inputs.nix.inputs.nixpkgs.follows = "nixpkgs"; # TODO get rid of this once https://github.com/NixOS/nix/pull/9546 is @@ -180,13 +180,9 @@ root=d7f16a3412e01a43a414535b16007c6931d3a9c7 ''; + nixpkgs.config.permittedInsecurePackages = [ "gitea-1.19.4" ]; nix = { - distributedBuilds = true; - buildMachines = [{ - hostName = "localhost"; - systems = [ system ]; - }]; - binaryCaches = [ ]; + settings.substituters = [ ]; }; services.gitea = { enable = true; @@ -202,7 +198,7 @@ testScript = let scripts.mktoken = pkgs.writeText "token.sql" '' - INSERT INTO access_token (id, uid, name, created_unix, updated_unix, token_hash, token_salt, token_last_eight) VALUES (1, 1, 'hydra', 1617107360, 1617107360, 'a930f319ca362d7b49a4040ac0af74521c3a3c3303a86f327b01994430672d33b6ec53e4ea774253208686c712495e12a486', 'XRjWE9YW0g', '31d3a9c7'); + INSERT INTO access_token (id, uid, name, created_unix, updated_unix, token_hash, token_salt, token_last_eight, scope) VALUES (1, 1, 'hydra', 1617107360, 1617107360, 'a930f319ca362d7b49a4040ac0af74521c3a3c3303a86f327b01994430672d33b6ec53e4ea774253208686c712495e12a486', 'XRjWE9YW0g', '31d3a9c7', 'all'); ''; scripts.git-setup = pkgs.writeShellScript "setup.sh" '' @@ -357,9 +353,9 @@ response = json.loads(data) - assert len(response) == 2, "Expected exactly two status updates for latest commit!" - assert response[0]['status'] == "success", "Expected latest status to be success!" - assert response[1]['status'] == "pending", "Expected first status to be pending!" + assert len(response) == 2, "Expected exactly three status updates for latest commit (queued, finished)!" + assert response[0]['status'] == "success", "Expected finished status to be success!" + assert response[1]['status'] == "pending", "Expected queued status to be pending!" machine.shutdown() ''; diff --git a/src/hydra-eval-jobs/hydra-eval-jobs.cc b/src/hydra-eval-jobs/hydra-eval-jobs.cc index 5cfc3a52..1d7de74b 100644 --- a/src/hydra-eval-jobs/hydra-eval-jobs.cc +++ b/src/hydra-eval-jobs/hydra-eval-jobs.cc @@ -89,7 +89,7 @@ struct MyArgs : MixEvalArgs, MixCommonArgs, RootArgs static MyArgs myArgs; -static std::string queryMetaStrings(EvalState & state, DrvInfo & drv, const std::string & name, const std::string & subAttribute) +static std::string queryMetaStrings(EvalState & state, PackageInfo & drv, const std::string & name, const std::string & subAttribute) { Strings res; std::function rec; @@ -181,7 +181,7 @@ static void worker( // CA derivations do not have static output paths, so we // have to defensively not query output paths in case we // encounter one. - DrvInfo::Outputs outputs = drv->queryOutputs( + PackageInfo::Outputs outputs = drv->queryOutputs( !experimentalFeatureSettings.isEnabled(Xp::CaDerivations)); if (drv->querySystem() == "unknown") diff --git a/src/hydra-queue-runner/build-remote.cc b/src/hydra-queue-runner/build-remote.cc index 4ce1d1b8..e6852c38 100644 --- a/src/hydra-queue-runner/build-remote.cc +++ b/src/hydra-queue-runner/build-remote.cc @@ -1,5 +1,6 @@ #include "build-result.hh" #include "serve-protocol.hh" +#include "serve-protocol-impl.hh" #include "state.hh" #include "current-process.hh" #include "processes.hh" @@ -99,7 +100,7 @@ void RemoteResult::updateWithBuildResult(const nix::BuildResult & buildResult) void State::buildRemote(ref destStore, ::Machine::ptr machine, Step::ptr step, - const BuildOptions & buildOptions, + const ServeProto::BuildOptions & buildOptions, RemoteResult & result, std::shared_ptr activeStep, std::function updateStep, NarMemberDatas & narMembers) @@ -124,7 +125,7 @@ void State::buildRemote(ref destStore, "--timeout", std::to_string(buildOptions.buildTimeout), "--max-build-log-size", std::to_string(buildOptions.maxLogSize), "--max-output-size", std::to_string(maxOutputSize), - "--repeat", std::to_string(buildOptions.repeats), + "--repeat", std::to_string(buildOptions.nrRepeats), "--log-file", result.logFile, // FIXME: step->isDeterministic }; @@ -143,7 +144,7 @@ void State::buildRemote(ref destStore, { auto activeStepState(activeStep->state_.lock()); if (activeStepState->cancelled) throw Error("step cancelled"); - activeStepState->pid = child.sshPid; + activeStepState->pid = child->sshPid; } Finally clearPid([&]() { diff --git a/src/hydra-queue-runner/builder.cc b/src/hydra-queue-runner/builder.cc index fb5e8b64..7cd8fc35 100644 --- a/src/hydra-queue-runner/builder.cc +++ b/src/hydra-queue-runner/builder.cc @@ -99,10 +99,13 @@ State::StepResult State::doBuildStep(nix::ref destStore, it). */ BuildID buildId; std::optional buildDrvPath; - BuildOptions buildOptions; - buildOptions.repeats = step->isDeterministic ? 1 : 0; - buildOptions.maxLogSize = maxLogSize; - buildOptions.enforceDeterminism = step->isDeterministic; + // Other fields set below + nix::ServeProto::BuildOptions buildOptions { + .maxLogSize = maxLogSize, + .nrRepeats = step->isDeterministic ? 1u : 0u, + .enforceDeterminism = step->isDeterministic, + .keepFailed = false, + }; auto conn(dbPool.get()); @@ -137,7 +140,7 @@ State::StepResult State::doBuildStep(nix::ref destStore, { auto i = jobsetRepeats.find(std::make_pair(build2->projectName, build2->jobsetName)); if (i != jobsetRepeats.end()) - buildOptions.repeats = std::max(buildOptions.repeats, i->second); + buildOptions.nrRepeats = std::max(buildOptions.nrRepeats, i->second); } } if (!build) build = *dependents.begin(); @@ -148,7 +151,7 @@ State::StepResult State::doBuildStep(nix::ref destStore, buildOptions.buildTimeout = build->buildTimeout; printInfo("performing step ā€˜%s’ %d times on ā€˜%s’ (needed by build %d and %d others)", - localStore->printStorePath(step->drvPath), buildOptions.repeats + 1, machine->sshName, buildId, (dependents.size() - 1)); + localStore->printStorePath(step->drvPath), buildOptions.nrRepeats + 1, machine->sshName, buildId, (dependents.size() - 1)); } if (!buildOneDone) diff --git a/src/hydra-queue-runner/dispatcher.cc b/src/hydra-queue-runner/dispatcher.cc index f5d9d3e3..6d738ded 100644 --- a/src/hydra-queue-runner/dispatcher.cc +++ b/src/hydra-queue-runner/dispatcher.cc @@ -231,11 +231,11 @@ system_time State::doDispatch() sort(machinesSorted.begin(), machinesSorted.end(), [](const MachineInfo & a, const MachineInfo & b) -> bool { - float ta = std::round(a.currentJobs / a.machine->speedFactorFloat); - float tb = std::round(b.currentJobs / b.machine->speedFactorFloat); + float ta = std::round(a.currentJobs / a.machine->speedFactor); + float tb = std::round(b.currentJobs / b.machine->speedFactor); return ta != tb ? ta < tb : - a.machine->speedFactorFloat != b.machine->speedFactorFloat ? a.machine->speedFactorFloat > b.machine->speedFactorFloat : + a.machine->speedFactor != b.machine->speedFactor ? a.machine->speedFactor > b.machine->speedFactor : a.currentJobs > b.currentJobs; }); diff --git a/src/hydra-queue-runner/hydra-queue-runner.cc b/src/hydra-queue-runner/hydra-queue-runner.cc index e9bfa4e6..073d8d4e 100644 --- a/src/hydra-queue-runner/hydra-queue-runner.cc +++ b/src/hydra-queue-runner/hydra-queue-runner.cc @@ -155,29 +155,27 @@ void State::parseMachines(const std::string & contents) auto machine = std::make_shared<::Machine>(nix::Machine { // `storeUri`, not yet used "", - // `systemTypes`, not yet used - {}, + // `systemTypes` + tokenizeString(tokens[1], ","), // `sshKey` tokens[2] == "-" ? "" : tokens[2], // `maxJobs` tokens[3] != "" ? string2Int(tokens[3]).value() : 1, - // `speedFactor`, not yet used - 1, + // `speedFactor` + atof(tokens[4].c_str()), // `supportedFeatures` std::move(supportedFeatures), // `mandatoryFeatures` std::move(mandatoryFeatures), // `sshPublicHostKey` tokens[7] != "" && tokens[7] != "-" - ? base64Decode(tokens[7]) + ? tokens[7] : "", }); machine->sshName = tokens[0]; - machine->systemTypesSet = tokenizeString(tokens[1], ","); - machine->speedFactorFloat = atof(tokens[4].c_str()); /* Re-use the State object of the previous machine with the same name. */ @@ -640,7 +638,7 @@ void State::dumpStatus(Connection & conn) json machine = { {"enabled", m->enabled}, - {"systemTypes", m->systemTypesSet}, + {"systemTypes", m->systemTypes}, {"supportedFeatures", m->supportedFeatures}, {"mandatoryFeatures", m->mandatoryFeatures}, {"nrStepsDone", s->nrStepsDone.load()}, @@ -927,10 +925,17 @@ void State::run(BuildID buildOne) while (true) { try { auto conn(dbPool.get()); - receiver dumpStatus_(*conn, "dump_status"); - while (true) { - conn->await_notification(); - dumpStatus(*conn); + try { + receiver dumpStatus_(*conn, "dump_status"); + while (true) { + conn->await_notification(); + dumpStatus(*conn); + } + } catch (pqxx::broken_connection & connEx) { + printMsg(lvlError, "main thread: %s", connEx.what()); + printMsg(lvlError, "main thread: Reconnecting in 10s"); + conn.markBad(); + sleep(10); } } catch (std::exception & e) { printMsg(lvlError, "main thread: %s", e.what()); diff --git a/src/hydra-queue-runner/nar-extractor.cc b/src/hydra-queue-runner/nar-extractor.cc index 3c6857bf..61299ecd 100644 --- a/src/hydra-queue-runner/nar-extractor.cc +++ b/src/hydra-queue-runner/nar-extractor.cc @@ -6,7 +6,46 @@ using namespace nix; -struct Extractor : ParseSink + +struct NarMemberConstructor : CreateRegularFileSink +{ + NarMemberData & curMember; + + HashSink hashSink = HashSink { HashAlgorithm::SHA256 }; + + std::optional expectedSize; + + NarMemberConstructor(NarMemberData & curMember) + : curMember(curMember) + { } + + void isExecutable() override + { + } + + void preallocateContents(uint64_t size) override + { + expectedSize = size; + } + + void operator () (std::string_view data) override + { + assert(expectedSize); + *curMember.fileSize += data.size(); + hashSink(data); + if (curMember.contents) { + curMember.contents->append(data); + } + assert(curMember.fileSize <= expectedSize); + if (curMember.fileSize == expectedSize) { + auto [hash, len] = hashSink.finish(); + assert(curMember.fileSize == len); + curMember.sha256 = hash; + } + } +}; + +struct Extractor : FileSystemObjectSink { std::unordered_set filesToKeep { "/nix-support/hydra-build-products", @@ -15,7 +54,6 @@ struct Extractor : ParseSink }; NarMemberDatas & members; - NarMemberData * curMember = nullptr; Path prefix; Extractor(NarMemberDatas & members, const Path & prefix) @@ -27,53 +65,22 @@ struct Extractor : ParseSink members.insert_or_assign(prefix + path, NarMemberData { .type = SourceAccessor::Type::tDirectory }); } - void createRegularFile(const Path & path) override + void createRegularFile(const Path & path, std::function func) override { - curMember = &members.insert_or_assign(prefix + path, NarMemberData { - .type = SourceAccessor::Type::tRegular, - .fileSize = 0, - .contents = filesToKeep.count(path) ? std::optional("") : std::nullopt, - }).first->second; - } - - std::optional expectedSize; - std::unique_ptr hashSink; - - void preallocateContents(uint64_t size) override - { - expectedSize = size; - hashSink = std::make_unique(htSHA256); - } - - void receiveContents(std::string_view data) override - { - assert(expectedSize); - assert(curMember); - assert(hashSink); - *curMember->fileSize += data.size(); - (*hashSink)(data); - if (curMember->contents) { - curMember->contents->append(data); - } - assert(curMember->fileSize <= expectedSize); - if (curMember->fileSize == expectedSize) { - auto [hash, len] = hashSink->finish(); - assert(curMember->fileSize == len); - curMember->sha256 = hash; - hashSink.reset(); - } + NarMemberConstructor nmc { + members.insert_or_assign(prefix + path, NarMemberData { + .type = SourceAccessor::Type::tRegular, + .fileSize = 0, + .contents = filesToKeep.count(path) ? std::optional("") : std::nullopt, + }).first->second, + }; + func(nmc); } void createSymlink(const Path & path, const std::string & target) override { members.insert_or_assign(prefix + path, NarMemberData { .type = SourceAccessor::Type::tSymlink }); } - - void isExecutable() override - { } - - void closeRegularFile() override - { } }; diff --git a/src/hydra-queue-runner/queue-monitor.cc b/src/hydra-queue-runner/queue-monitor.cc index d7214c43..1bd51f5a 100644 --- a/src/hydra-queue-runner/queue-monitor.cc +++ b/src/hydra-queue-runner/queue-monitor.cc @@ -10,8 +10,14 @@ using namespace nix; void State::queueMonitor() { while (true) { + auto conn(dbPool.get()); try { - queueMonitorLoop(); + queueMonitorLoop(*conn); + } catch (pqxx::broken_connection & e) { + printMsg(lvlError, "queue monitor: %s", e.what()); + printMsg(lvlError, "queue monitor: Reconnecting in 10s"); + conn.markBad(); + sleep(10); } catch (std::exception & e) { printError("queue monitor: %s", e.what()); sleep(10); // probably a DB problem, so don't retry right away @@ -20,16 +26,14 @@ void State::queueMonitor() } -void State::queueMonitorLoop() +void State::queueMonitorLoop(Connection & conn) { - auto conn(dbPool.get()); - - receiver buildsAdded(*conn, "builds_added"); - receiver buildsRestarted(*conn, "builds_restarted"); - receiver buildsCancelled(*conn, "builds_cancelled"); - receiver buildsDeleted(*conn, "builds_deleted"); - receiver buildsBumped(*conn, "builds_bumped"); - receiver jobsetSharesChanged(*conn, "jobset_shares_changed"); + receiver buildsAdded(conn, "builds_added"); + receiver buildsRestarted(conn, "builds_restarted"); + receiver buildsCancelled(conn, "builds_cancelled"); + receiver buildsDeleted(conn, "builds_deleted"); + receiver buildsBumped(conn, "builds_bumped"); + receiver jobsetSharesChanged(conn, "jobset_shares_changed"); auto destStore = getDestStore(); @@ -39,17 +43,17 @@ void State::queueMonitorLoop() while (!quit) { localStore->clearPathInfoCache(); - bool done = getQueuedBuilds(*conn, destStore, lastBuildId); + bool done = getQueuedBuilds(conn, destStore, lastBuildId); if (buildOne && buildOneDone) quit = true; /* Sleep until we get notification from the database about an event. */ if (done && !quit) { - conn->await_notification(); + conn.await_notification(); nrQueueWakeups++; } else - conn->get_notifs(); + conn.get_notifs(); if (auto lowestId = buildsAdded.get()) { lastBuildId = std::min(lastBuildId, static_cast(std::stoul(*lowestId) - 1)); @@ -61,11 +65,11 @@ void State::queueMonitorLoop() } if (buildsCancelled.get() || buildsDeleted.get() || buildsBumped.get()) { printMsg(lvlTalkative, "got notification: builds cancelled or bumped"); - processQueueChange(*conn); + processQueueChange(conn); } if (jobsetSharesChanged.get()) { printMsg(lvlTalkative, "got notification: jobset shares changed"); - processJobsetSharesChange(*conn); + processJobsetSharesChange(conn); } } @@ -696,7 +700,7 @@ BuildOutput State::getBuildOutputCached(Connection & conn, nix::ref product.fileSize = row[2].as(); } if (!row[3].is_null()) - product.sha256hash = Hash::parseAny(row[3].as(), htSHA256); + product.sha256hash = Hash::parseAny(row[3].as(), HashAlgorithm::SHA256); if (!row[4].is_null()) product.path = row[4].as(); product.name = row[5].as(); diff --git a/src/hydra-queue-runner/state.hh b/src/hydra-queue-runner/state.hh index 608441b4..589ca6d8 100644 --- a/src/hydra-queue-runner/state.hh +++ b/src/hydra-queue-runner/state.hh @@ -22,6 +22,7 @@ #include "sync.hh" #include "nar-extractor.hh" #include "machines.hh" +#include "serve-protocol.hh" typedef unsigned int BuildID; @@ -242,14 +243,6 @@ struct Machine : nix::Machine we are not yet used to, but once we are, we don't need this. */ std::string sshName; - /* TODO Get rid once `nix::Machine::systemTypes` is a set not - vector. */ - std::set systemTypesSet; - - /* TODO Get rid once `nix::Machine::systemTypes` is a `float` not - an `int`. */ - float speedFactorFloat = 1.0; - struct State { typedef std::shared_ptr ptr; counter currentJobs{0}; @@ -276,7 +269,7 @@ struct Machine : nix::Machine { /* Check that this machine is of the type required by the step. */ - if (!systemTypesSet.count(step->drv->platform == "builtin" ? nix::settings.thisSystem : step->drv->platform)) + if (!systemTypes.count(step->drv->platform == "builtin" ? nix::settings.thisSystem : step->drv->platform)) return false; /* Check that the step requires all mandatory features of this @@ -436,7 +429,7 @@ private: /* How often the build steps of a jobset should be repeated in order to detect non-determinism. */ - std::map, unsigned int> jobsetRepeats; + std::map, size_t> jobsetRepeats; bool uploadLogsToBinaryCache; @@ -465,12 +458,6 @@ private: public: State(std::optional metricsAddrOpt); - struct BuildOptions { - unsigned int maxSilentTime, buildTimeout, repeats; - size_t maxLogSize; - bool enforceDeterminism; - }; - private: nix::MaintainCount startDbUpdate(); @@ -503,7 +490,7 @@ private: void queueMonitor(); - void queueMonitorLoop(); + void queueMonitorLoop(Connection & conn); /* Check the queue for new builds. */ bool getQueuedBuilds(Connection & conn, @@ -555,7 +542,7 @@ private: void buildRemote(nix::ref destStore, Machine::ptr machine, Step::ptr step, - const BuildOptions & buildOptions, + const nix::ServeProto::BuildOptions & buildOptions, RemoteResult & result, std::shared_ptr activeStep, std::function updateStep, NarMemberDatas & narMembers); diff --git a/src/lib/Hydra/Controller/Build.pm b/src/lib/Hydra/Controller/Build.pm index c3869838..6b25ff80 100644 --- a/src/lib/Hydra/Controller/Build.pm +++ b/src/lib/Hydra/Controller/Build.pm @@ -15,6 +15,7 @@ use Nix::Config; use List::SomeUtils qw(all); use Encode; use JSON::PP; +use WWW::Form::UrlEncoded::PP qw(); use feature 'state'; @@ -141,7 +142,7 @@ sub view_nixlog : Chained('buildChain') PathPart('nixlog') { $c->stash->{step} = $step; my $drvPath = $step->drvpath; - my $log_uri = $c->uri_for($c->controller('Root')->action_for("log"), [basename($drvPath)]); + my $log_uri = $c->uri_for($c->controller('Root')->action_for("log"), [WWW::Form::UrlEncoded::PP::url_encode(basename($drvPath))]); showLog($c, $mode, $log_uri); } @@ -150,7 +151,7 @@ sub view_log : Chained('buildChain') PathPart('log') { my ($self, $c, $mode) = @_; my $drvPath = $c->stash->{build}->drvpath; - my $log_uri = $c->uri_for($c->controller('Root')->action_for("log"), [basename($drvPath)]); + my $log_uri = $c->uri_for($c->controller('Root')->action_for("log"), [WWW::Form::UrlEncoded::PP::url_encode(basename($drvPath))]); showLog($c, $mode, $log_uri); } diff --git a/src/lib/Hydra/Controller/Root.pm b/src/lib/Hydra/Controller/Root.pm index 548cfac3..e6c3049f 100644 --- a/src/lib/Hydra/Controller/Root.pm +++ b/src/lib/Hydra/Controller/Root.pm @@ -16,6 +16,7 @@ use List::Util qw[min max]; use List::SomeUtils qw{any}; use Net::Prometheus; use Types::Standard qw/StrMatch/; +use WWW::Form::UrlEncoded::PP qw(); use constant NARINFO_REGEX => qr{^([a-z0-9]{32})\.narinfo$}; # e.g.: https://hydra.example.com/realisations/sha256:a62128132508a3a32eef651d6467695944763602f226ac630543e947d9feb140!out.doi @@ -553,7 +554,7 @@ sub log :Local :Args(1) { my $logPrefix = $c->config->{log_prefix}; if (defined $logPrefix) { - $c->res->redirect($logPrefix . "log/" . basename($drvPath)); + $c->res->redirect($logPrefix . "log/" . WWW::Form::UrlEncoded::PP::url_encode(basename($drvPath))); } else { notFound($c, "The build log of $drvPath is not available."); } diff --git a/src/lib/Hydra/Plugin/GiteaStatus.pm b/src/lib/Hydra/Plugin/GiteaStatus.pm index 426c93f5..f3498941 100644 --- a/src/lib/Hydra/Plugin/GiteaStatus.pm +++ b/src/lib/Hydra/Plugin/GiteaStatus.pm @@ -88,10 +88,6 @@ sub buildQueued { common(@_, [], 0); } -sub buildStarted { - common(@_, [], 1); -} - sub buildFinished { common(@_, 2); } diff --git a/src/root/auth.tt b/src/root/auth.tt index d1539765..d49ba5bd 100644 --- a/src/root/auth.tt +++ b/src/root/auth.tt @@ -33,7 +33,7 @@