diff --git a/UserManual.md b/UserManual.md index 588b5da..c3b928f 100644 --- a/UserManual.md +++ b/UserManual.md @@ -604,6 +604,14 @@ All=.* Changes to this file are detected immediately and will be visible on next page refresh. +## Adding a description to a job + +Edit `/var/lib/laminar/cfg/jobs/$JOBNAME.conf`: + +``` +DESCRIPTION=Anything here will appear on the job page in the frontend unescaped. +``` + ## Setting the page title Change `LAMINAR_TITLE` in `/etc/laminar.conf` to your preferred page title. Laminar must be restarted for this change to take effect. diff --git a/src/laminar.cpp b/src/laminar.cpp index fa3261e..acf12fa 100644 --- a/src/laminar.cpp +++ b/src/laminar.cpp @@ -317,7 +317,8 @@ std::string Laminar::getStatus(MonitorScope scope) { j.set("number", build).set("started", started); j.EndObject(); }); - + auto desc = jobDescriptions.find(scope.job); + j.set("description", desc == jobDescriptions.end() ? "" : desc->second); } else if(scope.type == MonitorScope::ALL) { j.startArray("jobs"); db->stmt("SELECT name,number,startedAt,completedAt,result FROM builds b JOIN (SELECT name n,MAX(number) l FROM builds GROUP BY n) q ON b.name = q.n AND b.number = q.l") @@ -543,6 +544,10 @@ bool Laminar::loadConfiguration() { ctxPtnList.insert(ctx); jobContexts[jobName].swap(ctxPtnList); } + std::string desc = conf.get("DESCRIPTION"); + if(!desc.empty()) { + jobDescriptions[jobName] = desc; + } } } diff --git a/src/laminar.h b/src/laminar.h index 20a37ea..e316ba2 100644 --- a/src/laminar.h +++ b/src/laminar.h @@ -120,6 +120,8 @@ private: std::unordered_map> jobContexts; + std::unordered_map jobDescriptions; + std::unordered_map jobGroups; Settings settings; diff --git a/src/resources/index.html b/src/resources/index.html index 6b5a7bc..7da3bd4 100644 --- a/src/resources/index.html +++ b/src/resources/index.html @@ -228,6 +228,7 @@

{{$route.params.name}}

+
Last Successful Run
#{{lastSuccess.number}} {{lastSuccess?' - at '+formatDate(lastSuccess.started):'never'}}
diff --git a/src/resources/js/app.js b/src/resources/js/app.js index 93a08a3..4d8fcb9 100644 --- a/src/resources/js/app.js +++ b/src/resources/js/app.js @@ -507,6 +507,7 @@ const Jobs = function() { var Job = function() { var state = { + description: '', jobsRunning: [], jobsRecent: [], lastSuccess: null, @@ -524,6 +525,7 @@ var Job = function() { }, methods: { status: function(msg) { + state.description = msg.description; state.jobsRunning = msg.running; state.jobsRecent = msg.recent; state.lastSuccess = msg.lastSuccess; diff --git a/test/laminar-fixture.h b/test/laminar-fixture.h index e58d4a5..bfb635a 100644 --- a/test/laminar-fixture.h +++ b/test/laminar-fixture.h @@ -51,11 +51,16 @@ public: return kj::heap(*ioContext, bind_http.c_str(), path); } - void defineJob(const char* name, const char* scriptContent) { + void defineJob(const char* name, const char* scriptContent, const char* configContent = nullptr) { KJ_IF_MAYBE(f, tmp.fs->tryOpenFile(kj::Path{"cfg", "jobs", std::string(name) + ".run"}, kj::WriteMode::CREATE | kj::WriteMode::CREATE_PARENT | kj::WriteMode::EXECUTABLE)) { (*f)->writeAll(std::string("#!/bin/sh\n") + scriptContent + "\n"); } + if(configContent) { + KJ_IF_MAYBE(f, tmp.fs->tryOpenFile(kj::Path{"cfg", "jobs", std::string(name) + ".conf"}, kj::WriteMode::CREATE)) { + (*f)->writeAll(configContent); + } + } } struct RunExec { diff --git a/test/laminar-functional.cpp b/test/laminar-functional.cpp index 23d81ee..23e4dc1 100644 --- a/test/laminar-functional.cpp +++ b/test/laminar-functional.cpp @@ -151,3 +151,15 @@ TEST_F(LaminarFixture, Abort) { ASSERT_TRUE(laminar->abort("job1", 1)); EXPECT_EQ(LaminarCi::JobResult::ABORTED, res.wait(ioContext->waitScope).getResult()); } + +TEST_F(LaminarFixture, JobDescription) { + defineJob("foo", "true", "DESCRIPTION=bar"); + auto es = eventSource("/jobs/foo"); + ioContext->waitScope.poll(); + ASSERT_EQ(1, es->messages().size()); + auto json = es->messages().front().GetObject(); + ASSERT_TRUE(json.HasMember("data")); + auto data = json["data"].GetObject(); + ASSERT_TRUE(data.HasMember("description")); + EXPECT_STREQ("bar", data["description"].GetString()); +}