pw_cli: Add support for branding This adds the ability to change the banner (including color) that's displayed across the "pw" tooling; such as in "pw watch". Change-Id: I4483e0674020365f5da7974248446e8325ba8389
diff --git a/pw_cli/docs.rst b/pw_cli/docs.rst index fa5da59..fca0fa0 100644 --- a/pw_cli/docs.rst +++ b/pw_cli/docs.rst
@@ -173,3 +173,99 @@ optional arguments: -h, --help show this help message and exit --device DEVICE Set which device to target + +Branding Pigweed's tooling +========================== +An important part of starting a new project is picking a name, and in the case +of Pigweed, designing a banner for the project. Pigweed supports configuring +the banners by setting environment variables: + +* ``PW_BRANDING_BANNER`` - Absolute path to a filename containing a banner to + display when running the ``pw`` commands. See the example below. +* ``PW_BRANDING_BANNER_COLOR`` - Color of the banner. Possible values include: + ``red``, ``bold_red``, ``yellow``, ``bold_yellow``, ``green``, + ``bold_green``, ``blue``, ``cyan``, ``magenta``, ``bold_white``, + ``black_on_white``. See ``pw_cli.colors`` for details. + +The below example shows how to manually change the branding at the command +line. However, these environment variables should be set in the project root's +``bootstrap.sh`` before delegating to Pigweed's upstream ``bootstrap.sh``. + +.. code-block:: text + + $ cat foo-banner.txt + + ▒██████ ░▓██▓░ ░▓██▓░ + ▒█░ ▒█ ▒█ ▒█ ▒█ + ▒█▄▄▄▄ ▒█ █ ▒█ ▒█ █ ▒█ + ▒█▀ ▒█ ▒█ ▒█ ▒█ + ▒█ ░▓██▓░ ░▓██▓░ + + $ export PW_BRANDING_BANNER="$(pwd)/foo-banner.txt" + $ export PW_BRANDING_BANNER_COLOR="bold_red" + $ pw logdemo + + ▒██████ ░▓██▓░ ░▓██▓░ + ▒█░ ▒█ ▒█ ▒█ ▒█ + ▒█▄▄▄▄ ▒█ █ ▒█ ▒█ █ ▒█ + ▒█▀ ▒█ ▒█ ▒█ ▒█ + ▒█ ░▓██▓░ ░▓██▓░ + + 20200610 12:03:44 CRT This is a critical message + 20200610 12:03:44 ERR There was an error on our last operation + 20200610 12:03:44 WRN Looks like something is amiss; consider investigating + 20200610 12:03:44 INF The operation went as expected + 20200610 12:03:44 OUT Standard output of subprocess + +The branding is not purely visual; it serves to make it clear which project an +engineer is working with. + +Making the ASCII / ANSI art +--------------------------- +The most direct way to make the ASCII art is to create it with a text editor. +However, there are some tools to make the process faster and easier. + +* `Patorjk's ASCII art generator <http://patorjk.com/software/taag/>`_ - A + great starting place, since you can copy and paste straight from the browser + into a file, and then point ``PW_BRANDING_BANNER`` at it. Most of the fonts + use normal ASCII characters; and fonts with extended ASCII characters use the + Unicode versions of them (needed for modern terminals). +* `Online ANSII Edit by Andy Herbert + <http://andyherbert.github.io/ansiedit/public/index.html>`_ - Browser based + editor that can export to mixed UTF-8 and ANSII color. It's also `open source + <https://github.com/andyherbert/ansiedit>`_. What's nice about this editor is + that you can create a multi-color banner, and save it with the ``File`` --> + ``Export as ANSi (UTF-8)`` option, and use it directly as a Pigweed banner. + One caveat is that the editor uses UTF-8 box drawing characters, which don't + work well with all terminals. However, the box drawing characters look so + slick on terminals that support them that we feel this is a worthwhile + tradeoff. + +There are other options, but these require additional work to put into Pigweed +since they only export in the traditional ANS or ICE formats. The old ANS +formats do not have a converter (contributions welcome!). Here are some of the +options as of mid-2020: + +* `Playscii <http://vectorpoem.com/playscii/>`_ - Actively maintained. +* `Moebius <https://github.com/blocktronics/moebius>`_ - Actively maintained. +* `SyncDraw <http://syncdraw.bbsdev.net/>`_ - Actively maintained, in 2020, in + a CVS repository. +* `PabloDraw <http://picoe.ca/products/pablodraw/>`_ - Works on most desktop + machines thanks to being written in .NET. Not maintained, but works well. Has + an impresive brush system for organic style drawing. +* `TheDraw <https://en.wikipedia.org/wiki/TheDraw>`_ - One of the most popular + ANSI art editors back in the 90s. Requires DOSBox to run on modern machines, + but otherwise works. It has some of the most impressive capabilities, + including supporting full-color multi-character fonts. + +Future branding improvements +---------------------------- +Branding the ``pw`` tool is a great start, but more changes are planned: + +- Supporting branding the ``bootstrap/activate`` banner, which for technical + reasons is not the same code as the banner printing from the Python tooling. + These will use the same ``PW_BRANDING_BANNER`` and + ``PW_BRANDING_BANNER_COLOR`` environment variables. +- Supporting renaming the ``pw`` command to something project specific, like + ``foo`` in this case. +- Re-coloring the log headers from the ``pw`` tool.
diff --git a/pw_cli/py/pw_cli/arguments.py b/pw_cli/py/pw_cli/arguments.py index baac649..7a0f1b2 100644 --- a/pw_cli/py/pw_cli/arguments.py +++ b/pw_cli/py/pw_cli/arguments.py
@@ -20,15 +20,7 @@ from typing import NoReturn from pw_cli import plugins -from pw_cli.color import colors - -_PIGWEED_BANNER = ''' - ▒█████▄ █▓ ▄███▒ ▒█ ▒█ ░▓████▒ ░▓████▒ ▒▓████▄ - ▒█░ █░ ░█▒ ██▒ ▀█▒ ▒█░ █ ▒█ ▒█ ▀ ▒█ ▀ ▒█ ▀█▌ - ▒█▄▄▄█░ ░█▒ █▓░ ▄▄░ ▒█░ █ ▒█ ▒███ ▒███ ░█ █▌ - ▒█▀ ░█░ ▓█ █▓ ░█░ █ ▒█ ▒█ ▄ ▒█ ▄ ░█ ▄█▌ - ▒█ ░█░ ░▓███▀ ▒█▓▀▓█░ ░▓████▒ ░▓████▒ ▒▓████▀ -''' +from pw_cli.branding import banner _HELP_HEADER = '''The Pigweed command line interface (CLI). @@ -43,8 +35,8 @@ def print_banner() -> None: - """Prints the colorful PIGWEED banner to stderr.""" - print(colors().magenta(_PIGWEED_BANNER), file=sys.stderr) + """Prints the PIGWEED (or project specific) banner to stderr.""" + print(banner(), file=sys.stderr) def format_help() -> str:
diff --git a/pw_cli/py/pw_cli/branding.py b/pw_cli/py/pw_cli/branding.py new file mode 100644 index 0000000..87a424c --- /dev/null +++ b/pw_cli/py/pw_cli/branding.py
@@ -0,0 +1,55 @@ +# Copyright 2020 The Pigweed Authors +# +# 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 +# +# https://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. +"""Facilities for accessing the current Pigweed branding""" + +from typing import Optional +from pathlib import Path + +import pw_cli.env +import pw_cli.color + +_memoized_banner: Optional[str] = None + +# This is the default banner for Pigweed. +_PIGWEED_BANNER = ''' + ▒█████▄ █▓ ▄███▒ ▒█ ▒█ ░▓████▒ ░▓████▒ ▒▓████▄ + ▒█░ █░ ░█▒ ██▒ ▀█▒ ▒█░ █ ▒█ ▒█ ▀ ▒█ ▀ ▒█ ▀█▌ + ▒█▄▄▄█░ ░█▒ █▓░ ▄▄░ ▒█░ █ ▒█ ▒███ ▒███ ░█ █▌ + ▒█▀ ░█░ ▓█ █▓ ░█░ █ ▒█ ▒█ ▄ ▒█ ▄ ░█ ▄█▌ + ▒█ ░█░ ░▓███▀ ▒█▓▀▓█░ ░▓████▒ ░▓████▒ ▒▓████▀ +''' + + +def banner(): + global _memoized_banner # pylint: disable=global-statement + if _memoized_banner is not None: + return _memoized_banner + + parsed_env = pw_cli.env.pigweed_environment() + + # Take the banner from the file PW_BRANDING_BANNER; or use the default. + banner_filename = parsed_env.PW_BRANDING_BANNER + _memoized_banner = (Path(banner_filename).read_text() + if banner_filename else _PIGWEED_BANNER) + + # Color the banner if requested. + banner_color = parsed_env.PW_BRANDING_BANNER_COLOR + if banner_color != '': + _memoized_banner = getattr( + pw_cli.color.colors(), + banner_color, + str, + )(_memoized_banner) + + return _memoized_banner
diff --git a/pw_cli/py/pw_cli/color.py b/pw_cli/py/pw_cli/color.py index 6559def..63c58e2 100644 --- a/pw_cli/py/pw_cli/color.py +++ b/pw_cli/py/pw_cli/color.py
@@ -30,11 +30,13 @@ return lambda msg: f'{start}{msg}{reset}' -# TODO(keir): Totally replace this object with something more complete like the -# 'colorful' module. -class _Color: # pylint: disable=too-few-public-methods +# TODO(keir): Replace this with something like the 'colorful' module. +class _Color: + # pylint: disable=too-few-public-methods + # pylint: disable=too-many-instance-attributes """Helpers to surround text with ASCII color escapes""" def __init__(self): + self.none = str self.red = _make_color(31, 1) self.bold_red = _make_color(30, 41) self.yellow = _make_color(33, 1)
diff --git a/pw_cli/py/pw_cli/env.py b/pw_cli/py/pw_cli/env.py index 3611588..41f16f3 100644 --- a/pw_cli/py/pw_cli/env.py +++ b/pw_cli/py/pw_cli/env.py
@@ -49,6 +49,9 @@ parser.add_var('PW_VIRTUALENV_SETUP_PY_ROOTS') parser.add_var('PW_CARGO_PACKAGE_FILES') + parser.add_var('PW_BRANDING_BANNER') + parser.add_var('PW_BRANDING_BANNER_COLOR', default='magenta') + return parser
diff --git a/pw_watch/py/pw_watch/watch.py b/pw_watch/py/pw_watch/watch.py index e1f8485..5674b0e 100755 --- a/pw_watch/py/pw_watch/watch.py +++ b/pw_watch/py/pw_watch/watch.py
@@ -30,6 +30,7 @@ from watchdog.utils import has_attribute from watchdog.utils import unicode_paths +import pw_cli.branding import pw_cli.color import pw_cli.env import pw_cli.plugins @@ -40,14 +41,6 @@ _LOG = logging.getLogger(__name__) _ERRNO_INOTIFY_LIMIT_REACHED = 28 -_BUILD_MESSAGE = """ - ▒█████▄ █▓ ▄███▒ ▒█ ▒█ ░▓████▒ ░▓████▒ ▒▓████▄ - ▒█░ █░ ░█▒ ██▒ ▀█▒ ▒█░ █ ▒█ ▒█ ▀ ▒█ ▀ ▒█ ▀█▌ - ▒█▄▄▄█░ ░█▒ █▓░ ▄▄░ ▒█░ █ ▒█ ▒███ ▒███ ░█ █▌ - ▒█▀ ░█░ ▓█ █▓ ░█░ █ ▒█ ▒█ ▄ ▒█ ▄ ░█ ▄█▌ - ▒█ ░█░ ░▓███▀ ▒█▓▀▓█░ ░▓████▒ ░▓████▒ ▒▓████▀ -""" - _PASS_MESSAGE = """ ██████╗ █████╗ ███████╗███████╗██╗ ██╔══██╗██╔══██╗██╔════╝██╔════╝██║ @@ -213,7 +206,7 @@ # Clear the screen and show a banner indicating the build is starting. print('\033c', end='') # TODO(pwbug/38): Not Windows compatible. - print(_COLOR.magenta(_BUILD_MESSAGE)) + print(pw_cli.branding.banner()) print( _COLOR.green( ' Watching for changes. Ctrl-C to exit; enter to rebuild'))