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..87a424cc
--- /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'))