Skip to content

Commit

Permalink
Merge pull request #88 from JrGoodle/clowder-repo-command
Browse files Browse the repository at this point in the history
Add `clowder repo` command
  • Loading branch information
JrGoodle committed Jan 2, 2016
2 parents f1a0313 + 381b760 commit 9b017fd
Show file tree
Hide file tree
Showing 9 changed files with 63 additions and 68 deletions.
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Managing multiple repositories can be pretty frustrating. There are a number of

All of these have their own approach, but many are based on submodules or subtrees. Submodules and subtrees create a tight coupling between repositories because of the way dependencies are stored. Much has been written about their drawbacks elsewhere. Google's `repo` tool takes a different approach, but is closely tied to Google's development workflow.

`clowder` uses a similar approach as `repo` (and as it turns out, `gr` and `giternal`) with a yaml file instead of xml. URL information and relative project locations on disk are specified in a `clowder.yaml` file. This file is checked into its own repository (which at this point assumes one branch due to the way saved versions are handled). The use of a separate file for tracking projects means that there's detailed information about the dependencies between them, but each repository is still essentially independent. Projects can be tied to specific tags or commits, or can track branches. With the `clowder fix <version>` command, specific versions of the `clowder.yaml` file can be saved from the current commit hashes of all projects for later restoration.
`clowder` uses a similar approach as `repo` (and as it turns out, `gr` and `giternal`) with a yaml file instead of xml. URL information and relative project locations on disk are specified in a `clowder.yaml` file. This file is checked into its own repository. The use of a separate file for tracking projects means that there's detailed information about the dependencies between them, but each repository is still essentially independent. Projects can be tied to specific tags or commits, or can track branches. With the `clowder fix <version>` command, specific versions of the `clowder.yaml` file can be saved from the current commit hashes of all projects for later restoration.

The primary purpose of `clowder` is synchronization of multiple repositories, so normal development still takes place in individual repositories with the usual `git` commands.

Expand All @@ -37,7 +37,7 @@ For a few example projects, see the [examples directory](https://github.com/JrGo
To install from the [GitHub Releases](https://github.com/JrGoodle/clowder/releases) open a terminal and run:

```bash
$ pip3 install https://github.com/JrGoodle/clowder/releases/download/0.8.2/clowder-0.8.2-py3-none-any.whl
$ pip3 install https://github.com/JrGoodle/clowder/releases/download/0.8.3/clowder-0.8.3-py3-none-any.whl
```

To install from the cloned repository:
Expand Down Expand Up @@ -91,9 +91,9 @@ $ clowder fix v0.1 # Save a fixed version of clowder.yaml

```bash
$ export CMD='git status'
$ clowder forall "$CMD" # Run "$CMD" in all project directories
$ clowder forall "$CMD" -g clang # Run "$CMD" only for projects in clang group
$ clowder forall "$CMD" -p llvm-mirror/clang # Run "$CMD" only for clang project
$ clowder forall "$CMD" # Run $CMD in all project directories
$ clowder forall "$CMD" -g clang # Run $CMD only for projects in clang group
$ clowder forall "$CMD" -p llvm-mirror/clang # Run $CMD only for clang project
```

```bash
Expand All @@ -115,6 +115,10 @@ $ clowder meow -g clang llvm # print status of projects in clang and llvm groups
$ clowder meow -v -g clang # print verbose status of projects in clang group
```

```bash
$ clowder repo 'git status' # Run 'git status' in .clowder directory
```

```bash
$ clowder stash # Stash any changes in projects
$ clowder stash -g clang # Stash any changes in projects in clang group
Expand Down
17 changes: 6 additions & 11 deletions clowder/clowder_controller.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
"""clowder.yaml parsing and functionality"""
import os, yaml, sys
import os, yaml, subprocess, sys
from termcolor import colored
from clowder.group import Group
from clowder.source import Source
from clowder.utility.clowder_utilities import (
forall_run,
validate_yaml
)
from clowder.utility.clowder_utilities import validate_yaml

class ClowderController(object):
"""Class encapsulating project information from clowder.yaml for controlling clowder"""
Expand Down Expand Up @@ -43,21 +40,19 @@ def fix_version(self, version):

def forall_groups(self, command, group_names):
"""Runs command in all project directories of groups specified"""
directories = []
for group in self.groups:
if group.name in group_names:
for project in group.projects:
directories.append(project.full_path())
forall_run(command, directories)
project.run_command(command)
sys.exit() # Exit early to prevent printing extra newline

def forall_projects(self, command, project_names):
"""Runs command in all project directories of projects specified"""
directories = []
for group in self.groups:
for project in group.projects:
if project.name in project_names:
directories.append(project.full_path())
forall_run(command, directories)
project.run_command(command)
sys.exit() # Exit early to prevent printing extra newline

def get_all_group_names(self):
"""Returns all group names for current clowder.yaml"""
Expand Down
8 changes: 7 additions & 1 deletion clowder/clowder_repo.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Clowder repo management"""
import emoji, os, sys
import emoji, os, subprocess, sys
# from git import Repo
from termcolor import colored
from clowder.utility.git_utilities import (
Expand Down Expand Up @@ -29,6 +29,12 @@ def breed(self, url):
git_clone_url_at_path(url, self.clowder_path, 'refs/heads/master', 'origin')
self.symlink_yaml()

def run_command(self, command):
"""Run command in clowder repo"""
command_output = colored('$ ' + command, attrs=['bold'])
print(command_output)
subprocess.call(command.split(), cwd=self.clowder_path)

def print_status(self):
"""Print clowder repo status"""
repo_path = os.path.join(self.root_directory, '.clowder')
Expand Down
27 changes: 21 additions & 6 deletions clowder/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ def breed(self):
def fix(self):
"""clowder fix command"""
if self.clowder_repo is not None:
cprint('Fix...\n', 'yellow')
# cprint('Fix...\n', 'yellow')
self.clowder.fix_version(self.args.version)
else:
exit_clowder_not_found()

def forall(self):
"""clowder forall command"""
if self.clowder_repo is not None:
cprint('Forall...\n', 'yellow')
# cprint('Forall...\n', 'yellow')
self.clowder_repo.print_status()
print('')
if self.args.projects is None:
Expand All @@ -84,7 +84,7 @@ def forall(self):
def groom(self):
"""clowder groom command"""
if self.clowder_repo is not None:
cprint('Groom...\n', 'yellow')
# cprint('Groom...\n', 'yellow')
self.clowder_repo.print_status()
print('')
if self.args.projects is None:
Expand All @@ -97,7 +97,7 @@ def groom(self):
def herd(self):
"""clowder herd command"""
if self.clowder_repo is not None:
cprint('Herd...\n', 'yellow')
# cprint('Herd...\n', 'yellow')
self.clowder_repo.print_status()
self.clowder_repo.symlink_yaml(self.args.version)
print('')
Expand All @@ -115,7 +115,7 @@ def herd(self):
def meow(self):
"""clowder meow command"""
if self.clowder_repo is not None:
cprint('Meow...\n', 'yellow')
# cprint('Meow...\n', 'yellow')
self.clowder_repo.print_status()
print('')
if self.args.verbose:
Expand All @@ -125,10 +125,20 @@ def meow(self):
else:
exit_clowder_not_found()

def repo(self):
"""clowder repo command"""
if self.clowder_repo is not None:
# cprint('Repo...\n', 'yellow')
self.clowder_repo.print_status()
print('')
self.clowder_repo.run_command(self.args.cmd)
else:
exit_clowder_not_found()

def stash(self):
"""clowder stash command"""
if self.clowder_repo is not None:
cprint('Stash...\n', 'yellow')
# cprint('Stash...\n', 'yellow')
self.clowder_repo.print_status()
print('')
if self.args.projects is None:
Expand Down Expand Up @@ -173,6 +183,11 @@ def _configure_subparsers(self, subparsers):
parser_meow.add_argument('--groups', '-g', choices=self.group_names,
default=self.group_names, nargs='+',
help='Groups to print status for')
# clowder repo
repo_help = 'Run command in project directories'
parser_repo = subparsers.add_parser('repo', help=repo_help)
parser_repo.add_argument('cmd', help='Command to run in project directories')

# clowder fix
fix_help = 'Create version of clowder.yaml for current repos'
parser_fix = subparsers.add_parser('fix', help=fix_help)
Expand Down
10 changes: 9 additions & 1 deletion clowder/project.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Representation of clowder.yaml project"""
import os
import os, subprocess
from termcolor import colored, cprint
from clowder.utility.clowder_utilities import (
format_project_string,
Expand Down Expand Up @@ -142,3 +142,11 @@ def _print_status(self):
current_ref_output = format_ref_string(repo_path)
print(project_output + ' ' + current_ref_output)
cprint(self.path, 'cyan')

def run_command(self, command):
"""Run command in project directory"""
self._print_status()
command_output = colored('$ ' + command, attrs=['bold'])
print(command_output)
subprocess.call(command.split(), cwd=self.full_path())
print('')
19 changes: 1 addition & 18 deletions clowder/utility/clowder_utilities.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Clowder utilities"""
import errno, os, subprocess, sys
import errno, os, sys
from termcolor import colored
from clowder.utility.git_utilities import (
git_current_branch,
Expand All @@ -17,23 +17,6 @@ def force_symlink(file1, file2):
os.remove(file2)
os.symlink(file1, file2)

def forall_run(command, directories):
"""Run command in all directories"""
sorted_paths = sorted(set(directories))
paths = [p for p in sorted_paths if os.path.isdir(p)]
for path in paths:
running_output = colored('Running command', attrs=['underline'])
command_output = colored(command, attrs=['bold'])
print(running_output + ': ' + command_output)
directory_output = colored('Directory', attrs=['underline'])
path_output = colored(path, 'cyan')
print(directory_output + ': ' + path_output)
subprocess.call(command.split(),
cwd=path)
print('')
# Exit early to prevent printing extra newline
sys.exit()

def format_project_string(repo_path, name):
"""Return formatted project name"""
if git_is_dirty(repo_path):
Expand Down
28 changes: 7 additions & 21 deletions scripts/test_cats_example.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,37 +48,27 @@ test_herd_sha()
{
print_separator
echo "TEST: Test herd of static commit hash refs"
pushd .clowder &>/dev/null
git checkout static-refs
popd &>/dev/null
clowder repo 'git checkout static-refs'
clowder herd || exit 1
clowder meow || exit 1
pushd .clowder &>/dev/null
git checkout master
popd &>/dev/null
clowder repo 'git checkout master'
}

test_herd_tag()
{
print_separator
echo "TEST: Test herd of tag refs"
pushd .clowder &>/dev/null
git checkout tags
popd &>/dev/null
clowder repo 'git checkout tags'
clowder herd || exit 1
clowder meow || exit 1
pushd .clowder &>/dev/null
git checkout master
popd &>/dev/null
clowder repo 'git checkout master'
}

test_invalid_yaml()
{
print_separator
echo "TEST: Fail herd with invalid yaml"
pushd .clowder &>/dev/null
git checkout invalid-yaml
popd &>/dev/null
clowder repo 'git checkout invalid-yaml'
clowder herd && exit 1
pushd .clowder &>/dev/null
git checkout master
Expand All @@ -89,15 +79,11 @@ test_no_versions()
{
print_separator
echo "TEST: Test clowder repo with no versions fixed"
pushd .clowder &>/dev/null
git checkout no-versions
popd &>/dev/null
clowder repo 'git checkout no-versions'
clowder herd -v fixed-version && exit 1
clowder herd || exit 1
clowder meow || exit 1
pushd .clowder &>/dev/null
git checkout master
popd &>/dev/null
clowder repo 'git checkout master'
}

# export projects=( 'black-cats/kit' \
Expand Down
6 changes: 2 additions & 4 deletions scripts/test_utilities.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
make_dirty_clowder_repo()
{
echo "TEST: Make dirty clowder repo"
pushd .clowder &>/dev/null
touch newfile
git add newfile
popd &>/dev/null
clowder repo 'touch newfile'
clowder repo 'git add newfile'
clowder meow || exit 1
}

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
setup(
name='clowder',
description='A tool for managing code',
version='0.8.2',
version='0.8.3',
url='http://clowder.cat',
author='joe DeCapo',
author_email='joe@polka.cat',
Expand Down

0 comments on commit 9b017fd

Please sign in to comment.