Skip to content

Commit

Permalink
Merge pull request #92 from jacebrowning/release/v0.8
Browse files Browse the repository at this point in the history
Release v0.8
  • Loading branch information
jacebrowning committed Jan 14, 2016
2 parents f1c90af + 408aa9c commit d6fc1e5
Show file tree
Hide file tree
Showing 23 changed files with 657 additions and 499 deletions.
8 changes: 8 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
Revision History
================

0.8 (2015/10/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)
----------------

Expand Down
2 changes: 1 addition & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**The MIT License (MIT)**

Copyright © 2015, Jace Browning
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
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ ifndef TRAVIS
endif

# Test settings
UNIT_TEST_COVERAGE := 72
UNIT_TEST_COVERAGE := 73
INTEGRATION_TEST_COVERAGE := 52
COMBINED_TEST_COVERAGE := 94
COMBINED_TEST_COVERAGE := 98

# System paths
PLATFORM := $(shell python -c 'import sys; print(sys.platform)')
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ which will essentially:

1. create a working tree at _root_/`location`/`dir`
2. fetch from `repo` and checkout the specified `rev`
3. symbolically link each `location`/`dir` from _root_/`link` (optional)
3. symbolically link each `location`/`dir` 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
5. record the actual commit SHAs that were checked out (with `--lock` option)

where `rev` can be:

Expand Down Expand Up @@ -116,4 +116,4 @@ $ gdm uninstall
Advanced Options
================

See the full documentation at http://git-dependency-manager.info/.
See the full documentation at [git-dependency-manager.info](http://git-dependency-manager.info/interfaces/cli/).
3 changes: 2 additions & 1 deletion docs/interfaces/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ All of the [command-line interface](cli.md) functionality is available from the
To clone/checkout the specified dependencies, call:

```python
gdm.install(*names, root=None, depth=None, force=False, clean=True)
gdm.install(*names, root=None, depth=None, force=False, fetch=False, clean=True)
```

with optional arguments:
Expand All @@ -16,6 +16,7 @@ with optional arguments:
- `root`: specifies the path to the root working tree
- `depth`: number of levels of dependencies to traverse
- `force`: indicates uncommitted changes can be overwritten
- `fetch`: indicates the latest branches should always be fetched
- `clean`: indicates untracked files should be deleted from dependencies

## Update
Expand Down
10 changes: 8 additions & 2 deletions docs/interfaces/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,19 @@ or limit the traversal of nested dependencies:
gdm install --depth=<count>
```

Delete all untracked files in dependencies by instead running:
It will leave untracked files alone. To delete them, run:

```sh
gdm install --clean
```

The program will exit with an error if there are any uncommitted changes in dependencies. To overwrite all changes, run:
It will only fetch from the repository if needed. To always fetch, run:

```sh
gdm install --fetch
```

It will exit with an error if there are any uncommitted changes in dependencies. To overwrite all changes, run:

```sh
gdm install --force
Expand Down
5 changes: 3 additions & 2 deletions gdm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
import sys

__project__ = 'GDM'
__version__ = '0.7'
__version__ = '0.8'

CLI = 'gdm'
PLUGIN = 'deps'
NAME = "Git Dependency Manager"
VERSION = __project__ + ' v' + __version__
DESCRIPTION = "A language-agnostic \"dependency manager\" using Git."

PYTHON_VERSION = 3, 3
PYTHON_VERSION = 3, 4

if sys.version_info < PYTHON_VERSION: # pragma: no cover (manual test)
exit("Python {}.{}+ is required.".format(*PYTHON_VERSION))
Expand Down
6 changes: 5 additions & 1 deletion gdm/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ def main(args=None, function=None):
**shared)
sub.add_argument('name', nargs='*',
help="list of dependencies (`dir` values) to install")
sub.add_argument('-e', '--fetch', action='store_true',
help="always fetch the latest branches")

# Update parser
info = "update dependencies to the latest versions"
Expand Down Expand Up @@ -116,6 +118,8 @@ def _get_command(function, namespace):
kwargs.update(depth=namespace.depth,
force=namespace.force,
clean=namespace.clean)
if namespace.command == 'install':
kwargs.update(fetch=namespace.fetch)
if namespace.command == 'update':
kwargs.update(recurse=namespace.recurse,
lock=namespace.lock)
Expand All @@ -138,7 +142,7 @@ def _get_command(function, namespace):
def _run_command(function, args, kwargs, exit_msg):
success = False
try:
log.debug("Running %r command...", getattr(function, '__name__', 'a'))
log.debug("Running %s command...", getattr(function, '__name__', 'a'))
success = function(*args, **kwargs)
except KeyboardInterrupt:
log.debug("Command canceled")
Expand Down
10 changes: 6 additions & 4 deletions gdm/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ def wrapped(*args, **kwargs):


@restore_cwd
def install(*names, root=None, depth=None, force=False, clean=True):
def install(*names, root=None, depth=None,
force=False, fetch=False, clean=True):
"""Install dependencies for a project.
Optional arguments:
Expand All @@ -30,6 +31,7 @@ def install(*names, root=None, depth=None, force=False, clean=True):
- `root`: specifies the path to the root working tree
- `depth`: number of levels of dependencies to traverse
- `force`: indicates uncommitted changes can be overwritten
- `fetch`: indicates the latest branches should always be fetched
- `clean`: indicates untracked files should be deleted from dependencies
"""
Expand All @@ -44,8 +46,8 @@ def install(*names, root=None, depth=None, force=False, clean=True):
if config:
common.show("Installing dependencies...", log=False)
common.show()
count = config.install_deps(
*names, update=False, depth=depth, force=force, clean=clean)
count = config.install_deps(*names, update=False, depth=depth,
force=force, fetch=fetch, clean=clean)

return _display_result("install", "Installed", count)

Expand Down Expand Up @@ -80,7 +82,7 @@ def update(*names, root=None, depth=None,
common.show()
count = config.install_deps(
*names, update=True, depth=depth,
recurse=recurse, force=force, clean=clean)
recurse=recurse, force=force, fetch=True, clean=clean)
common.dedent(level=0)
if count and lock is not False:
common.show("Recording installed versions...", log=False)
Expand Down
135 changes: 15 additions & 120 deletions gdm/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,119 +6,12 @@
import yorm

from . import common
from .shell import ShellMixin, GitMixin
from . import shell
from .source import Source

log = logging.getLogger(__name__)


@yorm.attr(repo=yorm.converters.String)
@yorm.attr(dir=yorm.converters.String)
@yorm.attr(rev=yorm.converters.String)
@yorm.attr(link=yorm.converters.String)
class Source(yorm.converters.AttributeDictionary, ShellMixin, GitMixin):
"""A dictionary of `git` and `ln` arguments."""

def __init__(self, repo, dir, rev='master', link=None): # pylint: disable=W0622
super().__init__()
self.repo = repo
self.dir = dir
self.rev = rev
self.link = link
if not self.repo:
raise ValueError("'repo' missing on {}".format(repr(self)))
if not self.dir:
raise ValueError("'dir' missing on {}".format(repr(self)))

def __repr__(self):
return "<source {}>".format(self)

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)

def __eq__(self, other):
return self.dir == other.dir

def __ne__(self, other):
return self.dir != other.dir

def __lt__(self, other):
return self.dir < other.dir

def update_files(self, force=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):
log.debug("Creating a new repository...")
self.git_clone(self.repo, self.dir)
self.cd(self.dir)

# Check for uncommitted changes
if not force:
log.debug("Confirming there are no uncommitted changes...")
if self.git_changes():
common.show()
msg = "Uncommitted changes: {}".format(os.getcwd())
raise RuntimeError(msg)

# Fetch the desired revision
self.git_fetch(self.repo, self.rev)

# Update the working tree to the desired revision
self.git_update(self.rev, clean=clean)

def create_link(self, root, force=False):
"""Create a link from the target name to the current directory."""
if self.link:
log.info("Creating a symbolic link...")
target = os.path.join(root, self.link)
source = os.path.relpath(os.getcwd(), os.path.dirname(target))
if os.path.islink(target):
os.remove(target)
elif os.path.exists(target):
if force:
self.rm(target)
else:
common.show()
msg = "Preexisting link location: {}".format(target)
raise RuntimeError(msg)
self.ln(source, target)

def identify(self, allow_dirty=True):
"""Get the path and current repository URL and hash."""
if os.path.isdir(self.dir):

self.cd(self.dir)

path = os.getcwd()
url = self.git_get_url()
if self.git_changes(visible=True):
revision = '<dirty>'
if not allow_dirty:
common.show()
msg = "Uncommitted changes: {}".format(os.getcwd())
raise RuntimeError(msg)
else:
revision = self.git_get_sha()
common.show(revision, log=False)

return path, url, revision

else:

return os.getcwd(), '<missing>', '<unknown>'

def lock(self):
"""Return a locked version of the current source."""
_, _, sha = self.identify()
source = self.__class__(self.repo, self.dir, sha, self.link)
return source


@yorm.attr(all=Source)
class Sources(yorm.converters.SortedList):
"""A list of source dependencies."""
Expand All @@ -128,7 +21,7 @@ class Sources(yorm.converters.SortedList):
@yorm.attr(sources=Sources)
@yorm.attr(sources_locked=Sources)
@yorm.sync("{self.root}/{self.filename}")
class Config(ShellMixin):
class Config:
"""A dictionary of dependency configuration options."""

FILENAMES = ('gdm.yml', 'gdm.yaml', '.gdm.yml', '.gdm.yaml')
Expand All @@ -152,15 +45,16 @@ def location_path(self):
return os.path.join(self.root, self.location)

def install_deps(self, *names, depth=None,
update=True, recurse=False, force=False, clean=True):
update=True, recurse=False,
force=False, fetch=False, clean=True):
"""Get all sources."""
if depth == 0:
log.info("Skipped directory: %s", self.location_path)
return 0

if not os.path.isdir(self.location_path):
self.mkdir(self.location_path)
self.cd(self.location_path)
shell.mkdir(self.location_path)
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]
Expand All @@ -175,7 +69,7 @@ def install_deps(self, *names, depth=None,
log.info("Skipped dependency: %s", source.dir)
continue

source.update_files(force=force, clean=clean)
source.update_files(force=force, fetch=fetch, clean=clean)
source.create_link(self.root, force=force)
count += 1

Expand All @@ -189,11 +83,12 @@ def install_deps(self, *names, depth=None,
update=update and recurse,
recurse=recurse,
force=force,
fetch=fetch,
clean=clean,
)
common.dedent()

self.cd(self.location_path, visible=False)
shell.cd(self.location_path, _show=False)

common.dedent()
if dirs:
Expand All @@ -204,7 +99,7 @@ def install_deps(self, *names, depth=None,

def lock_deps(self, *names, obey_existing=True):
"""Lock down the immediate dependency versions."""
self.cd(self.location_path)
shell.cd(self.location_path)
common.show()
common.indent()

Expand All @@ -227,21 +122,21 @@ def lock_deps(self, *names, obey_existing=True):

common.show()

self.cd(self.location_path, visible=False)
shell.cd(self.location_path, _show=False)

if count:
yorm.update_file(self)
return count

def uninstall_deps(self):
"""Remove the sources location."""
self.rm(self.location_path)
shell.rm(self.location_path)
common.show()

def get_deps(self, depth=None, allow_dirty=True):
"""Yield the path, repository URL, and hash of each dependency."""
if os.path.exists(self.location_path):
self.cd(self.location_path)
shell.cd(self.location_path)
common.show()
common.indent()
else:
Expand All @@ -265,7 +160,7 @@ def get_deps(self, depth=None, allow_dirty=True):
)
common.dedent()

self.cd(self.location_path, visible=False)
shell.cd(self.location_path, _show=False)

common.dedent()

Expand Down

0 comments on commit d6fc1e5

Please sign in to comment.