diff --git a/.gitattributes b/.gitattributes index bace6a7c..201aadbe 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ * text=auto -CHANGES.md merge=union +CHANGELOG.md merge=union diff --git a/.travis.yml b/.travis.yml index dd36f8ae..5edbbb32 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ sudo: false language: python python: -- 3.4 - 3.5 cache: diff --git a/CHANGES.md b/CHANGELOG.md similarity index 75% rename from CHANGES.md rename to CHANGELOG.md index bb826459..959ecdd3 100644 --- a/CHANGES.md +++ b/CHANGELOG.md @@ -1,43 +1,41 @@ -Revision History -================ +# Revision History -0.10 (2016/04/14) ------------------ +## 0.11 (2016/05/10) + +- Removed dependency on `sh` to support Cygwin/MinGW/etc. on Windows. +- Dropped Python 3.4 support for `subprocess` and `*args` improvements. +- **BREAKING**: Renamed config file key `dir` to `name`. + +## 0.10 (2016/04/14) - Added `show` command to display dependency and internal paths. -0.9 (2016/03/31) ----------------- +## 0.9 (2016/03/31) - Added `edit` command to launch the configuration file. - Depth now defaults to 5 to prevent infinite recursion. - Fixed handling of source lists containing different dependencies. -0.8.3 (2016/03/14) ------------------- +## 0.8.3 (2016/03/14) - Renamed to GitMan. -0.8.2 (2016/02/24) ------------------- +## 0.8.2 (2016/02/24) - Updated to YORM v0.6. -0.8.1 (2016/01/21) ------------------- +## 0.8.1 (2016/01/21) - Added an error message when attempting to lock invalid repositories. -0.8 (2016/01/13) ----------------- +## 0.8 (2016/01/13) - Switched to using repository mirrors to speed up cloning. - Disabled automatic fetching on install. - Added `--fetch` option on `install` to always fetch. - Now displaying `git status` output when there are changes. -0.7 (2015/12/22) ----------------- +## 0.7 (2015/12/22) - Fixed `git remote rm` command (@hdnivara). - Now applying the `update` dependency filter to locking as well. @@ -45,14 +43,12 @@ Revision History - Added `lock` command to manually save all dependency versions. - Now requiring `--lock` option on `update` to explicitly lock dependencies. -0.6 (2015/11/13) ----------------- +## 0.6 (2015/11/13) - Added the ability to filter the dependency list on `install` and `update`. - Added `--depth` option to limit dependency traversal on `install`, `update`, and `list`. -0.5 (2015/10/20) ----------------- +## 0.5 (2015/10/20) - Added Git plugin support via: `git deps`. - Removed `--no-clean` option (now the default) on `install` and `update`. @@ -62,18 +58,15 @@ Revision History - Disabled warnings when running `install` without locked sources. - Added `--no-lock` option to disable version recording. -0.4.2 (2015/10/18) ------------------- +## 0.4.2 (2015/10/18) - Fixed crash when running with some sources missing. -0.4.1 (2015/09/24) ------------------- +## 0.4.1 (2015/09/24) - Switched to cloning for initial working tree creation. -0.4 (2015/09/18) ----------------- +## 0.4 (2015/09/18) - Replaced `install` command with `update`. - Updated `install` command to use locked dependency versions. @@ -81,67 +74,55 @@ Revision History - Now requiring `--force` to `uninstall` with uncommitted changes. - Updated `list` command to show full shell commands. -0.3.1 (2015/09/09) ------------------- +## 0.3.1 (2015/09/09) - Ensures files are not needlessly reloaded with newer versions of YORM. -0.3 (2015/06/26) ----------------- +## 0.3 (2015/06/26) - Added `--no-clean` option to disable removing untracked files. - Added support for `rev-parse` dates as the dependency `rev`. -0.2.5 (2015/06/15) ------------------- +## 0.2.5 (2015/06/15) - Added `--quiet` option to hide warnings. -0.2.4 (2015/05/19) ------------------- +## 0.2.4 (2015/05/19) - Now hiding YORM logging bellow warnings. -0.2.3 (2015/05/17) ------------------- +## 0.2.3 (2015/05/17) - Upgraded to YORM v0.4. -0.2.2 (2015/05/04) ------------------- +## 0.2.2 (2015/05/04) - Specified YORM < v0.4. -0.2.1 (2015/03/12) ------------------- +## 0.2.1 (2015/03/12) - Added automatic remote branch tracking in dependencies. - Now requiring `--force` when there are untracked files. -0.2 (2015/03/10) ----------------- +## 0.2 (2015/03/10) - Added `list` command to display current URLs/SHAs. -0.1.4 (2014/02/27) ------------------- +## 0.1.4 (2014/02/27) - Fixed an outdated index when checking for changes. -0.1.3 (2014/02/27) ------------------- +## 0.1.3 (2014/02/27) - Fixed extra whitespace when logging shell output. -0.1.2 (2014/02/27) ------------------- +## 0.1.2 (2014/02/27) - Added `--force` argument to: - overwrite uncommitted changes - create symbolic links in place of directories - Added live shell command output with `-vv` argument. -0.1 (2014/02/24) ----------------- +## 0.1 (2014/02/24) - Initial release. diff --git a/Makefile b/Makefile index 02383623..7547ed71 100644 --- a/Makefile +++ b/Makefile @@ -9,11 +9,6 @@ ifndef TRAVIS PYTHON_MINOR ?= 5 endif -# Test settings -UNIT_TEST_COVERAGE := 73 -INTEGRATION_TEST_COVERAGE := 79 -COMBINED_TEST_COVERAGE := 97 - # System paths PLATFORM := $(shell python -c 'import sys; print(sys.platform)') ifneq ($(findstring win32, $(PLATFORM)), ) @@ -68,6 +63,7 @@ PYREVERSE := $(BIN_)pyreverse NOSE := $(BIN_)nosetests PYTEST := $(BIN_)py.test COVERAGE := $(BIN_)coverage +COVERAGE_SPACE := $(BIN_)coverage.space SNIFFER := $(BIN_)sniffer HONCHO := $(ACTIVATE) && honcho @@ -119,7 +115,7 @@ depends: depends-ci depends-doc depends-dev .PHONY: depends-ci depends-ci: env Makefile $(DEPENDS_CI_FLAG) $(DEPENDS_CI_FLAG): Makefile - $(PIP) install --upgrade pep8 pep257 pylint coverage pytest pytest-describe pytest-expecter pytest-cov pytest-random freezegun + $(PIP) install --upgrade pep8 pep257 pylint coverage coverage.space pytest pytest-describe pytest-expecter pytest-cov pytest-random freezegun @ touch $(DEPENDS_CI_FLAG) # flag to indicate dependencies are installed .PHONY: depends-doc @@ -171,7 +167,7 @@ README-pypi.html: README.rst .PHONY: verify-readme verify-readme: $(DOCS_FLAG) -$(DOCS_FLAG): README.rst CHANGES.rst +$(DOCS_FLAG): README.rst CHANGELOG.rst $(PYTHON) setup.py check --restructuredtext --strict --metadata @ touch $(DOCS_FLAG) # flag to indicate README has been checked @@ -219,11 +215,11 @@ fix: depends-dev RANDOM_SEED ?= $(shell date +%s) PYTEST_CORE_OPTS := --verbose -r xXw --maxfail=3 -PYTEST_COV_OPTS := --cov=$(PACKAGE) --no-cov-on-fail --cov-report=term-missing +PYTEST_COV_OPTS := --cov=$(PACKAGE) --no-cov-on-fail --cov-report=term-missing --cov-report=html PYTEST_RANDOM_OPTS := --random --random-seed=$(RANDOM_SEED) PYTEST_OPTS := $(PYTEST_CORE_OPTS) $(PYTEST_COV_OPTS) $(PYTEST_RANDOM_OPTS) -PYTEST_OPTS_FAILFAST := $(PYTEST_OPTS) --failed --exitfirst +PYTEST_OPTS_FAILFAST := $(PYTEST_OPTS) --last-failed --exitfirst FAILURES := .cache/v/cache/lastfailed @@ -234,7 +230,7 @@ test-unit: depends-ci $(PYTEST) $(PYTEST_OPTS) $(PACKAGE) @- mv $(FAILURES).bak $(FAILURES) ifndef TRAVIS - $(COVERAGE) html --directory htmlcov --fail-under=$(UNIT_TEST_COVERAGE) + $(COVERAGE_SPACE) jacebrowning/gitman unit endif .PHONY: test-int @@ -242,7 +238,7 @@ test-int: depends-ci @ if test -e $(FAILURES); then $(PYTEST) $(PYTEST_OPTS_FAILFAST) tests; fi $(PYTEST) $(PYTEST_OPTS) tests ifndef TRAVIS - $(COVERAGE) html --directory htmlcov --fail-under=$(INTEGRATION_TEST_COVERAGE) + $(COVERAGE_SPACE) jacebrowning/gitman integration endif .PHONY: tests test-all @@ -251,7 +247,7 @@ test-all: depends-ci @ if test -e $(FAILURES); then $(PYTEST) $(PYTEST_OPTS_FAILFAST) $(PACKAGE) tests; fi $(PYTEST) $(PYTEST_OPTS) $(PACKAGE) tests ifndef TRAVIS - $(COVERAGE) html --directory htmlcov --fail-under=$(COMBINED_TEST_COVERAGE) + $(COVERAGE_SPACE) jacebrowning/gitman overall endif .PHONY: read-coverage diff --git a/README.md b/README.md index adc5eee8..449e1ac3 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,9 @@ GitMan is a language-agnostic "dependency manager" using Git. It aims to serve a ## Requirements -* Python 3.4+ -* Latest version of Git (with [stored credentials](http://git-dependency-manager.info/setup/git/)) -* OSX/Linux (with a decent shell for Git) +* Python 3.5+ +* Git 1.8+ (with [stored credentials](http://git-dependency-manager.info/setup/git/)) +* Unix shell (or Cygwin/MinGW/etc. on Windows) ## Installation @@ -37,11 +37,11 @@ Create a configuration file (`gitman.yml` or `.gitman.yml`) in the root of your ```yaml location: vendor sources: -- repo: https://github.com/kstenerud/iOS-Universal-Framework - dir: framework +- name: framework + repo: https://github.com/kstenerud/iOS-Universal-Framework rev: Mk5-end-of-life -- repo: https://github.com/jonreid/XcodeCoverage - dir: coverage +- name: coverage + repo: https://github.com/jonreid/XcodeCoverage rev: master link: Tools/XcodeCoverage ``` @@ -70,9 +70,9 @@ $ gitman update which will essentially: -1. create a working tree at _root_/`location`/`dir` +1. create a working tree at _root_/`location`/`name` 2. fetch from `repo` and checkout the specified `rev` -3. symbolically link each `location`/`dir` from _root_/`link` (if specified) +3. symbolically link each `location`/`name` from _root_/`link` (if specified) 4. repeat for all nested working trees containing a configuration file 5. record the actual commit SHAs that were checked out (with `--lock` option) diff --git a/docs/about/changelog.md b/docs/about/changelog.md new file mode 120000 index 00000000..699cc9e7 --- /dev/null +++ b/docs/about/changelog.md @@ -0,0 +1 @@ +../../CHANGELOG.md \ No newline at end of file diff --git a/docs/about/changes.md b/docs/about/changes.md deleted file mode 120000 index 8980b4a7..00000000 --- a/docs/about/changes.md +++ /dev/null @@ -1 +0,0 @@ -../../CHANGES.md \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 1ab770c0..2a117950 100644 --- a/docs/index.md +++ b/docs/index.md @@ -4,9 +4,9 @@ GitMan is a language-agnostic "dependency manager" using Git. It aims to serve a ## Requirements -* Python 3.4+ -* Latest version of Git (with [stored credentials](setup/git/#stored-credentials)) -* OSX/Linux (with a decent shell for Git) +* Python 3.5+ +* Git 1.8+ (with [stored credentials](setup/git/#stored-credentials)) +* Unix shell (or Cygwin/MinGW/etc. on Windows) ## Installation @@ -31,11 +31,11 @@ Create a configuration file (`gitman.yml` or `.gitman.yml`) in the root of your ```yaml location: vendor sources: -- repo: https://github.com/kstenerud/iOS-Universal-Framework - dir: framework +- name: framework + repo: https://github.com/kstenerud/iOS-Universal-Framework rev: Mk5-end-of-life -- repo: https://github.com/jonreid/XcodeCoverage - dir: coverage +- name: coverage + repo: https://github.com/jonreid/XcodeCoverage rev: master link: Tools/XcodeCoverage ``` @@ -56,9 +56,9 @@ $ gitman install which will essentially: -1. create a working tree at _root_/`location`/`dir` +1. create a working tree at _root_/`location`/`name` 2. fetch from `repo` and checkout the specified `rev` -3. symbolically link each `location`/`dir` from _root_/`link` (if specified) +3. symbolically link each `location`/`name` from _root_/`link` (if specified) 4. repeat for all nested working trees containing a configuration file where `rev` can be: diff --git a/docs/use-cases/branch-tracking.md b/docs/use-cases/branch-tracking.md index 6e42c671..c846890d 100644 --- a/docs/use-cases/branch-tracking.md +++ b/docs/use-cases/branch-tracking.md @@ -9,13 +9,11 @@ A web app's `gitman.yml` might look something like: ```yaml location: vendor sources: -- dir: api - link: '' +- name: api repo: https://github.com/example/api rev: develop sources_locked: -- dir: api - link: '' +- name: api repo: https://github.com/example/api rev: b2730855c9efaaa7448b25b82e5a4363785c83ed ``` diff --git a/docs/use-cases/build-integration.md b/docs/use-cases/build-integration.md index 8d77603e..3eda49ea 100644 --- a/docs/use-cases/build-integration.md +++ b/docs/use-cases/build-integration.md @@ -26,19 +26,19 @@ clean: using a configuration file similar to: ```yaml -location: sources +location: vendor sources: -- dir: lib_foo +- name: lib_foo repo: https://github.com/example/lib_foo rev: develop -- dir: lib_bar +- name: lib_bar repo: https://github.com/example/lib_bar rev: master sources_locked: -- dir: lib_foo +- name: lib_foo repo: https://github.com/example/lib_foo rev: 73cb3668d4c9c3388fb21de16c9c3f6217cc0e1c -- dir: lib_bar +- name: lib_bar repo: https://github.com/example/lib_bar rev: 560ea99953a4b3e393e170e07895d14904eb032c ``` diff --git a/docs/use-cases/linked-features.md b/docs/use-cases/linked-features.md index a2d61dc2..78fcdb2a 100644 --- a/docs/use-cases/linked-features.md +++ b/docs/use-cases/linked-features.md @@ -10,13 +10,11 @@ By manually modifying the `sources_locked` section, a particular version of the ```yaml location: vendor sources: -- dir: api - link: '' +- name: api repo: https://github.com/example/api rev: develop sources_locked: -- dir: api - link: '' +- name: api repo: https://github.com/example/api rev: feature/authenticate-with-github # related feature branch in the API ``` diff --git a/docs/use-cases/submodules.md b/docs/use-cases/submodules.md index 6e40389c..7bba5f05 100644 --- a/docs/use-cases/submodules.md +++ b/docs/use-cases/submodules.md @@ -19,8 +19,8 @@ To get the same behavior using `gitman`, first delete the `.gitmodules` file and ```yaml location: .gitman sources: -- repo: - dir: my_dependency +- name: my_dependency + repo: rev: a943a702d06f34599aee1f8da8ef9f7296031d69 link: vendor/my_depenendy ``` diff --git a/gitman/__init__.py b/gitman/__init__.py index 2881ddfa..4e4e8b17 100644 --- a/gitman/__init__.py +++ b/gitman/__init__.py @@ -3,7 +3,7 @@ import sys __project__ = 'GitMan' -__version__ = '0.10' +__version__ = '0.11' CLI = 'gitman' PLUGIN = 'deps' @@ -11,7 +11,7 @@ VERSION = __project__ + ' v' + __version__ DESCRIPTION = "A language-agnostic dependency manager using Git." -PYTHON_VERSION = 3, 4 +PYTHON_VERSION = 3, 5 if sys.version_info < PYTHON_VERSION: # pragma: no cover (manual test) sys.exit("Python {}.{}+ is required.".format(*PYTHON_VERSION)) diff --git a/gitman/common.py b/gitman/common.py index c235a4ff..f0e7f5f3 100644 --- a/gitman/common.py +++ b/gitman/common.py @@ -85,7 +85,6 @@ def configure_logging(count=0): datefmt=settings.LOGGING_DATEFMT) logging.root.handlers[0].setFormatter(formatter) logging.getLogger('yorm').setLevel(max(level, settings.YORM_LOGGING_LEVEL)) - logging.getLogger('sh').setLevel(max(level, settings.SH_LOGGING_LEVEL)) # Warn about excessive verbosity if count > _Config.MAX_VERBOSITY: diff --git a/gitman/git.py b/gitman/git.py index a462c837..d44d7faf 100644 --- a/gitman/git.py +++ b/gitman/git.py @@ -57,8 +57,7 @@ def changes(include_untracked=False, display_status=True, _show=False): git('diff-index', '--quiet', 'HEAD', _show=_show) # check for untracked files - output = git('ls-files', '--others', '--exclude-standard', - _show=_show, _capture=True) + output = git('ls-files', '--others', '--exclude-standard', _show=_show) except ShellError: status = True @@ -67,7 +66,7 @@ def changes(include_untracked=False, display_status=True, _show=False): status = bool(output.splitlines()) and include_untracked if status and display_status: - for line in git('status', _show=True, _capture=True).splitlines(): + for line in git('status', _show=True).splitlines(): common.show(line) return status @@ -92,25 +91,22 @@ def update(rev, *, clean=True, fetch=False): # pylint: disable=redefined-outer- def get_url(): """Get the current repository's URL.""" - return git('config', '--get', 'remote.origin.url', - _show=False, _capture=True) + return git('config', '--get', 'remote.origin.url', _show=False) def get_hash(_show=False): """Get the current working tree's hash.""" - return git('rev-parse', 'HEAD', _show=_show, _capture=True) + return git('rev-parse', 'HEAD', _show=_show) def get_tag(): """Get the current working tree's tag (if on a tag).""" - return git('describe', '--tags', '--exact-match', - _show=False, _ignore=True, _capture=True) + return git('describe', '--tags', '--exact-match', _show=False, _ignore=True) def get_branch(): """Get the current working tree's branch.""" - return git('rev-parse', '--abbrev-ref', 'HEAD', - _show=False, _capture=True) + return git('rev-parse', '--abbrev-ref', 'HEAD', _show=False) def _get_sha_from_rev(rev): @@ -121,5 +117,5 @@ def _get_sha_from_rev(rev): date = parts[1].strip("{}") git('checkout', '--force', branch, _show=False) rev = git('rev-list', '-n', '1', '--before={!r}'.format(date), - branch, _show=False, _capture=True) + branch, _show=False) return rev diff --git a/gitman/models/config.py b/gitman/models/config.py index 38f525f9..05a4cce8 100644 --- a/gitman/models/config.py +++ b/gitman/models/config.py @@ -22,7 +22,7 @@ class Config: LOG = "gitman.log" - def __init__(self, root, filename="gitman.yml", location="gdm_sources"): + def __init__(self, root, filename="gitman.yml", location="gitman_sources"): super().__init__() self.root = root self.filename = filename @@ -71,16 +71,16 @@ def install_deps(self, *names, depth=None, shell.cd(self.location_path) sources = self._get_sources(use_locked=False if update else None) - dirs = list(names) if names else [source.dir for source in sources] + dirs = list(names) if names else [source.name for source in sources] common.show() common.indent() count = 0 for source in sources: - if source.dir in dirs: - dirs.remove(source.dir) + if source.name in dirs: + dirs.remove(source.name) else: - log.info("Skipped dependency: %s", source.dir) + log.info("Skipped dependency: %s", source.name) continue source.update_files(force=force, fetch=fetch, clean=clean) @@ -118,12 +118,12 @@ def lock_deps(self, *names, obey_existing=True): common.indent() sources = self._get_sources(use_locked=obey_existing).copy() - dirs = list(names) if names else [source.dir for source in sources] + dirs = list(names) if names else [source.name for source in sources] count = 0 for source in sources: - if source.dir not in dirs: - log.info("Skipped dependency: %s", source.dir) + if source.name not in dirs: + log.info("Skipped dependency: %s", source.name) continue try: @@ -160,7 +160,7 @@ def get_deps(self, depth=None, allow_dirty=True): for source in self.sources: if depth == 0: - log.info("Skipped dependency: %s", source.dir) + log.info("Skipped dependency: %s", source.name) continue yield source.identify(allow_dirty=allow_dirty) @@ -207,7 +207,7 @@ def _get_sources(self, *, use_locked=None): extras = [] for source in self.sources + self.sources_locked: if source not in sources: - log.info("Source %r missing from selected section", source.dir) + log.info("Source %r missing from selected section", source.name) extras.append(source) return sources + extras @@ -232,4 +232,4 @@ def _valid_filename(filename): name, ext = os.path.splitext(filename.lower()) if name.startswith('.'): name = name[1:] - return name in ('gitman', 'gdm') and ext in ('.yml', '.yaml') + return name in ['gitman', 'gdm'] and ext in ['.yml', '.yaml'] diff --git a/gitman/models/source.py b/gitman/models/source.py index a2977fae..5c5aa854 100644 --- a/gitman/models/source.py +++ b/gitman/models/source.py @@ -15,7 +15,7 @@ log = logging.getLogger(__name__) -@yorm.attr(dir=String) +@yorm.attr(name=String) @yorm.attr(link=String) @yorm.attr(repo=String) @yorm.attr(rev=String) @@ -28,13 +28,13 @@ class Source(AttributeDictionary): def __init__(self, repo, name, rev='master', link=None): super().__init__() self.repo = repo - self.dir = name + self.name = name self.rev = rev self.link = link if not self.repo: raise InvalidConfig("'repo' missing on {}".format(repr(self))) - if not self.dir: - raise InvalidConfig("'dir' missing on {}".format(repr(self))) + if not self.name: + raise InvalidConfig("'name' missing on {}".format(repr(self))) def __repr__(self): return "".format(self) @@ -43,26 +43,26 @@ def __str__(self): fmt = "'{r}' @ '{v}' in '{d}'" if self.link: fmt += " <- '{s}'" - return fmt.format(r=self.repo, v=self.rev, d=self.dir, s=self.link) + return fmt.format(r=self.repo, v=self.rev, d=self.name, s=self.link) def __eq__(self, other): - return self.dir == other.dir + return self.name == other.name def __ne__(self, other): - return self.dir != other.dir + return self.name != other.name def __lt__(self, other): - return self.dir < other.dir + return self.name < other.name def update_files(self, force=False, fetch=False, clean=True): """Ensure the source matches the specified revision.""" log.info("Updating source files...") # Enter the working tree - if not os.path.exists(self.dir): + if not os.path.exists(self.name): log.debug("Creating a new repository...") - git.clone(self.repo, self.dir) - shell.cd(self.dir) + git.clone(self.repo, self.name) + shell.cd(self.name) # Check for uncommitted changes if not force: @@ -100,9 +100,9 @@ def create_link(self, root, force=False): def identify(self, allow_dirty=True, allow_missing=True): """Get the path and current repository URL and hash.""" - if os.path.isdir(self.dir): + if os.path.isdir(self.name): - shell.cd(self.dir) + shell.cd(self.name) path = os.getcwd() url = git.get_url() @@ -124,12 +124,12 @@ def identify(self, allow_dirty=True, allow_missing=True): else: - path = os.path.join(os.getcwd(), self.dir) + path = os.path.join(os.getcwd(), self.name) msg = "Not a valid repository: {}".format(path) raise InvalidRepository(msg) def lock(self): """Return a locked version of the current source.""" _, _, revision = self.identify(allow_missing=False) - source = self.__class__(self.repo, self.dir, revision, self.link) + source = self.__class__(self.repo, self.name, revision, self.link) return source diff --git a/gitman/shell.py b/gitman/shell.py index 26be3996..d052306f 100644 --- a/gitman/shell.py +++ b/gitman/shell.py @@ -1,10 +1,9 @@ """Utilities to call shell programs.""" import os +import subprocess import logging -from sh import Command, ErrorReturnCode - from . import common from .exceptions import ShellError @@ -14,32 +13,47 @@ log = logging.getLogger(__name__) -def call(name, *args, _show=True, _capture=False, _ignore=False): - """Call a shell program with arguments.""" - msg = CMD_PREFIX + ' '.join([name] + list(args)) +def call(name, *args, _show=True, _ignore=False): + """Call a shell program with arguments. + + :param name: name of program to call + :param args: list of command-line arguments + :param _show: display the call on stdout + :param _ignore: ignore non-zero return codes + + """ + program = CMD_PREFIX + ' '.join([name, *args]) if _show: - common.show(msg) + common.show(program) + else: + log.debug(program) + + if name == 'cd': + return os.chdir(args[0]) # 'cd' has no effect in a subprocess + + command = subprocess.run( + [name, *args], universal_newlines=True, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + ) + + for line in command.stdout.splitlines(): + log.debug(OUT_PREFIX + line.strip()) + + if command.returncode == 0: + return command.stdout.strip() + + elif _ignore: + log.debug("Ignored error from call to '%s'", program) + else: - log.debug(msg) - - if name == 'cd' and len(args) == 1: - return os.chdir(args[0]) - - try: - program = Command(name) - if _capture: - line = program(*args).strip() - log.debug(OUT_PREFIX + line) - return line - else: - for line in program(*args, _iter='err'): - log.debug(OUT_PREFIX + line.strip()) - except ErrorReturnCode as exc: - msg = "\n IN: '{}'{}".format(os.getcwd(), exc) - if _ignore: - log.debug("Ignored error from call to '%s'", name) - else: - raise ShellError(msg) + message = ( + "An external program call failed." + "\n\n" + "In worikng directory: " + os.getcwd() + "\n\n" + "The following command produced a non-zero return code:" + "\n\n" + + program + "\n" + + command.stdout + ) + raise ShellError(message) def mkdir(path): diff --git a/gitman/test/conftest.py b/gitman/test/conftest.py index 73f9e42d..086779fb 100644 --- a/gitman/test/conftest.py +++ b/gitman/test/conftest.py @@ -21,7 +21,6 @@ def pytest_configure(config): format="[%(levelname)-8s] (%(name)s @%(lineno)4d) %(message)s", ) logging.getLogger('yorm').setLevel(logging.WARNING) - logging.getLogger('sh').setLevel(logging.WARNING) terminal = config.pluginmanager.getplugin('terminal') diff --git a/gitman/test/files/gdm-custom.yml b/gitman/test/files/gdm-custom.yml index be43e5d4..3bc2c547 100644 --- a/gitman/test/files/gdm-custom.yml +++ b/gitman/test/files/gdm-custom.yml @@ -1,10 +1,10 @@ location: dependencies sources: - repo: https://github.com/jacebrowning/gitman - dir: gitman_1 + name: gitman_1 rev: fb693447579235391a45ca170959b5583c5042d8 link: src/gitman_a - repo: https://github.com/jacebrowning/gitman - dir: gitman_2 + name: gitman_2 rev: master link: src/gitman_b diff --git a/gitman/test/files/gdm-default.yml b/gitman/test/files/gdm-default.yml index f27da6e2..f9cb4201 100644 --- a/gitman/test/files/gdm-default.yml +++ b/gitman/test/files/gdm-default.yml @@ -1,5 +1,5 @@ location: gitman_modules sources: - repo: https://github.com/jacebrowning/gitman - dir: gitman_1 + name: gitman_1 rev: fb693447579235391a45ca170959b5583c5042d8 diff --git a/gitman/test/files/gitman.yml b/gitman/test/files/gitman.yml index bb570751..c04cb98d 100644 --- a/gitman/test/files/gitman.yml +++ b/gitman/test/files/gitman.yml @@ -1,27 +1,27 @@ location: /tmp/gitman-test-dependencies sources: -- dir: gitman_1 +- name: gitman_1 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: example-branch -- dir: gitman_2 +- name: gitman_2 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: example-tag -- dir: gitman_3 +- name: gitman_3 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: master@{2015-06-18 11:11:11} sources_locked: -- dir: gitman_1 +- name: gitman_1 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: eb37743011a398b208dd9f9ef79a408c0fc10d48 -- dir: gitman_2 +- name: gitman_2 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: 7bd138fe7359561a8c2ff9d195dff238794ccc04 -- dir: gitman_3 +- name: gitman_3 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: 2da24fca34af3748e3cab61db81a2ae8b35aec94 diff --git a/gitman/test/test_models_config.py b/gitman/test/test_models_config.py index 6174ea17..e47677b6 100644 --- a/gitman/test/test_models_config.py +++ b/gitman/test/test_models_config.py @@ -16,7 +16,7 @@ def test_init_defaults(self): assert 'mock/root' == config.root assert 'gitman.yml' == config.filename - assert 'gdm_sources' == config.location + assert 'gitman_sources' == config.location assert [] == config.sources def test_init_filename(self): @@ -24,7 +24,7 @@ def test_init_filename(self): config = Config('mock/root', 'mock.custom') assert 'mock.custom' == config.filename - assert 'gdm_sources' == config.location + assert 'gitman_sources' == config.location def test_init_location(self): """Verify the location can be customized.""" diff --git a/gitman/test/test_models_source.py b/gitman/test/test_models_source.py index 6fd2670a..259d8a33 100644 --- a/gitman/test/test_models_source.py +++ b/gitman/test/test_models_source.py @@ -20,7 +20,7 @@ def test_init_defaults(self): source = Source('http://mock.git', 'mock_dir') assert 'http://mock.git' == source.repo - assert 'mock_dir' == source.dir + assert 'mock_dir' == source.name assert 'master' == source.rev assert None is source.link @@ -57,7 +57,7 @@ def test_eq(self, source): source2 = copy(source) assert source == source2 - source2.dir = "dir2" + source2.name = "dir2" assert source != source2 def test_lt(self): @@ -83,4 +83,4 @@ def test_lock_uses_the_identity_rev(self, source): source2 = source.lock() assert 'abc123' == source2.rev - assert 'name' == source2.dir + assert 'name' == source2.name diff --git a/gitman/test/test_shell.py b/gitman/test/test_shell.py index 28b1e7a6..7f73487a 100644 --- a/gitman/test/test_shell.py +++ b/gitman/test/test_shell.py @@ -17,14 +17,8 @@ class TestCall: @patch('os.chdir') def test_cd(self, mock_chdir): """Verify directories are changed correctly.""" - shell.call('cd', 'mock/dir') - mock_chdir.assert_called_once_with('mock/dir') - - @patch('gitman.shell.Command') - def test_other(self, mock_command): - """Verify directories are changed correctly.""" - shell.call('mock_program') - mock_command.assert_called_once_with('mock_program') + shell.call('cd', 'mock/name') + mock_chdir.assert_called_once_with('mock/name') def test_other_error_uncaught(self): """Verify program errors raise exceptions.""" @@ -37,7 +31,7 @@ def test_other_error_ignored(self): def test_other_capture(self): """Verify a program's output can be captured.""" - stdout = shell.call('echo', 'Hello, world!\n', _capture=True) + stdout = shell.call('echo', 'Hello, world!\n') assert "Hello, world!" == stdout @@ -48,13 +42,13 @@ class TestPrograms: def test_mkdir(self, mock_call): """Verify the commands to create directories.""" - shell.mkdir('mock/dir/path') - assert_calls(mock_call, ["mkdir -p mock/dir/path"]) + shell.mkdir('mock/name/path') + assert_calls(mock_call, ["mkdir -p mock/name/path"]) def test_cd(self, mock_call): """Verify the commands to change directories.""" - shell.cd('mock/dir/path') - assert_calls(mock_call, ["cd mock/dir/path"]) + shell.cd('mock/name/path') + assert_calls(mock_call, ["cd mock/name/path"]) @patch('os.path.isdir', Mock(return_value=True)) def test_ln(self, mock_call): @@ -71,5 +65,5 @@ def test_ln_missing_parent(self, mock_call): def test_rm(self, mock_call): """Verify the commands to delete files/folders.""" - shell.rm('mock/dir/path') - assert_calls(mock_call, ["rm -rf mock/dir/path"]) + shell.rm('mock/name/path') + assert_calls(mock_call, ["rm -rf mock/name/path"]) diff --git a/mkdocs.yml b/mkdocs.yml index c8043d64..b5dbb959 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -20,6 +20,6 @@ pages: - 'Linking Feature Branches': 'use-cases/linked-features.md' - 'Build System Integration': 'use-cases/build-integration.md' - About: - - 'Release Notes': 'about/changes.md' + - 'Release Notes': 'about/changelog.md' - Contributing: 'about/contributing.md' - License: 'about/license.md' diff --git a/requirements.txt b/requirements.txt index 9faa8628..255402dc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1 @@ YORM ~= 0.8 -sh ~= 1.11 diff --git a/setup.py b/setup.py index 2df66a15..a090b9c9 100644 --- a/setup.py +++ b/setup.py @@ -6,13 +6,13 @@ from gitman import __project__, __version__, CLI, PLUGIN, DESCRIPTION -import os -if os.path.exists('README.rst'): - README = open('README.rst').read() +try: + README = open("README.rst").read() + CHANGELOG = open("CHANGELOG.rst").read() +except IOError: + LONG_DESCRIPTION = "" else: - README = "" # a placeholder, README is generated on release -CHANGES = open('CHANGES.md').read() - + LONG_DESCRIPTION = README + '\n' + CHANGELOG setuptools.setup( name=__project__, @@ -32,7 +32,7 @@ 'gdm = gitman.cli:main', ]}, - long_description=(README + '\n' + CHANGES), + long_description=LONG_DESCRIPTION, license='MIT', classifiers=[ 'Development Status :: 4 - Beta', @@ -44,7 +44,6 @@ 'Operating System :: POSIX', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Topic :: Software Development', 'Topic :: Software Development :: Build Tools', diff --git a/tests/files/gdm.yml b/tests/files/gdm.yml index e100f9e8..cc378c39 100644 --- a/tests/files/gdm.yml +++ b/tests/files/gdm.yml @@ -1,27 +1,27 @@ location: /tmp/gitman-test-dependencies sources: -- dir: gitman_1 +- name: gitman_1 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: example-branch -- dir: gitman_2 +- name: gitman_2 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: example-tag -- dir: gitman_3 +- name: gitman_3 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: master@{2015-06-16 10:30:30} sources_locked: -- dir: gitman_1 +- name: gitman_1 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: eb37743011a398b208dd9f9ef79a408c0fc10d48 -- dir: gitman_2 +- name: gitman_2 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: 7bd138fe7359561a8c2ff9d195dff238794ccc04 -- dir: gitman_3 +- name: gitman_3 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: 9bf18e16b956041f0267c21baad555a23237b52e diff --git a/tests/test_api.py b/tests/test_api.py index 41bab24c..94e99d12 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -19,15 +19,15 @@ CONFIG = """ location: deps sources: -- dir: gitman_1 +- name: gitman_1 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: example-branch -- dir: gitman_2 +- name: gitman_2 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: example-tag -- dir: gitman_3 +- name: gitman_3 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: 9bf18e16b956041f0267c21baad555a23237b52e @@ -72,16 +72,16 @@ def it_merges_sources(config): config.__mapper__.text = strip(""" location: deps sources: - - dir: gitman_1 + - name: gitman_1 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: example-branch sources_locked: - - dir: gitman_2 + - name: gitman_2 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: example-branch - - dir: gitman_3 + - name: gitman_3 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: 7bd138fe7359561a8c2ff9d195dff238794ccc04 @@ -95,12 +95,12 @@ def it_can_handle_missing_locked_sources(config): config.__mapper__.text = strip(""" location: deps sources: - - dir: gitman_1 + - name: gitman_1 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: example-branch sources_locked: - - dir: gitman_2 + - name: gitman_2 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: 7bd138fe7359561a8c2ff9d195dff238794ccc04 @@ -117,7 +117,7 @@ def config_with_link(config): config.__mapper__.text = strip(""" location: deps sources: - - dir: gitman_1 + - name: gitman_1 link: my_link repo: https://github.com/jacebrowning/gitman-demo rev: 7bd138fe7359561a8c2ff9d195dff238794ccc04 @@ -177,16 +177,16 @@ def it_locks_previously_locked_dependnecies(config): config.__mapper__.text = strip(""" location: deps sources: - - dir: gitman_1 + - name: gitman_1 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: example-branch - - dir: gitman_2 + - name: gitman_2 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: example-tag sources_locked: - - dir: gitman_2 + - name: gitman_2 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: (old revision) @@ -197,16 +197,16 @@ def it_locks_previously_locked_dependnecies(config): expect(config.__mapper__.text) == strip(""" location: deps sources: - - dir: gitman_1 + - name: gitman_1 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: example-branch - - dir: gitman_2 + - name: gitman_2 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: example-tag sources_locked: - - dir: gitman_2 + - name: gitman_2 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: 7bd138fe7359561a8c2ff9d195dff238794ccc04 @@ -216,16 +216,16 @@ def it_should_not_lock_dependnecies_when_disabled(config): config.__mapper__.text = strip(""" location: deps sources: - - dir: gitman_1 + - name: gitman_1 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: example-branch - - dir: gitman_2 + - name: gitman_2 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: example-tag sources_locked: - - dir: gitman_2 + - name: gitman_2 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: (old revision) @@ -236,16 +236,16 @@ def it_should_not_lock_dependnecies_when_disabled(config): expect(config.__mapper__.text) == strip(""" location: deps sources: - - dir: gitman_1 + - name: gitman_1 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: example-branch - - dir: gitman_2 + - name: gitman_2 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: example-tag sources_locked: - - dir: gitman_2 + - name: gitman_2 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: (old revision) @@ -256,15 +256,15 @@ def it_should_lock_all_dependencies_when_enabled(config): expect(config.__mapper__.text) == CONFIG + strip(""" sources_locked: - - dir: gitman_1 + - name: gitman_1 link: '' repo: https://github.com/jacebrowning/gitman-demo - rev: eb37743011a398b208dd9f9ef79a408c0fc10d48 - - dir: gitman_2 + rev: 1de84ca1d315f81b035cd7b0ecf87ca2025cdacd + - name: gitman_2 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: 7bd138fe7359561a8c2ff9d195dff238794ccc04 - - dir: gitman_3 + - name: gitman_3 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: 9bf18e16b956041f0267c21baad555a23237b52e @@ -277,15 +277,15 @@ def describe_list(): def it_updates_the_log(config): gitman.install() gitman.list() - with open(config.log_path) as fin: - contents = fin.read().replace("/private", "") + with open(config.log_path) as stream: + contents = stream.read().replace("/private", "") expect(contents) == strip(""" 2012-01-14 12:00:01 - /tmp/gitman-shared/deps/gitman_1: https://github.com/jacebrowning/gitman-demo @ eb37743011a398b208dd9f9ef79a408c0fc10d48 - /tmp/gitman-shared/deps/gitman_1/gdm_sources/gdm_3: https://github.com/jacebrowning/gdm-demo @ ddbe17ef173538d1fda29bd99a14bab3c5d86e78 - /tmp/gitman-shared/deps/gitman_1/gdm_sources/gdm_3/gdm_sources/gdm_3: https://github.com/jacebrowning/gdm-demo @ fb693447579235391a45ca170959b5583c5042d8 - /tmp/gitman-shared/deps/gitman_1/gdm_sources/gdm_3/gdm_sources/gdm_4: https://github.com/jacebrowning/gdm-demo @ 63ddfd82d308ddae72d31b61cb8942c898fa05b5 - /tmp/gitman-shared/deps/gitman_1/gdm_sources/gdm_4: https://github.com/jacebrowning/gdm-demo @ 63ddfd82d308ddae72d31b61cb8942c898fa05b5 + /tmp/gitman-shared/deps/gitman_1: https://github.com/jacebrowning/gitman-demo @ 1de84ca1d315f81b035cd7b0ecf87ca2025cdacd + /tmp/gitman-shared/deps/gitman_1/gitman_sources/gdm_3: https://github.com/jacebrowning/gdm-demo @ 050290bca3f14e13fd616604202b579853e7bfb0 + /tmp/gitman-shared/deps/gitman_1/gitman_sources/gdm_3/gitman_sources/gdm_3: https://github.com/jacebrowning/gdm-demo @ fb693447579235391a45ca170959b5583c5042d8 + /tmp/gitman-shared/deps/gitman_1/gitman_sources/gdm_3/gitman_sources/gdm_4: https://github.com/jacebrowning/gdm-demo @ 63ddfd82d308ddae72d31b61cb8942c898fa05b5 + /tmp/gitman-shared/deps/gitman_1/gitman_sources/gdm_4: https://github.com/jacebrowning/gdm-demo @ 63ddfd82d308ddae72d31b61cb8942c898fa05b5 /tmp/gitman-shared/deps/gitman_2: https://github.com/jacebrowning/gitman-demo @ 7bd138fe7359561a8c2ff9d195dff238794ccc04 /tmp/gitman-shared/deps/gitman_3: https://github.com/jacebrowning/gitman-demo @ 9bf18e16b956041f0267c21baad555a23237b52e """, end='\n\n') @@ -299,15 +299,15 @@ def it_records_all_versions_when_no_arguments(config): expect(config.__mapper__.text) == CONFIG + strip(""" sources_locked: - - dir: gitman_1 + - name: gitman_1 link: '' repo: https://github.com/jacebrowning/gitman-demo - rev: eb37743011a398b208dd9f9ef79a408c0fc10d48 - - dir: gitman_2 + rev: 1de84ca1d315f81b035cd7b0ecf87ca2025cdacd + - name: gitman_2 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: 7bd138fe7359561a8c2ff9d195dff238794ccc04 - - dir: gitman_3 + - name: gitman_3 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: 9bf18e16b956041f0267c21baad555a23237b52e @@ -319,11 +319,11 @@ def it_records_specified_dependencies(config): expect(config.__mapper__.text) == CONFIG + strip(""" sources_locked: - - dir: gitman_1 + - name: gitman_1 link: '' repo: https://github.com/jacebrowning/gitman-demo - rev: eb37743011a398b208dd9f9ef79a408c0fc10d48 - - dir: gitman_3 + rev: 1de84ca1d315f81b035cd7b0ecf87ca2025cdacd + - name: gitman_3 link: '' repo: https://github.com/jacebrowning/gitman-demo rev: 9bf18e16b956041f0267c21baad555a23237b52e