From 03323f6ef19e187c37da3a4fd2db6803474d6e80 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Mon, 22 Nov 2021 13:14:01 -0500 Subject: [PATCH 1/4] TT: add helpers for linking to jobs, jobsets, and projects, and for generating colon separated names. --- src/lib/Hydra/View/TT.pm | 262 ++++++++++++++++++++++++++++++++++++++- t/View/TT.t | 77 ++++++++++++ 2 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 t/View/TT.t diff --git a/src/lib/Hydra/View/TT.pm b/src/lib/Hydra/View/TT.pm index c6176231..84fcf3e9 100644 --- a/src/lib/Hydra/View/TT.pm +++ b/src/lib/Hydra/View/TT.pm @@ -3,6 +3,7 @@ package Hydra::View::TT; use strict; use warnings; use base 'Catalyst::View::TT'; +use Template::Plugin::HTML; use Hydra::Helper::Nix; use Time::Seconds; @@ -11,7 +12,20 @@ __PACKAGE__->config( ENCODING => 'utf-8', PRE_CHOMP => 1, POST_CHOMP => 1, - expose_methods => [qw/buildLogExists buildStepLogExists jobExists relativeDuration stripSSHUser/]); + expose_methods => [qw/ + buildLogExists + buildStepLogExists + jobExists + linkToJob + linkToJobset + linkToProject + makeNameLinksForJob + makeNameLinksForJobset + makeNameTextForJob + makeNameTextForJobset + relativeDuration + stripSSHUser + /]); sub buildLogExists { my ($self, $c, $build) = @_; @@ -64,4 +78,250 @@ sub jobExists { return defined $jobset->builds->search({ job => $jobName, iscurrent => 1 })->single; } +=head2 linkToProject + +Given a L, return a link to the project. + +Arguments: + +=over 3 + +=item C<$self> +=back + +=item C<$c> +Catalyst Context +=back + +=item C<$project> + +The L to link to. + +=back + +=cut +sub linkToProject { + my ($self, $c, $project) = @_; + + my $html = Template::Plugin::HTML->new(); + + my $projectName = $project->name; + my $escapedProjectName = $html->escape($projectName); + + return '' . $escapedProjectName . ''; +} + +=head2 linkToJobset + +Given a L, return a link to the jobset +and its project in project:jobset notation. + +Arguments: + +=over 3 + +=item C<$self> +=back + +=item C<$c> +Catalyst Context +=back + +=item C<$jobset> + +The L to link to. + +=back + +=cut +sub linkToJobset { + my ($self, $c, $jobset) = @_; + + my $html = Template::Plugin::HTML->new(); + + my $jobsetName = $jobset->name; + my $escapedJobsetName = $html->escape($jobsetName); + + return linkToProject($self, $c, $jobset->project) . + ':' . $escapedJobsetName . ''; +} + +=head2 linkToJobset + +Given a L and L Job name, return +a link to the job, jobset, and project in project:jobset:job notation. + +Arguments: + +=over 4 + +=item C<$self> +=back + +=item C<$c> +Catalyst Context +=back + +=item C<$jobset> + +The L to link to. +=back + +=item C<$jobName> + +The L job name to link to. + +=back + +=cut +sub linkToJob { + my ($self, $c, $jobset, $jobName) = @_; + + my $html = Template::Plugin::HTML->new(); + + my $escapedJobName = $html->escape($jobName); + + return linkToJobset($self, $c, $jobset) . + ':' . $escapedJobName . ''; +} + +=head2 makeNameLinksForJobset + +Given a L, return a link to the jobset's +project and a non-link to the jobset in project:jobset notation. + +Arguments: + +=over 3 + +=item C<$self> +=back + +=item C<$c> +Catalyst Context +=back + +=item C<$jobset> + +The L to link to. + +=back + +=cut +sub makeNameLinksForJobset { + my ($self, $c, $jobset) = @_; + + my $html = Template::Plugin::HTML->new(); + + my $escapedJobsetName = $html->escape($jobset->name); + + return linkToProject($self, $c, $jobset->project) . ':' . $escapedJobsetName; +} + +=head2 makeNameLinksForJob + +Given a L and L Job name, return +a link to the jobset and project, and a non-link to the job in +project:jobset:job notation. + +Arguments: + +=over 4 + +=item C<$self> +=back + +=item C<$c> +Catalyst Context +=back + +=item C<$jobset> + +The L to link to. + +=back + + +=item C<$jobName> + +The L job name to link to. + +=back + +=cut +sub makeNameLinksForJob { + my ($self, $c, $jobset, $jobName) = @_; + + my $html = Template::Plugin::HTML->new(); + + my $escapedJobName = $html->escape($jobName); + + return linkToJobset($self, $c, $jobset) . ':' . $escapedJobName; +} + +=head2 makeNameTextForJobset + +Given a L, return the project and +jobset in project:jobset notation. + +Arguments: + +=over 3 + +=item C<$self> +=back + +=item C<$c> +Catalyst Context +=back + +=item C<$jobset> + +The L to link to. + +=back + +=cut +sub makeNameTextForJobset { + my ($self, $c, $jobset) = @_; + + return $jobset->project->name . ":" . $jobset->name; +} + +=head2 makeNameTextForJob + +Given a L and L Job name, return +the job, jobset, and project in project:jobset:job notation. + +Arguments: + +=over 4 + +=item C<$self> +=back + +=item C<$c> +Catalyst Context +=back + +=item C<$jobset> + +The L to link to. + +=back + + +=item C<$jobName> + +The L job name to link to. + +=back + +=cut +sub makeNameTextForJob { + my ($self, $c, $jobset, $jobName) = @_; + + return $jobset->project->name . ":" . $jobset->name . ":" . $jobName; +} + 1; diff --git a/t/View/TT.t b/t/View/TT.t new file mode 100644 index 00000000..346879a1 --- /dev/null +++ b/t/View/TT.t @@ -0,0 +1,77 @@ +use feature 'unicode_strings'; +use strict; +use warnings; +use Setup; + +my %ctx = test_init(); + +require Hydra::Schema; +require Hydra::Model::DB; + +use Test2::V0; + +require Hydra; # calls setup() + + +my $db = Hydra::Model::DB->new; +hydra_setup($db); + +require Hydra::View::TT; + +# The following lines are a cheap and hacky trick to get $c, +# there is no other reason to call /. +require Catalyst::Test; +Catalyst::Test->import('Hydra'); +my($_, $c) = ctx_request('/'); + + +my $project = $db->resultset('Projects')->create({name => "tests", displayname => "", owner => "root"}); +my $jobset = createBaseJobset("example", "bogus.nix", $ctx{jobsdir}); +my $job = "myjob"; + + +is( + Hydra::View::TT::linkToProject(undef, $c, $project), + 'tests', + "linkToProject" +); +is( + Hydra::View::TT::linkToJobset(undef, $c, $jobset), + 'tests:' + . 'example', + "linkToJobset" +); +is( + Hydra::View::TT::linkToJob(undef, $c, $jobset, $job), + 'tests:' + . 'example:' + . 'myjob', + "linkToJob" +); + +is( + Hydra::View::TT::makeNameLinksForJobset(undef, $c, $jobset), + 'tests' + . ':example', + "makeNameLinksForJobset" +); +is( + Hydra::View::TT::makeNameLinksForJob(undef, $c, $jobset, $job), + 'tests:' + . 'example' + . ':myjob', + "makeNameLinksForJob" +); + +is( + Hydra::View::TT::makeNameTextForJobset(undef, $c, $jobset), + 'tests:example', + "makeNameTextForJobset" +); +is( + Hydra::View::TT::makeNameTextForJob(undef, $c, $jobset, $job), + 'tests:example:myjob', + "makeNameTextForJob" +); + +done_testing; From f1343b3a4c9a38f538a516dc477ff47532cf498a Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Mon, 22 Nov 2021 13:22:16 -0500 Subject: [PATCH 2/4] layout.tt: support a title with HTML in it By default, title is escaped. To support links in titles, support providing the title with HTML in it. --- src/root/layout.tt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/root/layout.tt b/src/root/layout.tt index e5331ddd..8eb1f119 100644 --- a/src/root/layout.tt +++ b/src/root/layout.tt @@ -81,7 +81,8 @@ [% IF !hideHeader %] [% ELSE %] [% IF first %]
[% first = 0; END; %] From b06457c75cf5ff64f380bf703287ee0d439133f0 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Mon, 22 Nov 2021 13:23:02 -0500 Subject: [PATCH 3/4] Titles of pages: make project:jobset:job names clickable But don't make the final element clickable when we're looking at that thing. --- src/root/all.tt | 15 +++++++++++---- src/root/build.tt | 5 ++++- src/root/edit-jobset.tt | 6 +++--- src/root/evals.tt | 9 +++++++-- src/root/job.tt | 3 ++- src/root/jobset-eval.tt | 4 +++- src/root/jobset.tt | 4 +++- src/root/log.tt | 5 ++++- 8 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/root/all.tt b/src/root/all.tt index a4517349..e877f5b5 100644 --- a/src/root/all.tt +++ b/src/root/all.tt @@ -1,7 +1,14 @@ -[% WRAPPER layout.tt title="Latest builds" _ - (job ? " for job $project.name:$jobset.name:$job" : - jobset ? " for jobset $project.name:$jobset.name" : - project ? " for project $project.name" : "") %] +[% WRAPPER layout.tt +titleHTML="Latest builds" _ + (job ? " for job " _ linkToJob(jobset, job) : + jobset ? " for jobset " _ linkToJobset(jobset) : + project ? " for project " _ linkToProject(project) : + "") + title="Latest builds" _ + (job ? " for job " _ makeNameTextForJob(jobset, job) : + jobset ? " for jobset " _ makeNameTextForJobset(jobset) : + project ? " for project $project.name" : + "") %] [% PROCESS common.tt %]

Showing builds [% (page - 1) * resultsPerPage + 1 %] - [% (page - 1) * resultsPerPage + builds.size %] out of [% total %] in order of descending finish time.

diff --git a/src/root/build.tt b/src/root/build.tt index f9f442f6..ea3b75c4 100644 --- a/src/root/build.tt +++ b/src/root/build.tt @@ -1,4 +1,7 @@ -[% WRAPPER layout.tt title="Build $id of job $project.name:$jobset.name:$job" %] +[% WRAPPER layout.tt + title="Build $id of job " _ makeNameTextForJob(jobset, job) + titleHTML="Build $id of job " _ linkToJob(jobset, job) +%] [% PROCESS common.tt %] [% PROCESS "product-list.tt" %] [% USE HTML %] diff --git a/src/root/edit-jobset.tt b/src/root/edit-jobset.tt index 22874815..dbd26dcc 100644 --- a/src/root/edit-jobset.tt +++ b/src/root/edit-jobset.tt @@ -1,8 +1,8 @@ [% WRAPPER layout.tt title= (create ? "Creating jobset in project $project.name" : - createFromEval ? "Creating jobset from evaluation $eval.id of $project.name:$jobset.name" : - cloneJobset ? "Cloning jobset $project.name:$jobset.name" : - "Editing jobset $project.name:$jobset.name") %] + createFromEval ? "Creating jobset from evaluation $eval.id of " _ makeNameTextForJobset(jobset) : + cloneJobset ? "Cloning jobset " _ makeNameTextForJobset(jobset) : + "Editing jobset " _ makeNameTextForJobset(jobset)) %] [% PROCESS common.tt %] [% USE format %] diff --git a/src/root/evals.tt b/src/root/evals.tt index 977441b3..c12079d1 100644 --- a/src/root/evals.tt +++ b/src/root/evals.tt @@ -1,6 +1,11 @@ -[% WRAPPER layout.tt title= +[% WRAPPER layout.tt + title= (build ? "Evaluations containing build $build.id" : - jobset ? "Evaluations of jobset $project.name:$jobset.name" : + jobset ? "Evaluations of jobset " _ makeNameTextForJobset(jobset) : + "Latest evaluations") + titleHTML = + (build ? "Evaluations containing build $build.id" : + jobset ? "Evaluations of jobset " _ linkToJobset(jobset) : "Latest evaluations") %] [% PROCESS common.tt %] diff --git a/src/root/job.tt b/src/root/job.tt index e8a56569..7e475f69 100644 --- a/src/root/job.tt +++ b/src/root/job.tt @@ -1,5 +1,6 @@ [% WRAPPER layout.tt - title="Job $project.name:$jobset.name:$job" + title=makeNameTextForJob(jobset, job) + titleHTML=makeNameLinksForJob(jobset, job) starUri=c.uri_for(c.controller('Job').action_for('star'), c.req.captures) %] [% PROCESS common.tt %] diff --git a/src/root/jobset-eval.tt b/src/root/jobset-eval.tt index d6f2b6d1..e1d25af1 100644 --- a/src/root/jobset-eval.tt +++ b/src/root/jobset-eval.tt @@ -1,4 +1,6 @@ -[% WRAPPER layout.tt title="Evaluation $eval.id of jobset $project.name:$jobset.name " %] +[% WRAPPER layout.tt + title="Evaluation $eval.id of jobset " _ makeNameTextForJobset(jobset) + titleHTML="Evaluation $eval.id of jobset " _ linkToJobset(jobset) %] [% PROCESS common.tt %]