diff options
author | Joseph Hunkeler <jhunkeler@gmail.com> | 2020-08-20 12:50:19 -0400 |
---|---|---|
committer | Joseph Hunkeler <jhunkeler@gmail.com> | 2020-08-20 12:50:19 -0400 |
commit | c1c3985bad82088a3f60b5d52a8c61b1178eb120 (patch) | |
tree | b40e4762e34ade3199251c42dfc2c05a2b033468 | |
parent | a4531b58f9455a2f86f08afe20f641ef07254f77 (diff) | |
download | good_feeder-c1c3985bad82088a3f60b5d52a8c61b1178eb120.tar.gz |
Improvements:
* Convert UTC to localtime
* User-defined separator between in title record
* Reordered the usage statement a little
* Display program name and version in usage statement
-rw-r--r-- | good_feeder/cli/main.py | 130 |
1 files changed, 102 insertions, 28 deletions
diff --git a/good_feeder/cli/main.py b/good_feeder/cli/main.py index 43d15e0..33a7762 100644 --- a/good_feeder/cli/main.py +++ b/good_feeder/cli/main.py @@ -2,45 +2,102 @@ import os import colorama import feedparser +import pytz +import tzlocal import urllib3 import requests import sys -from pprint import pprint +from datetime import datetime from .. import __version__ + +DEFAULT_DEPTH_SEPARATOR = chr(0xbb) +DEPTH_SEPARATOR = DEFAULT_DEPTH_SEPARATOR +DEPTH_SEPARATOR_MAXLEN = 2 +TZ = tzlocal.get_localzone() + + def auth_valid(s): if ":" in s: return True return False + def auth_split(s): return s.split(":", 1) + +def separator_apply(delim, s): + return s.replace(DEFAULT_DEPTH_SEPARATOR, delim) + + def main(): import argparse - parser = argparse.ArgumentParser() - parser.add_argument('-a', '--auth', help='Format: "username:token" (or omit "-a" and use JENKINS_AUTH environment variable)', type=str) - parser.add_argument('--insecure', help='Disable SSL certificate verification', action='store_true') - parser.add_argument('-l', '--latest', help="Show only latest builds", action='store_true') - parser.add_argument('-f', '--failed', help="Show only failed builds", action='store_true') - parser.add_argument('-s', '--search', help='Filter output on sub-string (case-sensitive)', action='append', nargs="?", type=str) - parser.add_argument('-n', '--negate', help='Negate search filter', action='store_true') - parser.add_argument('--html', help='Output as html (i.e. email)', action='store_true') - - parser.add_argument('-V', '--version', help='Show version', action='store_true'); - parser.add_argument('jenkins_url', type=str, nargs='?') - args = parser.parse_args() + parser = argparse.ArgumentParser( + description=f"{os.path.basename(sys.argv[0])} v{__version__}") + + parser.add_argument('-a', '--auth', + help='Format: "username:token" (or omit "-a"' + ' and use JENKINS_AUTH environment variable)', + type=str) + + parser.add_argument('-L', '--localtime', + help='Convert server UTC timestamp to local time', + action='store_true') + + parser.add_argument('--insecure', + help='Disable SSL certificate verification', + action='store_true') + + parser.add_argument('--html', + help='Output as html (i.e. email)', + action='store_true') + + parser.add_argument('-S', '--sep', + help=f"default string: '{DEFAULT_DEPTH_SEPARATOR}', " + f"max length: {DEPTH_SEPARATOR_MAXLEN}", + action='store', + type=str) + + parser.add_argument('-l', '--latest', + help="Show only latest builds", + action='store_true') + + parser.add_argument('-f', '--failed', + help="Show only failed builds", + action='store_true') + + parser.add_argument('-s', '--search', + help='Filter output on sub-string (case-sensitive)', + action='append', + nargs="?", + type=str) + + parser.add_argument('-n', '--negate', + help='Negate search filter', + action='store_true') + + parser.add_argument('-V', '--version', + help='Show version', + action='store_true') + + parser.add_argument('jenkins_url', + help='URL to server, folder, or job', + nargs='?', + type=str) + args = parser.parse_args() username = "" token = "" if args.version: print(__version__) - exit(0) + return 0 if not args.jenkins_url: - print("Jenkins URL required (i.e. https://jenkins OR https://jenkins/job/folder)") - exit(1) + parser.print_help() + print("\nJenkins URL required\n", file=sys.stderr) + return 1 # Disable SSL drivel when in insecure mode if args.insecure: @@ -50,13 +107,13 @@ def main(): # i.e. username:token if args.auth: if not auth_valid(args.auth): - print("Invalid auth string") - exit(1) + print("Invalid auth string", file=sys.stderr) + return 1 username, token = auth_split(args.auth) elif os.environ.get("JENKINS_AUTH"): if not auth_valid(os.environ["JENKINS_AUTH"]): - print("Invalid JENKINS_AUTH string") - exit(1) + print("Invalid JENKINS_AUTH string", file=sys.stderr) + return 1 username, token = auth_split(os.environ["JENKINS_AUTH"]) # Set default server entrypoint @@ -69,18 +126,22 @@ def main(): # Is this an anonymous or authenticated server query? if not username and not token: - auth_data=None + auth_data = None else: auth_data = (username, token) + if args.sep: + DEPTH_SEPARATOR = args.sep + if len(DEPTH_SEPARATOR) > DEPTH_SEPARATOR_MAXLEN: + DEPTH_SEPARATOR = DEPTH_SEPARATOR[:DEPTH_SEPARATOR_MAXLEN] + # Get RSS data data = requests.get(jenkins_url, auth=auth_data, verify=not args.insecure) # On failure dump whatever Jenkins needs you to know (RAW HTML) if not data: print(data.text, file=sys.stderr) - exit(1) - + return 1 # Consume RSS feed data feed = feedparser.parse(data.text) @@ -93,7 +154,7 @@ def main(): # Verify we have data to iterate over if not feed or not feed.entries: print("No records") - exit(0) + return 0 # Begin dumping RSS records for rec in feed.entries: @@ -108,15 +169,27 @@ def main(): else: color_title = colorama.Fore.GREEN + if args.localtime: + dt = datetime.strptime(rec.published, "%Y-%m-%dT%H:%M:%SZ") + rec.published = dt.replace(tzinfo=pytz.utc)\ + .astimezone(TZ)\ + .strftime("%Y-%m-%dT%H:%M:%S") + if args.html: - output_fmt = f"<span style=\"color:{color_date}\">{rec.published}</span>: " \ - f"<span style=\"color:{color_title}\">{rec.title}</span> " \ + output_fmt = \ + f"<span style=\"color:{color_date}\">{rec.published}</span>: "\ + f"<span style=\"color:{color_title}\">{rec.title}</span> "\ f"(<a href=\"{rec.link}\">link</a>)<br>" else: - output_fmt = f"{color_date}{rec.published}{colorama.Style.RESET_ALL}: " \ - f"{color_title}{rec.title}{colorama.Style.RESET_ALL} " \ + output_fmt = \ + f"{color_date}{rec.published}{colorama.Style.RESET_ALL}: "\ + f"{color_title}{rec.title}{colorama.Style.RESET_ALL} "\ f"({rec.link})" + # Apply user-defined separator + if args.sep: + output_fmt = separator_apply(DEPTH_SEPARATOR, output_fmt) + # User-defined search is additive (-s str1 -s str2 -s str...) if args.search: for pattern in args.search: @@ -131,5 +204,6 @@ def main(): else: print(output_fmt) + if __name__ == '__main__': sys.exit(main) |