Skip to content

Commit

Permalink
Merge pull request #68 from jacebrowning/release/v1.0
Browse files Browse the repository at this point in the history
Release v1.0
  • Loading branch information
jacebrowning committed Nov 1, 2016
2 parents a444b59 + 5109e87 commit c67fd54
Show file tree
Hide file tree
Showing 33 changed files with 304 additions and 95 deletions.
17 changes: 10 additions & 7 deletions .scrutinizer.yml
@@ -1,9 +1,12 @@
checks:
python:
code_rating: true
duplicate_code: true
python:
code_rating: true
duplicate_code: true
tools:
external_code_coverage: true
pylint:
python_version: 3
config_file: '.pylintrc'
external_code_coverage: true
pylint:
python_version: 3
config_file: '.pylintrc'
filter:
excluded_paths:
- "*/tests/*"
3 changes: 3 additions & 0 deletions .travis.yml
Expand Up @@ -13,6 +13,9 @@ env:
global:
- RANDOM_SEED=0

before_install:
- make doctor

install:
- make install

Expand Down
34 changes: 34 additions & 0 deletions .verchew
@@ -0,0 +1,34 @@
[Make]

cli = make

version = GNU Make


[Python]

cli = python

version = Python 3.5.


[virtualenv]

cli = virtualenv

version = 15.


[pandoc]

cli = pandoc

version = 1.


[Graphviz]

cli = dot
cli_version_arg = -V

version = 2.
4 changes: 4 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,9 @@
# Revision History

## 1.0 (2016/11/01)

- Initial stable release.

## 0.6.1 (2016/09/23)

- Added a delay to ensure all applications close.
Expand Down
10 changes: 8 additions & 2 deletions CONTRIBUTING.md
Expand Up @@ -5,12 +5,18 @@
### Requirements

* Make:
* Windows: http://cygwin.com/install.html
* Windows: https://cygwin.com/install.html
* Mac: https://developer.apple.com/xcode
* Linux: http://www.gnu.org/software/make (likely already installed)
* Linux: https://www.gnu.org/software/make (likely already installed)
* Pandoc: http://johnmacfarlane.net/pandoc/installing.html
* Graphviz: http://www.graphviz.org/Download.php

To confirm these system dependencies are configured correctly:

```sh
$ make doctor
```

### Installation

Install project dependencies into a virtual environment:
Expand Down
5 changes: 2 additions & 3 deletions Makefile
Expand Up @@ -78,8 +78,7 @@ run: install

.PHONY: doctor
doctor: ## Confirm system dependencies are available
@ echo "Checking Python version:"
@ python --version | tee /dev/stderr | grep -q "3.5."
bin/verchew

# PROJECT DEPENDENCIES #########################################################

Expand Down Expand Up @@ -114,7 +113,7 @@ $(PIP): $(PYTHON)
@ touch $@

$(PYTHON):
$(SYS_PYTHON) -m venv $(ENV)
$(SYS_PYTHON) -m venv --clear $(ENV)

# CHECKS #######################################################################

Expand Down
16 changes: 8 additions & 8 deletions README.md
Expand Up @@ -36,15 +36,15 @@ and some just don't make sense to keep running on all your computers:

## Installation

`mine` can be installed with pip:
Install mine with pip:

```
```sh
$ pip install mine
```

or directly from the source code:

```
```sh
$ git clone https://github.com/jacebrowning/mine.git
$ cd mine
$ python setup.py install
Expand All @@ -54,7 +54,7 @@ $ python setup.py install

Create a `mine.yml` in your Dropbox:

```
```yaml
config:
computers:
- name: My iMac
Expand Down Expand Up @@ -90,24 +90,24 @@ The `versions` dictionary identifies the name of the executable on each platform

To synchronize the current computer's state:

```
```sh
$ mine
```

To close applications on remote computers and start them locally:

```
```sh
$ mine switch
```

To close applications locally an start them on another computer:

```
```sh
$ mine switch <name>
```

To delete conflicted files in your Dropbox:

```
```sh
$ mine clean
```
210 changes: 210 additions & 0 deletions bin/verchew
@@ -0,0 +1,210 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# The MIT License (MIT)
# Copyright © 2016, Jace Browning
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.


from __future__ import unicode_literals

import os
import sys
import argparse
try:
import configparser # Python 3
except ImportError:
import ConfigParser as configparser # Python 2
from collections import OrderedDict
from subprocess import Popen, PIPE, STDOUT
import logging

__version__ = '0.5b1'

PY2 = sys.version_info[0] == 2
CONFIG_FILENAMES = ['.verchew', '.verchewrc', 'verchew.ini', '.verchew.ini']
STYLE = {
"x": "✘",
"~": "✔"
}
COLOR = {
"x": "\033[91m", # red
"~": "\033[92m", # green
None: "\033[0m", # reset
}

log = logging.getLogger(__name__)


def main():
args = parse_args()
configure_logging(args.verbose)

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

path = find_config(args.root)
config = parse_config(path)

if not check_dependencies(config) and args.exit_code:
sys.exit(1)


def parse_args():
parser = argparse.ArgumentParser()

version = "%(prog)s v" + __version__
parser.add_argument('--version', action='version', version=version)
parser.add_argument('-v', '--verbose', action='count', default=0,
help="enable verbose logging")
parser.add_argument('-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")

args = parser.parse_args()

return args


def configure_logging(count=0):
if count == 0:
level = logging.WARNING
elif count == 1:
level = logging.INFO
else:
level = logging.DEBUG

logging.basicConfig(level=level, format="%(levelname)s: %(message)s")


def find_config(root=None, config_filenames=None):
root = root or os.getcwd()
config_filenames = config_filenames or CONFIG_FILENAMES

path = None
log.info("Looking for config file in: %s", root)
log.debug("Filename options: %s", ", ".join(config_filenames))
for filename in os.listdir(root):
if filename in config_filenames:
path = os.path.join(root, filename)
log.info("Found config file: %s", path)
return path

msg = "No config file found in: {0}".format(root)
raise RuntimeError(msg)


def parse_config(path):
data = OrderedDict()

log.info("Parsing config file: %s", path)
config = configparser.ConfigParser()
config.read(path)

for section in config.sections():
data[section] = OrderedDict()
for name, value in config.items(section):
data[section][name] = value

return data


def check_dependencies(config):
success = []

for name, settings in config.items():
show("Checking for {0}...".format(name), head=True)
output = get_version(settings['cli'], settings.get('cli_version_arg'))
if match_version(settings['version'], output):
show(_("~") + " MATCHED: {0}".format(settings['version']))
success.append(_("~"))
else:
show(_("x") + " EXPECTED: {0}".format(settings['version']))
success.append(_("x"))

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

return _("x") not in success


def get_version(program, argument=None):
argument = argument or '--version'
args = [program, argument]

show("$ {0}".format(" ".join(args)))
output = call(args)
show(output.splitlines()[0])

return output


def match_version(pattern, output):
return output.startswith(pattern) or " " + pattern in output


def call(args):
try:
process = Popen(args, stdout=PIPE, stderr=STDOUT)
except OSError:
log.debug("Command not found: %s", args[0])
output = "sh: command not found: {0}".format(args[0])
else:
raw = process.communicate()[0]
output = raw.decode('utf-8').strip()
log.debug("Command output: %r", output)

return output


def show(text, start='', end='\n', head=False):
"""Python 2 and 3 compatible version of print."""
if head:
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')
sys.stdout.write(formatted)
sys.stdout.flush()


def _(word, utf8=None, tty=None):
"""Format and colorize a word based on available encoding."""
formatted = word

style_support = sys.stdout.encoding == 'UTF-8' if utf8 is None else utf8
color_support = sys.stdout.isatty() if tty is None else tty

if style_support:
formatted = STYLE.get(word, word)

if color_support and COLOR.get(word):
formatted = COLOR[word] + formatted + COLOR[None]

return formatted


if __name__ == '__main__': # pragma: no cover
main()
9 changes: 1 addition & 8 deletions mine/__init__.py
@@ -1,15 +1,8 @@
"""Package for mine."""

import sys

__project__ = 'mine'
__version__ = '0.6.1'
__version__ = '1.0'

CLI = 'mine'
VERSION = "{0} v{1}".format(__project__, __version__)
DESCRIPTION = "Share application state across computers using Dropbox."

PYTHON_VERSION = 3, 3

if sys.version_info < PYTHON_VERSION: # pragma: no cover (manual test)
exit("Python {}.{}+ is required.".format(*PYTHON_VERSION))

0 comments on commit c67fd54

Please sign in to comment.