222 lines
7.5 KiB
Python
Executable File
222 lines
7.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import argparse
|
|
import configparser
|
|
import io
|
|
import os
|
|
import os.path
|
|
import sys
|
|
from urllib import parse as urllib_parse
|
|
|
|
import jinja2
|
|
|
|
|
|
def escape(buff):
|
|
"""Because otherwise Firefox is a sad panda."""
|
|
return buff.replace(',', '%2c').replace('-', '%2D')
|
|
|
|
|
|
def generate_dashboard_url(dashboard):
|
|
"""Generate a dashboard URL from a given definition."""
|
|
try:
|
|
title = dashboard.get('dashboard', 'title')
|
|
except configparser.NoOptionError:
|
|
raise ValueError("option 'title' in section 'dashboard' not set")
|
|
|
|
try:
|
|
foreach = dashboard.get('dashboard', 'foreach')
|
|
except configparser.NoOptionError:
|
|
raise ValueError("option 'foreach' in section 'dashboard' not set")
|
|
|
|
try:
|
|
baseurl = dashboard.get('dashboard', 'baseurl')
|
|
except configparser.NoOptionError:
|
|
baseurl = 'https://review.opendev.org/#/dashboard/?'
|
|
|
|
url = baseurl
|
|
url += escape(urllib_parse.urlencode({'title': title,
|
|
'foreach': foreach}))
|
|
for section in dashboard.sections():
|
|
if not section.startswith('section'):
|
|
continue
|
|
|
|
try:
|
|
query = dashboard.get(section, 'query')
|
|
except configparser.NoOptionError:
|
|
raise ValueError("option 'query' in '%s' not set" % section)
|
|
|
|
title = section[9:-1]
|
|
encoded = escape(urllib_parse.urlencode({title: query}))
|
|
url += "&%s" % encoded
|
|
return url
|
|
|
|
|
|
def get_options():
|
|
"""Parse command line arguments and options."""
|
|
parser = argparse.ArgumentParser(
|
|
description='Create a Gerrit dashboard URL from specified dashboard '
|
|
'definition files')
|
|
parser.add_argument('dashboard_paths', nargs='+',
|
|
metavar='dashboard_path',
|
|
help='Path to a dashboard definition file or a '
|
|
'directory containing a set of dashboard '
|
|
'definition files with the file suffix .dash.')
|
|
parser.add_argument('--check-only', default=False, action="store_true",
|
|
help='Only check the syntax of the specified '
|
|
'dasbhoard files')
|
|
parser.add_argument('--template', default='single.txt',
|
|
help='Name of template')
|
|
|
|
# Find path to template_dir
|
|
# We need to support running with and without installation
|
|
if os.path.exists('templates'):
|
|
template_dir = 'templates'
|
|
elif os.path.exists('/usr/local/share/gerrit-dash-creator/templates'):
|
|
template_dir = '/usr/local/share/gerrit-dash-creator/templates'
|
|
else:
|
|
template_dir = os.path.join(sys.prefix, 'share',
|
|
'gerrit-dash-creator', 'templates')
|
|
parser.add_argument('--template-directory',
|
|
default=template_dir,
|
|
help='Directory to scan for template files')
|
|
parser.add_argument('--template-file', default=None,
|
|
help='Location of a specific template file')
|
|
parser.add_argument('--open', action='store_true',
|
|
help='Open the link in the default web browser')
|
|
return parser.parse_args()
|
|
|
|
|
|
def read_dashboard_file(dashboard_file):
|
|
"""Read and parse a dashboard definition from a specified file."""
|
|
if (not os.path.isfile(dashboard_file) or
|
|
not os.access(dashboard_file, os.R_OK)):
|
|
raise ValueError("dashboard file '%s' is missing or "
|
|
"is not readable" % dashboard_file)
|
|
dashboard = configparser.ConfigParser()
|
|
dashboard.read_file(open(dashboard_file))
|
|
return dashboard
|
|
|
|
|
|
def load_template(template_file=None, template_directory=None,
|
|
template_name=None):
|
|
"""Load the specified template."""
|
|
if template_file:
|
|
template_name = os.path.basename(template_file)
|
|
template_directory = os.path.dirname(os.path.abspath(template_file))
|
|
|
|
try:
|
|
loader = jinja2.FileSystemLoader(template_directory)
|
|
environment = jinja2.Environment(loader=loader)
|
|
template = environment.get_template(template_name)
|
|
except (jinja2.exceptions.TemplateError, IOError) as e:
|
|
print("error: opening template '%s' failed: %s" %
|
|
(template_name, e.__class__.__name__))
|
|
return
|
|
|
|
return template
|
|
|
|
|
|
def get_configuration(dashboard):
|
|
"""Returns the configuration of a dashboard as string."""
|
|
configuration = io.StringIO()
|
|
dashboard.write(configuration)
|
|
result = configuration.getvalue()
|
|
configuration.close()
|
|
return result
|
|
|
|
|
|
def generate_dashboard_urls(dashboards, template, open_browser=False):
|
|
"""Prints the dashboard URLs of a set of dashboards."""
|
|
result = 0
|
|
|
|
for dashboard_file in dashboards:
|
|
dashboard = dashboards[dashboard_file]
|
|
try:
|
|
url = generate_dashboard_url(dashboard)
|
|
except ValueError as e:
|
|
raise ValueError("generating dashboard '%s' failed: %s" %
|
|
(dashboard_file, e))
|
|
result = 1
|
|
continue
|
|
|
|
variables = {
|
|
'url': url,
|
|
'title': dashboard.get('dashboard', 'title') or None,
|
|
'description': dashboard.get('dashboard', 'description') or None,
|
|
'configuration': get_configuration(dashboard)
|
|
}
|
|
url = template.render(variables)
|
|
print(url)
|
|
if open_browser:
|
|
import webbrowser
|
|
webbrowser.open(url)
|
|
|
|
return result
|
|
|
|
|
|
def load_dashboards(paths):
|
|
"""Load specified dashboards from files or directories."""
|
|
dashboards = {}
|
|
for dashboard_path in paths:
|
|
dashboard_files = []
|
|
if os.path.isdir(dashboard_path):
|
|
for root, dirs, files in os.walk(dashboard_path):
|
|
for file in files:
|
|
if file.endswith('.dash'):
|
|
dashboard_files.append(os.path.join(root, file))
|
|
else:
|
|
dashboard_files.append(dashboard_path)
|
|
|
|
for dashboard_file in dashboard_files:
|
|
try:
|
|
dashboards[dashboard_file] = read_dashboard_file(
|
|
dashboard_file
|
|
)
|
|
except configparser.Error as e:
|
|
raise ValueError("dashboard file '%s' cannot be "
|
|
"parsed: %s" % (dashboard_file, e))
|
|
|
|
return dashboards
|
|
|
|
|
|
def main():
|
|
"""Entrypoint."""
|
|
opts = get_options()
|
|
|
|
template = None
|
|
if not opts.check_only:
|
|
template = load_template(
|
|
template_file=opts.template_file,
|
|
template_directory=opts.template_directory,
|
|
template_name=opts.template
|
|
)
|
|
|
|
try:
|
|
dashboards = load_dashboards(opts.dashboard_paths)
|
|
if not opts.check_only and template:
|
|
generate_dashboard_urls(dashboards, template,
|
|
open_browser=opts.open)
|
|
elif not opts.check_only and not template:
|
|
return 1
|
|
except ValueError as e:
|
|
print("error: %s" % e)
|
|
return 1
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|