resolves #61: clickable up/downstream triggers

Recognises triggers in build logs and converts them to
hyperlinks. Also separates upstream job from reason string
and allows both to be provided
pull/70/head
Oliver Giles 6 years ago
parent f5e719ac02
commit 63301c73d9

@ -1,5 +1,5 @@
///
/// Copyright 2015-2017 Oliver Giles
/// Copyright 2015-2018 Oliver Giles
///
/// This file is part of Laminar
///
@ -36,20 +36,13 @@ static int setParams(int argc, char** argv, T& request) {
n++;
}
int argsConsumed = n;
char* job = getenv("JOB");
char* num = getenv("RUN");
char* reason = getenv("LAMINAR_REASON");
if(job && num) n+=2;
else if(reason) n++;
if(n == 0) return argsConsumed;
auto params = request.initParams(n + (job&&num?2:0) + (reason?1:0));
auto params = request.initParams(n);
for(int i = 0; i < argsConsumed; ++i) {
for(int i = 0; i < n; ++i) {
char* name = argv[i];
char* val = strchr(name, '=');
*val++ = '\0';
@ -57,19 +50,28 @@ static int setParams(int argc, char** argv, T& request) {
params[i].setValue(val);
}
int argsConsumed = n;
if(job && num) {
params[argsConsumed].setName("=parentJob");
params[argsConsumed].setValue(job);
params[argsConsumed+1].setName("=parentBuild");
params[argsConsumed+1].setValue(num);
} else if(reason) {
params[argsConsumed].setName("=reason");
params[argsConsumed].setValue(reason);
params[n].setName("=parentJob");
params[n++].setValue(job);
params[n].setName("=parentBuild");
params[n++].setValue(num);
}
if(reason) {
params[n].setName("=reason");
params[n].setValue(reason);
}
return argsConsumed;
}
static void printTriggerLink(const char* job, uint run) {
// use a private ANSI CSI sequence to mark the JOB:NUM so the
// frontend can recognise it and generate a hyperlink.
printf("\033[{%s:%d\033\\\n", job, run);
}
int main(int argc, char** argv) {
if(argc < 2) {
fprintf(stderr, "Usage: %s <command> [parameters...]\n", argv[0]);
@ -129,7 +131,7 @@ int main(int argc, char** argv) {
fprintf(stderr, "Failed to start job '%s'\n", argv[2]);
ret = ENOENT;
}
printf("%s:%d\n", argv[jobNameIndex], resp.getBuildNum());
printTriggerLink(argv[jobNameIndex], resp.getBuildNum());
}));
jobNameIndex += n + 1;
} while(jobNameIndex < argc);
@ -150,7 +152,7 @@ int main(int argc, char** argv) {
req.setJobName(argv[jobNameIndex]);
int n = setParams(argc - jobNameIndex - 1, &argv[jobNameIndex + 1], req);
ts.add(req.send().then([&ret,argv,jobNameIndex](capnp::Response<LaminarCi::RunResults> resp){
printf("%s:%d\n", argv[jobNameIndex], resp.getBuildNum());
printTriggerLink(argv[jobNameIndex], resp.getBuildNum());
if(resp.getResult() != LaminarCi::JobResult::SUCCESS) {
ret = EFAILED;
}

@ -178,20 +178,22 @@ void Laminar::sendStatus(LaminarClient* client) {
j.set("time", time(nullptr));
j.startObject("data");
if(client->scope.type == MonitorScope::RUN) {
db->stmt("SELECT queuedAt,startedAt,completedAt, result, reason FROM builds WHERE name = ? AND number = ?")
db->stmt("SELECT queuedAt,startedAt,completedAt,result,reason,parentJob,parentBuild FROM builds WHERE name = ? AND number = ?")
.bind(client->scope.job, client->scope.num)
.fetch<time_t, time_t, time_t, int, std::string>([&](time_t queued, time_t started, time_t completed, int result, std::string reason) {
.fetch<time_t, time_t, time_t, int, std::string, std::string, uint>([&](time_t queued, time_t started, time_t completed, int result, std::string reason, std::string parentJob, uint parentBuild) {
j.set("queued", started-queued);
j.set("started", started);
j.set("completed", completed);
j.set("result", to_string(RunState(result)));
j.set("reason", reason);
j.startObject("upstream").set("name", parentJob).set("num", parentBuild).EndObject(2);
});
if(const Run* run = activeRun(client->scope.job, client->scope.num)) {
j.set("queued", run->startedAt - run->queuedAt);
j.set("started", run->startedAt);
j.set("reason", run->reason());
j.set("result", to_string(RunState::RUNNING));
j.set("reason", run->reason());
j.startObject("upstream").set("name", run->parentName).set("num", run->parentBuild).EndObject(2);
db->stmt("SELECT completedAt - startedAt FROM builds WHERE name = ? ORDER BY completedAt DESC LIMIT 1")
.bind(run->name)
.fetch<uint>([&](uint lastRuntime){

@ -294,6 +294,7 @@
<div style="clear:both;"></div>
<dl class="dl-horizontal">
<dt>Reason</dt><dd>{{job.reason}}</dd>
<dt v-show="job.upstream.num > 0">Upstream</dt><dd v-show="job.upstream.num > 0"><router-link :to="'/jobs/'+job.upstream.name">{{job.upstream.name}}</router-link> <router-link :to="'/jobs/'+job.upstream.name+'/'+job.upstream.num">#{{job.upstream.num}}</router-link></li></dd>
<dt>Queued for</dt><dd>{{job.queued}}s</dd>
<dt>Started</dt><dd>{{formatDate(job.started)}}</dd>
<dt v-show="runComplete(job)">Completed</dt><dd v-show="job.completed">{{formatDate(job.completed)}}</dd>

@ -655,14 +655,14 @@ var Job = function() {
const Run = function() {
var state = {
job: { artifacts: [] },
job: { artifacts: [], upstream: {} },
latestNum: null,
log: '',
autoscroll: false
};
var firstLog = false;
var logHandler = function(vm, d) {
state.log += ansi_up.ansi_to_html(d.replace(/</g,'&lt;').replace(/>/g,'&gt;'));
state.log += ansi_up.ansi_to_html(d.replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\033\[\{([^:]+):(\d+)\033\\/g, (m,$1,$2)=>{return '<a href="/jobs/'+$1+'" onclick="return vroute(this);">'+$1+'</a>:<a href="/jobs/'+$1+'/'+$2+'" onclick="return vroute(this);">#'+$2+'</a>';}));
vm.$forceUpdate();
if (!firstLog) {
firstLog = true;

@ -159,9 +159,6 @@ bool Run::configure(uint buildNum, std::shared_ptr<Node> nd, const kj::Directory
}
std::string Run::reason() const {
if(!parentName.empty()) {
return std::string("Triggered by upstream ") + parentName + " #" + std::to_string(parentBuild);
}
return reasonMsg;
}

Loading…
Cancel
Save