Repository: galaxyproject/planemo
Issue: Traceback for planemo shed_update --skip_upload

The command in the title works, but ends in a traceback:

```
Repository metadata updated.
Traceback (most recent call last):
  File "/opt/galaxyproject_planemo/.venv/bin/planemo", line 9, in <module>
    load_entry_point('planemo==0.24.0.dev0', 'console_scripts', 'planemo')()
  File "/opt/galaxyproject_planemo/.venv/local/lib/python2.7/site-packages/click/core.py", line 716, in __call__
    return self.main(*args, **kwargs)
  File "/opt/galaxyproject_planemo/.venv/local/lib/python2.7/site-packages/click/core.py", line 696, in main
    rv = self.invoke(ctx)
  File "/opt/galaxyproject_planemo/.venv/local/lib/python2.7/site-packages/click/core.py", line 1060, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/opt/galaxyproject_planemo/.venv/local/lib/python2.7/site-packages/click/core.py", line 889, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/opt/galaxyproject_planemo/.venv/local/lib/python2.7/site-packages/click/core.py", line 534, in invoke
    return callback(*args, **kwargs)
  File "/opt/galaxyproject_planemo/.venv/local/lib/python2.7/site-packages/click/decorators.py", line 64, in new_func
    return ctx.invoke(f, obj, *args[1:], **kwargs)
  File "/opt/galaxyproject_planemo/.venv/local/lib/python2.7/site-packages/click/core.py", line 534, in invoke
    return callback(*args, **kwargs)
  File "/opt/galaxyproject_planemo/.venv/local/lib/python2.7/site-packages/planemo/commands/cmd_shed_update.py", line 143, in cli
    exit_code = shed.for_each_repository(ctx, update, paths, **kwds)
  File "/opt/galaxyproject_planemo/.venv/local/lib/python2.7/site-packages/planemo/shed/__init__.py", line 687, in for_each_repository
    function(realized_repository)
  File "/opt/galaxyproject_planemo/.venv/local/lib/python2.7/site-packages/planemo/commands/cmd_shed_update.py", line 110, in update
    'time': captured_io["time"],
KeyError: 'time'
```


Changed files:
planemo/shed/__init__.py, planemo/commands/cmd_shed_update.py, tests/test_shed_upload.py

diff --git a/planemo/commands/cmd_shed_update.py b/planemo/commands/cmd_shed_update.py
index 565c091b..4d78314c 100644
--- a/planemo/commands/cmd_shed_update.py
+++ b/planemo/commands/cmd_shed_update.py
@@ -69,76 +69,76 @@ def cli(ctx, paths, **kwds):
 
     def update(realized_repository):
         collected_data['results']['total'] += 1
+        skip_upload = kwds["skip_upload"]
+        skip_metadata = kwds["skip_metadata"]
         upload_ret_code = 0
         upload_ok = True
 
         captured_io = {}
-        if not kwds["skip_upload"]:
+        if not skip_upload:
             with captured_io_for_xunit(kwds, captured_io):
                 upload_ret_code = shed.upload_repository(
                     ctx, realized_repository, **kwds
                 )
                 upload_ok = not upload_ret_code
 
+        repo_result = {
+            'classname': realized_repository.name,
+            'time': captured_io.get("time", None),
+            'name': 'shed-update',
+            'stdout': captured_io.get("stdout", None),
+            'stderr': captured_io.get("stderr", None),
+        }
+
         # Now that we've uploaded (or skipped appropriately), collect results.
         if upload_ret_code == 2:
             collected_data['results']['failures'] += 1
-            collected_data['tests'].append({
-                'classname': realized_repository.name,
+            repo_result.update({
                 'errorType': 'FailedUpdate',
                 'errorMessage': 'Failed to update repository as it does not exist in target ToolShed',
-                'time': captured_io["time"],
-                'name': 'shed-update',
-                'stdout': captured_io["stdout"],
-                'stderr': captured_io["stderr"],
             })
+            collected_data['tests'].append(repo_result)
             error("Failed to update repository it does not exist "
                   "in target ToolShed.")
             return upload_ret_code
-        repo_id = realized_repository.find_repository_id(ctx, shed_context)
+
+        exit = 0
         metadata_ok = True
-        if not kwds["skip_metadata"]:
-            metadata_ok = realized_repository.update(ctx, shed_context, repo_id)
-        if metadata_ok:
-            info("Repository metadata updated.")
+        if not skip_metadata:
+            repo_id = shed.handle_force_create(realized_repository, ctx, shed_context, **kwds)
+            # failing to create the repo, give up
+            if repo_id is None:
+                exit = shed.report_non_existent_repository(realized_repository)
+                metadata_ok = False
+            else:
+                metadata_ok = realized_repository.update(ctx, shed_context, repo_id)
         else:
+            info("Skipping repository metadata update.")
+
+        if not metadata_ok:
             error("Failed to update repository metadata.")
 
         if metadata_ok and upload_ok:
-            collected_data['tests'].append({
-                'classname': realized_repository.name,
-                'time': captured_io["time"],
-                'name': 'shed-update',
-                'stdout': captured_io["stdout"],
-                'stderr': captured_io["stderr"],
-            })
-            return 0
+            pass
         elif upload_ok:
             collected_data['results']['skips'] += 1
-            collected_data['tests'].append({
-                'classname': realized_repository.name,
+            repo_result.update({
                 'errorType': 'FailedMetadata',
                 'errorMessage': 'Failed to update repository metadata',
-                'time': captured_io["time"],
-                'name': 'shed-update',
-                'stdout': captured_io["stdout"],
-                'stderr': captured_io["stderr"],
             })
-            error("Repo updated but metadata was not.")
-            return 1
+            if not skip_upload:
+                error("Repo updated but metadata was not.")
+            exit = exit or 1
         else:
             collected_data['results']['failures'] += 1
-            collected_data['tests'].append({
-                'classname': realized_repository.name,
+            repo_result.update({
                 'errorType': 'FailedUpdate',
                 'errorMessage': 'Failed to update repository',
-                'time': captured_io["time"],
-                'name': 'shed-update',
-                'stdout': captured_io["stdout"],
-                'stderr': captured_io["stderr"],
             })
             error("Failed to update a repository.")
-            return 1
+            exit = exit or 1
+        collected_data['tests'].append(repo_result)
+        return exit
 
     exit_code = shed.for_each_repository(ctx, update, paths, **kwds)
 
diff --git a/planemo/shed/__init__.py b/planemo/shed/__init__.py
index 05624108..15a68fdb 100644
--- a/planemo/shed/__init__.py
+++ b/planemo/shed/__init__.py
@@ -226,6 +226,20 @@ def find_urls_for_xml(root):
     return urls
 
 
+def handle_force_create(realized_repository, ctx, shed_context, **kwds):
+    repo_id = realized_repository.find_repository_id(ctx, shed_context)
+    if repo_id is None and kwds.get("force_repository_creation", None):
+        repo_id = realized_repository.create(ctx, shed_context)
+    # failing to create the repo, give up
+    return repo_id
+
+
+def report_non_existent_repository(realized_repository):
+    name = realized_repository.name
+    error("Repository [%s] does not exist in the targeted Tool Shed." % name)
+    return 2
+
+
 def upload_repository(ctx, realized_repository, **kwds):
     """Upload a tool directory as a tarball to a tool shed.
     """
@@ -241,14 +255,10 @@ def upload_repository(ctx, realized_repository, **kwds):
     update_kwds = {}
     _update_commit_message(ctx, realized_repository, update_kwds, **kwds)
 
-    repo_id = realized_repository.find_repository_id(ctx, shed_context)
-    if repo_id is None and kwds.get("force_repository_creation", None):
-        repo_id = realized_repository.create(ctx, shed_context)
+    repo_id = handle_force_create(realized_repository, ctx, shed_context, **kwds)
     # failing to create the repo, give up
     if repo_id is None:
-        name = realized_repository.name
-        error("Repository [%s] does not exist in the targeted Tool Shed." % name)
-        return 2
+        return report_non_existent_repository(realized_repository)
 
     if kwds.get("check_diff", False):
         is_diff = diff_repo(ctx, realized_repository, **kwds)
diff --git a/tests/test_shed_upload.py b/tests/test_shed_upload.py
index f5fb0692..75cf8818 100644
--- a/tests/test_shed_upload.py
+++ b/tests/test_shed_upload.py
@@ -32,7 +32,13 @@ class ShedUploadTestCase(CliShedTestCase):
             upload_command.extend(self._shed_args())
             self._check_exit_code(upload_command, exit_code=2)
 
-    def test_upload_with_check_diff(self):
+    def test_update_not_exists_update_only(self):
+        with self._isolate_repo("single_tool"):
+            upload_command = ["shed_update", "--skip_upload"]
+            upload_command.extend(self._shed_args())
+            self._check_exit_code(upload_command, exit_code=2)
+
+    def test_update_with_check_diff(self):
         with self._isolate_repo("single_tool") as f:
             upload_command = [
                 "shed_update", "--force_repository_creation", "--check_diff"
@@ -53,7 +59,13 @@ class ShedUploadTestCase(CliShedTestCase):
             r = self._check_exit_code(upload_command)
             assert "not different, skipping upload." not in r.output
 
-    def test_upload_with_force_create(self):
+    def test_update_metadata_only(self):
+        with self._isolate_repo("single_tool"):
+            upload_command = ["shed_update", "--force_repository_creation", "--skip_upload"]
+            upload_command.extend(self._shed_args())
+            self._check_exit_code(upload_command)
+
+    def test_update_with_force_create(self):
         with self._isolate_repo("single_tool") as f:
             upload_command = ["shed_update", "--force_repository_creation"]
             upload_command.extend(self._shed_args())