Skip to content

Commit

Permalink
Update verchew
Browse files Browse the repository at this point in the history
  • Loading branch information
jacebrowning committed Aug 17, 2023
1 parent 9e75871 commit cc9c6cb
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 57 deletions.
2 changes: 1 addition & 1 deletion .verchew.ini
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ version = 1

cli = dot
cli_version_arg = -V
version = 7
version = 7 || 8
optional = true
message = This is only needed to generate UML diagrams for documentation.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## 2.2 (beta)

- Added a `sync()` utility map arbitrary objects to the filesystem.
- Added a `sync()` utility to map arbitrary objects to the filesystem.

## 2.1.2 (2023-05-27)

Expand Down
129 changes: 74 additions & 55 deletions bin/verchew
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

#
# The MIT License (MIT)
# Copyright © 2016, Jace Browning
#
Expand Down Expand Up @@ -37,7 +37,6 @@ import sys
from collections import OrderedDict
from subprocess import PIPE, STDOUT, Popen


PY2 = sys.version_info[0] == 2

if PY2:
Expand All @@ -47,13 +46,16 @@ else:
import configparser
from urllib.request import urlretrieve

__version__ = '3.1.1'
__version__ = "3.4.1"

SCRIPT_URL = (
"https://raw.githubusercontent.com/jacebrowning/verchew/main/verchew/script.py"
)
WRAPPER_URL = (
"https://raw.githubusercontent.com/jacebrowning/verchew/main/verchew/wrapper.sh"
)

CONFIG_FILENAMES = ['verchew.ini', '.verchew.ini', '.verchewrc', '.verchew']
CONFIG_FILENAMES = ["verchew.ini", ".verchew.ini", ".verchewrc", ".verchew"]

SAMPLE_CONFIG = """
[Python]
Expand Down Expand Up @@ -108,11 +110,12 @@ def main():
if args.quiet:
QUIET = True

log.debug("PWD: %s", os.getenv('PWD'))
log.debug("PATH: %s", os.getenv('PATH'))
log.debug("PWD: %s", os.getenv("PWD"))
log.debug("PATH: %s", os.getenv("PATH"))

if args.vendor:
vendor_script(args.vendor)
vendor_script(SCRIPT_URL, args.vendor)
vendor_script(WRAPPER_URL, args.vendor + "-wrapper")
sys.exit(0)

path = find_config(args.root, generate=args.init)
Expand All @@ -123,36 +126,34 @@ def main():


def parse_args():
parser = argparse.ArgumentParser(description="System dependency version checker.",)
parser = argparse.ArgumentParser(description="System dependency version checker.")

version = "%(prog)s v" + __version__
parser.add_argument("--version", action="version", version=version)
parser.add_argument(
'--version', action='version', version=version,
)
parser.add_argument(
'-r', '--root', metavar='PATH', help="specify a custom project root directory"
"-r", "--root", metavar="PATH", help="specify a custom project root directory"
)
parser.add_argument(
'--exit-code',
action='store_true',
"--exit-code",
action="store_true",
help="return a non-zero exit code on failure",
)

group_logging = parser.add_mutually_exclusive_group()
group_logging.add_argument(
'-v', '--verbose', action='count', default=0, help="enable verbose logging"
"-v", "--verbose", action="count", default=0, help="enable verbose logging"
)
group_logging.add_argument(
'-q', '--quiet', action='store_true', help="suppress all output on success"
"-q", "--quiet", action="store_true", help="suppress all output on success"
)

group_commands = parser.add_argument_group('commands')
group_commands = parser.add_argument_group("commands")
group_commands.add_argument(
'--init', action='store_true', help="generate a sample configuration file"
"--init", action="store_true", help="generate a sample configuration file"
)

group_commands.add_argument(
'--vendor', metavar='PATH', help="download the program for offline use"
"--vendor", metavar="PATH", help="download the program for offline use"
)

args = parser.parse_args()
Expand All @@ -171,14 +172,14 @@ def configure_logging(count=0):
logging.basicConfig(level=level, format="%(levelname)s: %(message)s")


def vendor_script(path):
def vendor_script(url, path):
root = os.path.abspath(os.path.join(path, os.pardir))
if not os.path.isdir(root):
log.info("Creating directory %s", root)
os.makedirs(root)

log.info("Downloading %s to %s", SCRIPT_URL, path)
urlretrieve(SCRIPT_URL, path)
log.info("Downloading %s to %s", url, path)
urlretrieve(url, path)

log.debug("Making %s executable", path)
mode = os.stat(path).st_mode
Expand Down Expand Up @@ -213,8 +214,8 @@ def generate_config(root=None, filenames=None):
path = os.path.join(root, filenames[0])

log.info("Generating sample config: %s", path)
with open(path, 'w') as config:
config.write(SAMPLE_CONFIG + '\n')
with open(path, "w") as config:
config.write(SAMPLE_CONFIG + "\n")

return path

Expand All @@ -232,9 +233,13 @@ def parse_config(path):
data[section][name] = value

for name in data:
version = data[name].get('version') or ""
data[name]['version'] = version
data[name]['patterns'] = [v.strip() for v in version.split('||')]
version = data[name].get("version") or ""
data[name]["version"] = version
data[name]["patterns"] = [v.strip() for v in version.split("||")]

data[name]["optional"] = data[name].get(
"optional", "false"
).strip().lower() in ("true", "yes", "y", True)

return data

Expand All @@ -244,32 +249,32 @@ def check_dependencies(config):

for name, settings in config.items():
show("Checking for {0}...".format(name), head=True)
output = get_version(settings['cli'], settings.get('cli_version_arg'))
output = get_version(settings["cli"], settings.get("cli_version_arg"))

for pattern in settings['patterns']:
for pattern in settings["patterns"]:
if match_version(pattern, output):
show(_("~") + " MATCHED: {0}".format(pattern or "<anything>"))
success.append(_("~"))
break
else:
if settings.get('optional'):
show(_("?") + " EXPECTED (OPTIONAL): {0}".format(settings['version']))
if settings.get("optional"):
show(_("?") + " EXPECTED (OPTIONAL): {0}".format(settings["version"]))
success.append(_("?"))
else:
if QUIET:
if "not found" in output:
actual = "Not found"
else:
actual = output.split('\n')[0].strip('.')
expected = settings['version'] or "<anything>"
actual = output.split("\n", maxsplit=1)[0].strip(".")
expected = settings["version"] or "<anything>"
print("{0}: {1}, EXPECTED: {2}".format(name, actual, expected))
show(
_("x")
+ " EXPECTED: {0}".format(settings['version'] or "<anything>")
+ " EXPECTED: {0}".format(settings["version"] or "<anything>")
)
success.append(_("x"))
if settings.get('message'):
show(_("#") + " MESSAGE: {0}".format(settings['message']))
if settings.get("message"):
show(_("#") + " MESSAGE: {0}".format(settings["message"]))

show("Results: " + " ".join(success), head=True)

Expand All @@ -278,32 +283,46 @@ def check_dependencies(config):

def get_version(program, argument=None):
if argument is None:
args = [program, '--version']
args = [program, "--version"]
elif argument:
args = [program, argument]
args = [program] + argument.split()
else:
args = [program]

show("$ {0}".format(" ".join(args)))
output = call(args)
lines = output.splitlines()
show(lines[0] if lines else "<nothing>")

if lines:
for line in lines:
if any(char.isdigit() for char in line):
show(line)
break
else:
show(lines[0])
else:
show("<nothing>")

return output


def match_version(pattern, output):
if "not found" in output.split('\n')[0]:
lines = output.splitlines()
if not lines or "not found" in lines[0]:
return False

regex = pattern.replace('.', r'\.') + r'(\b|/)'
regex = pattern.replace(".", r"\.") + r"(\b|/)"

log.debug("Matching %s: %s", regex, output)
match = re.match(regex, output)
if match is None:
match = re.match(r'.*[^\d.]' + regex, output)
for line in lines:
log.debug("Matching %s: %s", regex, line)
match = re.match(regex, line)
if match is None:
log.debug("Matching %s: %s", regex, line)
match = re.match(r".*[^\d.]" + regex, line)
if match:
return True

return bool(match)
return False


def call(args):
Expand All @@ -314,27 +333,27 @@ def call(args):
output = "sh: command not found: {0}".format(args[0])
else:
raw = process.communicate()[0]
output = raw.decode('utf-8').strip()
output = raw.decode("utf-8").strip()
log.debug("Command output: %r", output)

return output


def show(text, start='', end='\n', head=False):
def show(text, start="", end="\n", head=False):
"""Python 2 and 3 compatible version of print."""
if QUIET:
return

if head:
start = '\n'
end = '\n\n'
start = "\n"
end = "\n\n"

if log.getEffectiveLevel() < logging.WARNING:
log.info(text)
else:
formatted = start + text + end
if PY2:
formatted = formatted.encode('utf-8')
formatted = formatted.encode("utf-8")
sys.stdout.write(formatted)
sys.stdout.flush()

Expand All @@ -344,11 +363,11 @@ def _(word, is_tty=None, supports_utf8=None, supports_ansi=None):
formatted = word

if is_tty is None:
is_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()
is_tty = hasattr(sys.stdout, "isatty") and sys.stdout.isatty()
if supports_utf8 is None:
supports_utf8 = str(sys.stdout.encoding).lower() == 'utf-8'
supports_utf8 = str(sys.stdout.encoding).lower() == "utf-8"
if supports_ansi is None:
supports_ansi = sys.platform != 'win32' or 'ANSICON' in os.environ
supports_ansi = sys.platform != "win32" or "ANSICON" in os.environ

style_support = supports_utf8
color_support = is_tty and supports_ansi
Expand All @@ -362,5 +381,5 @@ def _(word, is_tty=None, supports_utf8=None, supports_ansi=None):
return formatted


if __name__ == '__main__': # pragma: no cover
if __name__ == "__main__": # pragma: no cover
main()

0 comments on commit cc9c6cb

Please sign in to comment.