Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion renga/cli/_ascii.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@
from networkx.algorithms.dag import topological_sort


def _format_sha1(graph, key):
"""Return formatted text with the submodule information."""
submodule = ':'.join(graph.G.nodes[key].get('submodule', []))
if submodule:
return click.style(submodule, fg='green') + '@' + click.style(
key[0][:8], fg='yellow')
return click.style(key[0][:8], fg='yellow')


@attr.s
class DAG(object):
"""Generate ASCII representation of a DAG."""
Expand Down Expand Up @@ -72,7 +81,7 @@ def __iter__(self):

def node_text(self, node):
"""Return text for a given node."""
return [click.style(node[0][:7], fg='yellow') + ' ' + node[1]]
return [_format_sha1(self.graph, node) + ' ' + node[1]]

def iter_edges(self, node):
"""Yield edges for a node and update internal status."""
Expand Down
62 changes: 49 additions & 13 deletions renga/cli/_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@
import attr
import networkx as nx
import yaml
from git import IndexFile
from git import IndexFile, Submodule

from renga._compat import Path
from renga.models.cwl.command_line_tool import CommandLineTool
from renga.models.cwl.workflow import Workflow

from ._repo import Repo


@attr.s
class Graph(object):
Expand Down Expand Up @@ -64,7 +66,7 @@ def find_cwl(self, commit):
]

if len(files) == 1:
return os.path.relpath(Path(files[0]).resolve(), self.repo_path)
return files[0]

def find_latest_cwl(self):
"""Return the latest CWL in the repository."""
Expand All @@ -88,9 +90,8 @@ def iter_file_inputs(self, tool, basedir):
for input_ in tool.inputs:
if input_.type == 'File' and input_.default:
yield os.path.relpath(
(basedir / input_.default.path).resolve(),
self.repo_path
), input_.id
(self.repo_path / basedir / input_.default.path).resolve(),
self.repo_path), input_.id

def add_tool(self, commit, path):
"""Add a tool and its dependencies to the graph."""
Expand Down Expand Up @@ -124,7 +125,44 @@ def add_file(self, path, revision='HEAD'):

if file_commits:
#: Does not have a parent CWL.
return self.add_node(file_commits[0], path)
root_node = self.add_node(file_commits[0], path)
parent_commit, original_path = root_node

#: Capture information about the submodule in a submodule.
root_submodule = self.G.nodes[root_node].get('submodule', [])

#: Resolve Renga based submodules.
original_path = Path(original_path)
if original_path.is_symlink() or str(original_path).startswith(
'.renga/vendors'):
original_path = original_path.resolve()

for submodule in Submodule.iter_items(
self.repo.git, parent_commit=parent_commit):
try:
subpath = original_path.relative_to(
Path(submodule.path).resolve())
subgraph = Graph(repo=Repo(git_home=submodule.path))
subnode = subgraph.add_file(
str(subpath), revision=submodule.hexsha)

#: Extend node metadata.
for _, data in subgraph.G.nodes(data=True):
data['submodule'] = root_submodule + [
submodule.name
]

#: Merge file node with it's symlinked version.
self.G = nx.contracted_nodes(
nx.compose(self.G, subgraph.G),
root_node,
subnode,
) # TODO optionally it can be changed to an edge.
break
except ValueError:
continue

return root_node

@property
def _output_keys(self):
Expand Down Expand Up @@ -162,7 +200,7 @@ def build_status(self, revision='HEAD'):

for filepath, _ in index.entries.keys():
if not filepath.startswith('.renga') and \
filepath not in {'.gitignore', }:
filepath not in {'.gitignore', '.gitattributes'}:
self.add_file(filepath, revision=revision)
current_files.add(filepath)

Expand All @@ -172,8 +210,7 @@ def build_status(self, revision='HEAD'):
graph_files = sorted(
((commit, filepath) for (commit, filepath) in self.G
if filepath in current_files),
key=itemgetter(1)
)
key=itemgetter(1))

status = {'up-to-date': {}, 'outdated': {}, 'multiple-versions': {}}

Expand All @@ -183,10 +220,9 @@ def build_status(self, revision='HEAD'):
if len(keys) > 1:
status['multiple-versions'][filepath] = keys

if any(len(self.G.nodes[key]['_need_update']) > 1
for key in keys):
updates = list(self.G.nodes[key]
['_need_update'] for key in keys)
if any(len(self.G.nodes[key]['_need_update']) > 1 for key in keys):
updates = list(
self.G.nodes[key]['_need_update'] for key in keys)
status['outdated'][filepath] = updates
elif len(keys) == 1:
status['up-to-date'][filepath] = keys[0][0]
Expand Down
19 changes: 9 additions & 10 deletions renga/cli/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import click

from ._ascii import _format_sha1
from ._git import with_git
from ._graph import Graph
from ._repo import pass_repo
Expand Down Expand Up @@ -46,13 +47,12 @@ def status(ctx, repo, revision, path):

for filepath, files in status['outdated'].items():
paths = (', '.join(
'{0}@{1}'.format(
click.style(p, fg='yellow', bold=True),
click.style(c[:8], fg='blue'),
)
for c, p in stts
if not p.startswith('.renga') and p not in status['outdated']
) for stts in files)
'{0}#{1}'.format(
click.style(p, fg='blue', bold=True),
_format_sha1(graph, (c, p)),
) for c, p in stts
if not p.startswith('.renga') and p not in status['outdated'])
for stts in files)

click.echo('\t{0}: {1}'.format(
click.style(filepath, fg='red', bold=True), ', '.join(paths)))
Expand All @@ -71,10 +71,9 @@ def status(ctx, repo, revision, path):
click.echo()

for filepath, files in status['multiple-versions'].items():
commits = (click.style(commit[:8], fg='blue')
for commit, _ in files)
commits = (_format_sha1(graph, key) for key in files)
click.echo('\t{0}: {1}'.format(
click.style(filepath, fg='yellow', bold=True),
click.style(filepath, fg='blue', bold=True),
', '.join(commits)))

click.echo()
Expand Down