diff options
author | Lars Wirzenius <liw@liw.fi> | 2015-10-21 22:55:10 +0300 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2015-10-24 17:20:53 +0300 |
commit | 939214962dfb003db3d6f322398c344e95a9134a (patch) | |
tree | bd635d38916786ac8e45eb04b812d1f5bb537b63 | |
parent | 72d1ed8ece7985245a80a477dea0d9286d815e00 (diff) | |
download | ick-939214962dfb003db3d6f322398c344e95a9134a.tar.gz |
Add ick-html program
-rw-r--r-- | NEWS | 2 | ||||
-rwxr-xr-x | ick-html | 109 | ||||
-rw-r--r-- | icklib/buildinfo.py | 7 | ||||
-rw-r--r-- | icklib/project.py | 20 | ||||
-rw-r--r-- | icklib/templates/index.j2 | 21 | ||||
-rw-r--r-- | icklib/templates/project.j2 | 16 |
6 files changed, 172 insertions, 3 deletions
@@ -5,6 +5,8 @@ Version 0.8, released UNRELEASED -------------------------------- * Stop creating a build directory for no-op builds. +* Add the `ick-html` program to produce simple, ugly web pages from a + state directory. Version 0.7, released 2015-10-11 -------------------------------- diff --git a/ick-html b/ick-html new file mode 100755 index 0000000..08510c6 --- /dev/null +++ b/ick-html @@ -0,0 +1,109 @@ +#!/usr/bin/env python2 +# Copyright 2015 Lars Wirzenius +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# =*= License: GPL-3+ =*= + + +import glob +import os +import shutil + +import cliapp +import jinja2 +import yaml + +import icklib + + +class IckHTML(cliapp.Application): + + def process_args(self, args): + ick_filename, html_dir = self.parse_command_line_args(args) + ick = self.read_ick_file(ick_filename) + self.create_html(ick_filename, ick, html_dir) + + def parse_command_line_args(self, args): + if len(args) != 2: + raise cliapp.AppException('Must have exactly one argument') + return args[0], args[1] + + def read_ick_file(self, filename): + with open(filename) as f: + return yaml.safe_load(f) + + def create_html(self, ick_filename, ick, html_dir): + loader = jinja2.PackageLoader('icklib', 'templates') + env = jinja2.Environment( + loader=loader, + autoescape=lambda foo: True, + extensions=['jinja2.ext.autoescape']) + + statedir = icklib.create_statedir_from_ick(ick) + + self.create_index_html(ick_filename, ick, env, html_dir) + for project in self.get_projects(ick): + project.builds = statedir.get_build_infos(project.name) + self.copy_build_files_to_html_dir(statedir, project, html_dir) + self.create_project_html(statedir, project, env, html_dir) + + def create_index_html(self, ick_filename, ick, env, html_dir): + vars = { + 'ick_filename': ick_filename, + 'projects': self.get_projects(ick), + 'statedir': icklib.create_statedir_from_ick(ick), + } + + template = env.get_template('index.j2') + with open(os.path.join(html_dir, 'index.html'), 'w') as f: + f.write(template.render(**vars)) + + def get_projects(self, ick): + return sorted( + icklib.create_projects_from_ick(ick, None), + key=lambda p: p.name) + + def copy_build_files_to_html_dir(self, statedir, project, html_dir): + for build in project.builds: + build_dir = os.path.join( + html_dir, project.name, str(build.build_number)) + os.makedirs(build_dir) + self.copy_build_log(build, build_dir) + self.copy_build_artifacts(statedir, project, build, build_dir) + + def copy_build_log(self, build, build_dir): + orig = build.get_build_log_filename() + target = os.path.join(build_dir, 'build_log.txt') + shutil.copy(orig, target) + + def copy_build_artifacts(self, statedir, project, build, build_dir): + artifacts = build.get_artifacts_directory() + target = os.path.join(build_dir, 'build_log.txt') + for artifact in glob.glob(os.path.join(artifacts, '*')): + shutil.copy(artifact, build_dir) + + def create_project_html(self, statedir, project, env, html_dir): + + vars = { + 'project': project, + } + + template = env.get_template('project.j2') + with open(os.path.join(html_dir, '%s.html' % project.name), 'w') as f: + f.write(template.render(**vars)) + + +if __name__ == '__main__': + IckHTML().run() diff --git a/icklib/buildinfo.py b/icklib/buildinfo.py index ad76c5f..022516b 100644 --- a/icklib/buildinfo.py +++ b/icklib/buildinfo.py @@ -23,7 +23,7 @@ import yaml class BuildInformation(object): - _attrs = ['build_number', 'commits', 'pipeline'] + _attrs = ['build_number', 'commits', 'pipeline', 'status'] def __init__(self): self._dirname = None @@ -33,8 +33,11 @@ class BuildInformation(object): def set_dirname(self, dirname): self._dirname = dirname + def get_artifacts_directory(self): + return os.path.join(self._dirname, 'artifacts') + def create_artifacts_directory(self): - dirname = os.path.join(self._dirname, 'artifacts') + dirname = self.get_artifacts_directory() if not os.path.exists(dirname): os.makedirs(dirname) return dirname diff --git a/icklib/project.py b/icklib/project.py index 2c522c9..5cd4646 100644 --- a/icklib/project.py +++ b/icklib/project.py @@ -81,7 +81,13 @@ class Project(object): project=self.name) run_state.logger.important( 'Executing pipeline {name}', name=pipeline_name) - pipeline.build(self, statedir, targets, run_state) + try: + pipeline.build(self, statedir, targets, run_state) + except: + if getattr(run_state, 'build_info', None) is not None: + run_state.build_info.status = 'FAILURE' + run_state.build_info.save() + raise def run_argv_on_target(self, target, argv, displayed_cmd, remote_cwd): self.report_running_on_target(target, displayed_cmd) @@ -300,6 +306,7 @@ class CreateBuildInfo(icklib.BuildStep): self.run_state.project_info.save() # Don't want to lose this if crash. self.run_state.build_info = self.statedir.create_new_build( self.project.name, build_number) + self.run_state.build_info.status = 'RUNNING' self.pivot_to_real_build_log_file(self.run_state) @@ -347,6 +354,14 @@ class CreateBuildInfo(icklib.BuildStep): run_state.logger.add_output_file(f, False) +class FinishBuildInfo(icklib.BuildStep): + + def build(self): + if getattr(self.run_state, 'build_info', None) is not None: + self.run_state.build_info.status = 'SUCCESS' + self.run_state.build_info.save() + + class RunShellCommandsOnEachTarget(icklib.BuildStep): def build(self): @@ -884,6 +899,7 @@ def create_projects_from_ick(ick, wanted_names): FindNewCommitToBuildForShell, CreateBuildInfo, RunShellCommandsOnEachTarget, + FinishBuildInfo, SaveProjectInfo, ], 'debian-ci': [ @@ -899,6 +915,7 @@ def create_projects_from_ick(ick, wanted_names): FindDebianChangesFiles, SetupAPTRepository, UploadDebianPackages, + FinishBuildInfo, SaveProjectInfo, ], 'debian-release': [ @@ -914,6 +931,7 @@ def create_projects_from_ick(ick, wanted_names): FindDebianChangesFiles, SetupAPTRepository, UploadDebianPackages, + FinishBuildInfo, SaveProjectInfo, ], } diff --git a/icklib/templates/index.j2 b/icklib/templates/index.j2 new file mode 100644 index 0000000..b462cae --- /dev/null +++ b/icklib/templates/index.j2 @@ -0,0 +1,21 @@ +<html> + <head> + <title>Ick projects</title> + </head> + <body> + <h1>Ick projects</h1> + <p>This is a list of projects built by Ick, from + {{ ick_filename }}.</p> + <ul> + {% for project in projects %} + <li><a href="{{ project.name }}.html">{{ project.name }}</a> + {% if statedir.get_build_infos(project.name) %} + (latest build: {{ statedir.get_build_infos(project.name)[-1].status }}) + {% else %} + (no builds) + {% endif %} + </li> + {% endfor %} + </ul> + </body> +</html> diff --git a/icklib/templates/project.j2 b/icklib/templates/project.j2 new file mode 100644 index 0000000..929104b --- /dev/null +++ b/icklib/templates/project.j2 @@ -0,0 +1,16 @@ +<html> + <head> + <title>Ick: {{ project.name }}</title> + </head> + <body> + <h1>{{ project.name }}</h1> + <ul> + {% for build in project.builds %} + <li>Build #{{ build.build_number }}: {{ build.status }} + <a href="{{project.name}}/{{build.build_number}}/build_log.txt" + >build log</a> + <a href="{{project.name}}/{{build.build_number}}/">artifacts</a></li> + {% endfor %} + </ul> + </body> +</html> |