Skip to content

Commit

Permalink
Add command to download script for offline use
Browse files Browse the repository at this point in the history
  • Loading branch information
jacebrowning committed Apr 18, 2020
1 parent 6662338 commit c1fddb0
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 20 deletions.
3 changes: 3 additions & 0 deletions .pylint.ini
Expand Up @@ -129,6 +129,9 @@ disable=
singleton-comparison,
bad-continuation,
global-statement,
import-error,
no-name-in-module,
ungrouped-imports,

# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
@@ -1,6 +1,7 @@
# 3.1 (unreleased)
# 3.1 (beta)

- Added support for unicode characters in Python 3.8.
- Added `--vendor PATH` command to download the program for offline use.

# 3.0.2 (2020-01-06)

Expand Down
8 changes: 4 additions & 4 deletions docs/cli/configuration.md
Expand Up @@ -2,10 +2,10 @@

Any of the following can be used as the `verchew` configuration filename:

* `.verchew.ini`
* `verchew.ini`
* `.verchew`
* `.verchewrc`
- `.verchew.ini`
- `verchew.ini`
- `.verchew`
- `.verchewrc`

# Version Arguments

Expand Down
16 changes: 13 additions & 3 deletions docs/cli/vendoring.md
Expand Up @@ -2,14 +2,16 @@

Normally, `verchew` is installed using `pip`, but given the variety of environments in which this program is run, you might be in a situation where `pip`:

* is not installed
* does not have the correct permissions to install `verchew`
* does not have network access to PyPI
- is not installed
- does not have the correct permissions to install `verchew`
- does not have network access to PyPI

For this reason, `verchew` can also be embedded into your project as a standalone Python script.

## Setup

### Manual

Create a `bin/` directory in your project:

```
Expand All @@ -29,6 +31,14 @@ Ensure the script has executable permissions:
$ chmod a+x bin/verchew
```

### Automatic

If you already have `verchew` installed, it can download itself:

```
$ verchew --vendor bin/verchew
```

## Usage

To check the versions of your system dependencies simply run:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
@@ -1,7 +1,7 @@
[tool.poetry]

name = "verchew"
version = "3.0.2" # also update verchew/script.py
version = "3.1b1" # also update verchew/script.py
description = "System dependency version checker."

license = "MIT"
Expand Down
10 changes: 10 additions & 0 deletions tests/test_cli.py
Expand Up @@ -144,6 +144,16 @@ def it_generates_a_sample_config(cli):
expect(cmd.returncode) == 0


def describe_vendor():
def it_creates_a_new_file(cli):
cmd = cli('--vendor', 'bin/verchew')

expect(cmd.stderr) == ""
expect(cmd.stdout) == ""
expect(cmd.returncode) == 0
expect(cmd.files_created).contains('bin/verchew')


def describe_main():
@pytest.mark.skipif(
sys.version_info[0] == 2 or sys.platform == 'win32',
Expand Down
52 changes: 42 additions & 10 deletions verchew/script.py
Expand Up @@ -41,11 +41,17 @@
PY2 = sys.version_info[0] == 2

if PY2:
import ConfigParser as configparser # pylint: disable=import-error
import ConfigParser as configparser
from urllib import urlretrieve
else:
import configparser # type: ignore
import configparser
from urllib.request import urlretrieve

__version__ = '3.0.2'
__version__ = '3.1b1'

SCRIPT_URL = (
"https://raw.githubusercontent.com/jacebrowning/verchew/master/verchew/script.py"
)

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

Expand Down Expand Up @@ -100,6 +106,10 @@ def main():
log.debug("PWD: %s", os.getenv('PWD'))
log.debug("PATH: %s", os.getenv('PATH'))

if args.vendor:
vendor_script(args.vendor)
sys.exit(0)

path = find_config(args.root, generate=args.init)
config = parse_config(path)

Expand All @@ -108,30 +118,38 @@ def main():


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

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

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

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

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

args = parser.parse_args()

return args
Expand All @@ -148,6 +166,20 @@ def configure_logging(count=0):
logging.basicConfig(level=level, format="%(levelname)s: %(message)s")


def vendor_script(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.debug("Making %s executable", path)
mode = os.stat(path).st_mode
os.chmod(path, mode | 0o111)


def find_config(root=None, filenames=None, generate=False):
root = root or os.getcwd()
filenames = filenames or CONFIG_FILENAMES
Expand Down
45 changes: 44 additions & 1 deletion verchew/tests/test_script.py
Expand Up @@ -4,16 +4,59 @@

from __future__ import unicode_literals

import os

import pytest
from expecter import expect

from verchew.script import _, find_config, get_version, match_version, parse_config
from verchew.script import (
_,
find_config,
get_version,
match_version,
parse_config,
vendor_script,
)


def write(config, text, indent=8):
config.write(text.replace(' ' * indent, ''))


def describe_vendor_script():
def when_missing_file(tmpdir):
tmpdir.chdir()
path = os.path.join(tmpdir, 'missing')

vendor_script('missing')

expect(os.access(path, os.X_OK)) == True
with open(path, 'r') as f:
expect(f.read()).contains("verchew")

def when_missing_directory(tmpdir):
tmpdir.chdir()
path = os.path.join(tmpdir, 'bin/missing')

vendor_script('bin/missing')

expect(os.access(path, os.X_OK)) == True
with open(path, 'r') as f:
expect(f.read()).contains("verchew")

def when_existing_file(tmpdir):
tmpdir.chdir()
path = os.path.join(tmpdir, 'existing')
with open(path, 'w') as f:
f.write('Hello, world!')

vendor_script('existing')

expect(os.access(path, os.X_OK)) == True
with open(path, 'r') as f:
expect(f.read()).contains("verchew")


def describe_find_config():
@pytest.fixture
def config(tmpdir):
Expand Down

0 comments on commit c1fddb0

Please sign in to comment.