Skip to content
This repository has been archived by the owner on Jun 25, 2020. It is now read-only.

Commit

Permalink
Add export command (#120)
Browse files Browse the repository at this point in the history
* Adding back snapshot command. Revert "Revert "Adding snapshot command to generate rosinstall with SHAs for current …" (#117)"

This reverts commit 5da3d4c.

* Fixes #116 by resolving a circular import dependency introduced in config_yaml.py when adding the export command.

* Renames function cmd_export to cmd_snapshot to maintain compatibility with rosws.

* Adding a sanity check on rosws to the Travis CI configuration.
  • Loading branch information
rsinnet authored and wjwwood committed Jul 18, 2018
1 parent 840c1f5 commit 3a6a2d3
Show file tree
Hide file tree
Showing 10 changed files with 408 additions and 94 deletions.
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ script:
- python -c 'import sys; print(sys.path)'
- python -c 'import coverage'
- python -m nose --with-coverage --cover-package=wstool test/local

# Install rosinstall from pip and perform a sanity check
- pip install --upgrade rosinstall
- rosws
notifications:
email: false
after_success:
Expand Down
8 changes: 4 additions & 4 deletions completion/wstool-completion.bash
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ fi

# put here to be extendable
if [ -z "$WSTOOL_BASE_COMMANDS" ]; then
_WSTOOL_BASE_COMMANDS="help init set merge info remove diff status update --version"
_WSTOOL_BASE_COMMANDS="help init set merge info remove diff status update export --version"
fi

# Based originally on the bzr/svn bash completition scripts.
Expand Down Expand Up @@ -110,11 +110,11 @@ _wstool_complete()
update|up)
cmdOpts="-t --target-workspace --delete-changed-uris --abort-changed-uris --backup-changed-uris"
;;
snapshot)
cmdOpts="-t --target-workspace"
export)
cmdOpts="-t --target-workspace -o --output --exact --spec"
;;
info)
cmdOpts="-t --target-workspace --data-only --no-pkg-path --pkg-path-only --only --yaml"
cmdOpts="-t --target-workspace --data-only --no-pkg-path --pkg-path-only --only --yaml -u --untracked --fetch -s --short --root -m --managed-only"
;;
*)
;;
Expand Down
14 changes: 12 additions & 2 deletions src/wstool/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,14 +241,24 @@ def get_base_path(self):
def get_config_filename(self):
return self.config_filename

def get_source(self):
def get_source(self, versioned=False, vcs_only=False):
"""
:param versioned: True returns sources as versioned PathSpec, False
returns sources as simple PathSpec
:param vcs_only: True returns only version-controlled sources, False
returns all sources
:returns: all elements that got added by user keystrokes
(CLI and changed .rosinstall)
"""
source_aggregate = []
for tree_el in self.trees:
source_aggregate.append(tree_el.get_path_spec())
if vcs_only and not tree_el.is_vcs_element():
continue

if not versioned or not tree_el.is_vcs_element():
source_aggregate.append(tree_el.get_path_spec())
else:
source_aggregate.append(tree_el.get_versioned_path_spec())
return source_aggregate

def get_config_elements(self):
Expand Down
100 changes: 76 additions & 24 deletions src/wstool/config_yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
__REPOTYPES__ = ['svn', 'bzr', 'hg', 'git', 'tar']
__ALLTYPES__ = __REPOTYPES__ + ['other', 'setup-file']

## The Path spec is a leightweigt object to transport the
## The Path spec is a lightweight object to transport the
## specification of a config element between functions,
## independently of yaml structure.
## Specifications are persisted in yaml, this file deals
Expand Down Expand Up @@ -249,19 +249,42 @@ def get_legacy_type(self):
return 'setup-file'
return 'other'

def get_legacy_yaml(self):
def get_legacy_yaml(self, spec=True, exact=False):
"""
:param spec: If True, the version information will come from the
workspace .rosinstall. If False, the version information will come
from the current work trees.
:param exact: If True, the versions will be set to the exact commit
UUIDs. If False, the version name will be used, which might be a
branch name aut cetera.
return something like
{hg: {local-name: common,
version: common-1.0.2,
uri: https://kforge.org/common/}}
"""
# TODO switch to new syntax
properties = {'local-name': self._local_name}
if self._uri is not None:
properties['uri'] = self._uri
if self._version is not None:
properties['version'] = self._version
if spec:
if self._uri is not None:
properties['uri'] = self._uri
if exact:
if self._revision is not None:
properties['version'] = self._revision
else:
if self._version is not None:
properties['version'] = self._version
else:
if self._curr_uri is not None:
properties['uri'] = self._curr_uri
if exact:
if self._currevision is not None:
properties['version'] = self._currevision

else:
if self._curr_version is not None:
properties['version'] = self._curr_version

if self._tags is not None:
for tag in self._tags:
if tag != 'setup-file' and tag != []:
Expand Down Expand Up @@ -378,29 +401,58 @@ def get_path_spec_from_yaml(yaml_dict):


def generate_config_yaml(config, filename, header, pretty=False,
sort_with_localname=False):
sort_with_localname=False, spec=True,
exact=False, vcs_only=False):
"""
Writes file filename with header first and then the config as yaml
Writes file filename with header first and then the config as YAML.
:param config: The configuration containing all the entries to be included
in the generated YAML.
:param filename: If filename is not an absolute path, it will be assumed to
be relative to config.get_base_path(). If filename is None, the output will
be sent to stdout instead of a file.
:param header: A header to be included with the generated config YAML.
:param pretty: If True, the generated config YAML will be printed in
long-form YAML. If false, the default flow style will be used instead.
:param sort_with_localname: If true, config entries will be sorted by their
localname fields. If false, the order will be as passed in through config.
:param spec: If True, the version information will come from the workspace
.rosinstall. If False, the version information will come from the current
work trees.
:param exact: If True, the versions will be set to the exact commit UUIDs.
If False, the version name will be used, which might be a branch name
aut cetera.
:param vcs_only: If True, the generated config YAML will include only
version-controlled entries. If False, all entries in current workspace will
be included.
"""
if not os.path.exists(config.get_base_path()):
os.makedirs(config.get_base_path())
config_filepath = os.path.realpath(os.path.join(config.get_base_path(), filename))
with open(config_filepath, 'w+b') as f:
if header is not None:
f.write(header.encode('UTF-8'))
if sort_with_localname:
items = [x.get_legacy_yaml() for x in
sorted(config.get_source(),
key=lambda x:x.get_local_name())]
else:
items = [x.get_legacy_yaml() for x in config.get_source()]

if not items:
return
content = ""
if header:
content += header

# Do a pass-through if just pulling versioning information straight from
# the .rosinstall
passthrough = spec and not exact
items = config.get_source(not passthrough, vcs_only)
if sort_with_localname:
items = sorted(items, key=lambda x: x.get_local_name())
items = [x.get_legacy_yaml(spec, exact) for x in items]

if items:
if pretty:
content = yaml.safe_dump(items, allow_unicode=True,
default_flow_style=False)
content += yaml.safe_dump(items, allow_unicode=True,
default_flow_style=False)
else:
content = yaml.safe_dump(items)
f.write(content.encode('UTF-8'))
content += yaml.safe_dump(items)

if filename:
config_filepath = filename if os.path.isabs(filename) else \
os.path.realpath(os.path.join(config.get_base_path(), filename))

with open(config_filepath, 'w+b') as f:
f.write(content.encode('UTF-8'))
else:
print(content)
63 changes: 60 additions & 3 deletions src/wstool/multiproject_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"set": "add or changes one entry from your workspace config",
"update": "update or check out some of your config elements",
"remove": "remove an entry from your workspace config, without deleting files",
"snapshot": "write a file specifying repositories to have the version they currently have",
"export": "export a snapshot of the workspace",
"diff": "print a diff over some SCM controlled entries",
"foreach": "run shell command in given entries",
"status": "print the change status of files in some SCM controlled entries",
Expand All @@ -70,7 +70,7 @@
__MULTIPRO_CMD_HELP_LIST__ = ['help', 'init',
None, 'set', 'merge', 'remove', 'scrape',
None, 'update',
None, 'info', 'status', 'diff', 'foreach']
None, 'info', 'export', 'status', 'diff', 'foreach']

# command aliases
__MULTIPRO_CMD_ALIASES__ = {'update': 'up',
Expand Down Expand Up @@ -468,7 +468,8 @@ def cmd_init(self, argv):

# includes ROS specific files

print("Writing %s" % os.path.join(config.get_base_path(), self.config_filename))
if self.config_filename:
print("Writing %s" % os.path.join(config.get_base_path(), self.config_filename))
self.config_generator(config, self.config_filename, get_header(self.progname))

## install or update each element
Expand Down Expand Up @@ -1114,6 +1115,60 @@ def cmd_remove(self, target_path, argv, config=None):

return 0

def cmd_snapshot(self, target_path, argv, config=None):
parser = OptionParser(
usage="usage: %s info [localname]* [OPTIONS]" % self.progname,
formatter=IndentedHelpFormatterWithNL(),
description=__MULTIPRO_CMD_DICT__["export"] + """
Exports the current workspace.
The --exact option will cause the output to contain the exact commit uuid for
each version-controlled entry. The --spec option tells wstool to look at the
workspace .rosinstall for versioning info instead of the workspace.
Examples:
$ %(prog)s export
$ %(prog)s export -t ~/ros/fuerte
$ %(prog)s export --exact
""" % {'prog': self.progname, 'opts': ONLY_OPTION_VALID_ATTRS},
epilog="See: http://www.ros.org/wiki/rosinstall for details\n")
parser.add_option(
"-o", "--output", dest="output_filename", default=None,
help="Write the .rosinstall export to the specified file",
action="store")
parser.add_option(
"-t", "--target-workspace", dest="workspace", default=None,
help="which workspace to use",
action="store")
parser.add_option(
"--exact", dest="exact", default=False, action="store_true",
help="export exact commit hashes instead of branch names")
parser.add_option(
"--spec", dest="spec", default=False, action="store_true",
help="export version from workspace spec instead of current")

(options, _) = parser.parse_args(argv)

if config is None:
config = multiproject_cmd.get_config(
target_path,
additional_uris=[],
config_filename=self.config_filename)
elif config.get_base_path() != target_path:
raise MultiProjectException("Config path does not match %s %s " %
(config.get_base_path(), target_path))

# TODO: Check for workspace differences and issue warnings?

fname = options.output_filename
if fname:
fname = os.path.abspath(fname)
print("Writing %s" % fname)
self.config_generator(config, fname, get_header(self.progname),
spec=options.spec, exact=options.exact,
vcs_only=True)

return 0

def cmd_info(self, target_path, argv, reverse=True, config=None):
parser = OptionParser(
Expand Down Expand Up @@ -1215,6 +1270,8 @@ def cmd_info(self, target_path, argv, reverse=True, config=None):
print('\n'.join(lines))
return 0
elif options.yaml:
# TODO: Not sure what this does, used to be cmd_snapshot,
# but that command was not implemented.
source_aggregate = multiproject_cmd.cmd_snapshot(config,
localnames=args)
print(yaml.safe_dump(source_aggregate), end='')
Expand Down
6 changes: 4 additions & 2 deletions src/wstool/multiproject_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,12 @@ def add_uris(config,


def cmd_persist_config(config, filename, header=None,
pretty=False, sort_with_localname=False):
pretty=False, sort_with_localname=False,
spec=True, exact=False, vcs_only=False):
"""writes config to given file in yaml syntax"""
generate_config_yaml(config, filename, header,
pretty, sort_with_localname)
pretty, sort_with_localname,
spec, exact, vcs_only)


def cmd_version():
Expand Down
9 changes: 7 additions & 2 deletions src/wstool/wstool_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,17 @@ class WstoolCLI(MultiprojectCLI):

def __init__(self, config_filename=ROSINSTALL_FILENAME, progname=_PROGNAME):

def config_generator(config, filename, header=None):
def config_generator(config, filename, header=None, spec=True,
exact=False, vcs_only=False):
wstool.multiproject_cmd.cmd_persist_config(
config,
filename,
header,
pretty=True,
sort_with_localname=True)
sort_with_localname=True,
spec=spec,
exact=exact,
vcs_only=vcs_only)

MultiprojectCLI.__init__(
self,
Expand Down Expand Up @@ -149,6 +153,7 @@ def wstool_main(argv=None, usage=None):
'remove': cli.cmd_remove,
'set': cli.cmd_set,
'merge': cli.cmd_merge,
'export': cli.cmd_snapshot,
'diff': cli.cmd_diff,
'foreach': cli.cmd_foreach,
'scrape': cli.cmd_scrape,
Expand Down

0 comments on commit 3a6a2d3

Please sign in to comment.