"snapcraft validate" and "snapcraft gated" commands #813

Merged
merged 22 commits into from Sep 30, 2016

Conversation

Projects
None yet
5 participants
Contributor

ralsina commented Sep 19, 2016

Implements snapcraft validate and gated commands, according to discussions and specs.

runtests.sh
@@ -49,7 +49,7 @@ python3 -m coverage && coverage="true"
run_static_tests(){
SRC_PATHS="bin snapcraft integration_tests snaps_tests"
- python3 -m flake8 --max-complexity=10 $SRC_PATHS
+ python3 -m flake8 --max-complexity=12 $SRC_PATHS
@elopio

elopio Sep 20, 2016

Member

hey, that's cheating! :)

@ralsina

ralsina Sep 21, 2016

Contributor

Sorry, that was just temporary while I figure out how to make main have less than a 12 complexity with 12 commands, adding a XXX comment :-)

@ralsina

ralsina Sep 22, 2016

Contributor

Done by adding a specific flake8 comment to disable this check in the function that was failing.

Rationale is: I can make the complexity "go away" by doing things like a dictionary dispatch but IRL it's replacing a straightforward if/elif chain with something much less obvious.

snapcraft/_store.py
+ return details
+
+
+def gated(snap_name):
@elopio

elopio Sep 20, 2016

Member

I would call this: print_gated_snaps.

@sergiusens

sergiusens Sep 21, 2016

Collaborator

or show_gated

@ralsina

ralsina Sep 21, 2016

Contributor

I was going with the convention of functions being called the same as the command. If you guys like that better, no problem for me.

@sergiusens

sergiusens Sep 21, 2016

Collaborator

fair enough from me.

@josepht

josepht Sep 29, 2016

Contributor

I like having the functions match the command.

snapcraft/_store.py
+
+def _get_snap_data(name, store):
+ search_results = store.cpi.search_package(name, 'stable', None)
+ if search_results is None:
@sergiusens

sergiusens Sep 21, 2016

Collaborator

is there a scenario where another zero value would make if not search_results not a desirable way to write this?

@ralsina

ralsina Sep 21, 2016

Contributor

No such scenario. I sorta like this better since None is what the called function returns, but am ok with changing it :-)

@sergiusens

sergiusens Sep 21, 2016

Collaborator

let's change it please. Thanks

@ralsina

ralsina Sep 21, 2016

Contributor

Changed

snapcraft/_store.py
+ assertion['valid'] = "false"
+
+ cmdline = ['snap', 'sign']
+ if key is not None:
@sergiusens

sergiusens Sep 21, 2016

Collaborator

if key: reads better here, is there a possibility for it to be a zero value?

@ralsina

ralsina Sep 21, 2016

Contributor

There is a remote chance of someone doing something like --key='' I guess but that ends up being None. Changing it, since empty strings are invalid key names anyway.

snapcraft/storeapi/__init__.py
+ try:
+ response_json = response.json()
+ except (AttributeError, JSONDecodeError):
+ raise errors.StoreValidationError(snap_id, response)
@sergiusens

sergiusens Sep 21, 2016

Collaborator

what would this error look like?

@ralsina

ralsina Sep 21, 2016

Contributor

Honestly, not very good, like

Expecting value: line 1 column 1 (char 0)

I'd change it to a generic "Invalid response from server" maybe?

@sergiusens

sergiusens Sep 21, 2016

Collaborator

Yes please, the json decode error is just not friendly for a user.

What would help with debugging this later is to add a logger.debug line with response somewhere in the message. Like logger.debug('Invalid response from the server when doing blah: {} {}'.format(response.code, response.text))

@ralsina

ralsina Sep 21, 2016

Contributor

Done.

snapcraft/_store.py
+ store = storeapi.StoreClient()
+ # Get data for the gating snap
+ with _requires_login():
+ snap_data = _get_snap_data(snap_name, store)
@cprov

cprov Sep 23, 2016

Contributor

@ralsina let's drop this search-based query by the new snap-details endpoint caller:

store.cpi.get_package(self, snap_name, channel, arch):

https://github.com/snapcore/snapcraft/blob/master/snapcraft/storeapi/__init__.py#L291

@ralsina

ralsina Sep 23, 2016

Contributor

ack, replacing.

@ralsina

ralsina Sep 23, 2016

Contributor

Done.

integration_tests/__init__.py
@@ -186,3 +186,27 @@ def update_name_and_version(self, project_dir, name=None, version=None):
else:
print(line)
return updated_project_dir
+
+ def gated(self, snap_name, expected_validations, expected_error=None):
@cprov

cprov Sep 23, 2016

Contributor

Roberto, expected_validations has to be optional as well, otherwise you end up passing a and expected result that is completely irrelevant in the testing scenario.

@ralsina

ralsina Sep 23, 2016

Contributor

Done

integration_tests/test_validations.py
+ self.assertEqual(1, self.gated('ubuntu-core', [
+ ('snap-1', '3'),
+ ('snap-2', '5'),
+ ], expected_error='Have you run "snapcraft login'))
@cprov

cprov Sep 23, 2016

Contributor

Like this call, the expected_validations is not relevant here, only the expected_error

@ralsina

ralsina Sep 23, 2016

Contributor

Fixed

integration_tests/test_validations.py
@@ -0,0 +1,80 @@
+# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
@cprov

cprov Sep 23, 2016

Contributor

I think snapcraft guidelines enforce having individual integration test files for each command. Keeping it this way is simpler than arguing for a guidelines change (small boilerplate)

@ralsina

ralsina Sep 23, 2016

Contributor

I'll split it.

@ralsina

ralsina Sep 23, 2016

Contributor

Done

snapcraft/_store.py
+
+def _get_snap_data(name, store):
+ try:
+ details = store.cpi.get_package(name, 'stable', None)
@cprov

cprov Sep 23, 2016

Contributor

get_package() is not prepared for receiving arch=None, it's pushing a bogus header for CPI waiting for a disaster to happen ... arch is not important for snap-id lookups, so adjust get_package to suppress 'X-Ubuntu-Architecture' is arch is None (similar tweak on errors.SnapNotFound)
Also, I don't see much value in this wrapper if SnapNotFound message is sane, no need to turn it into RuntimeError, 2 different things.

@ralsina

ralsina Sep 23, 2016

Contributor

Done. I also fixed a similar wrapper in _store.download

snapcraft/_store.py
+ snap_data = _get_snap_data(snap_name, store)
+ release = str(snap_data['release'][0])
+ snap_id = snap_data['snap_id']
+ developer_id = snap_data['developer_id']
@cprov

cprov Sep 23, 2016

Contributor

"developer_id" will be the account-id of the uploader of the latest revision available in stable channel, because of package-collaborating (sharing) it might not be the "publisher", there is no "publisher_id" in CPI. More importantly, it's not the currently authenticated "account-id", so I think it will be less ambiguous (little more expensive) if you simply fetch account-info.account-id.

@ralsina

ralsina Sep 23, 2016

Contributor

Ack, will do.

snapcraft/storeapi/__init__.py
@@ -296,7 +305,8 @@ def get_package(self, snap_name, channel, arch):
}
params = {
'channel': channel,
- 'fields': 'status,download_url,download_sha512',
+ 'fields': 'status,download_url,download_sha512,snap_id,'
+ 'developer_id,release',
@cprov

cprov Sep 23, 2016

Contributor

as commented above, "developer_id" is not what you are looking for for validation assertions, it's always signed by the publisher.

@ralsina

ralsina Sep 27, 2016

Contributor

Done.

@cprov

cprov Sep 27, 2016

Contributor

So, you don't need 'developer_id' and 'release' in the current code.

@ralsina

ralsina Sep 27, 2016

Contributor

Fixed.

@ralsina ralsina changed the title from [WIP] "snapcraft validate" and "snapcraft gated" commands to "snapcraft validate" and "snapcraft gated" commands Sep 27, 2016

Thanks, looks generally good, but for a better review I need to sit down and read the design docs for validate and gated. Will get back to you again

snapcraft/_store.py
except storeapi.errors.SHAMismatchError:
raise RuntimeError(
'Failed to download {} at {} (mismatched SHA)'.format(
snap_name, download_path))
+
+
+def _get_snap_data(name, store):
@sergiusens

sergiusens Sep 27, 2016

Collaborator

Is there a reason for this method?

@ralsina

ralsina Sep 27, 2016

Contributor

Just that it was common code between gated and validate. It used to be more complicated, it is now too trivial not to inline, will change it.

snapcraft/storeapi/errors.py
@@ -37,10 +37,14 @@ class StoreError(SnapcraftError):
class SnapNotFoundError(StoreError):
- fmt = 'The "{name}" for {arch} was not found in {channel}.'
+ fmt = 'Snap "{name}" for {arch} cannot be found in the {channel} channel.'
@sergiusens

sergiusens Sep 27, 2016

Collaborator

Mind using !r here?

@ralsina

ralsina Sep 27, 2016

Contributor

Sure!

snapcraft/storeapi/errors.py
+ if arch:
+ super().__init__(name=name, channel=channel, arch=arch)
+ else:
+ self.fmt = 'Snap {name} was not found in the {channel} channel.'
@sergiusens

sergiusens Sep 27, 2016

Collaborator

Same !r

@ralsina

ralsina Sep 27, 2016

Contributor

Sure! For all args?

Looks good, few points to be improved inline.

Could you, please, squash the ~50 commits to make the PR cleaner.

integration_tests/test_gated.py
+class GatedTestCase(integration_tests.StoreTestCase):
+
+ def setUp(self):
+ if os.getenv('TEST_STORE') in ['staging', 'production']:
@cprov

cprov Sep 27, 2016

Contributor

The check below seems to be more effective and the pattern used in other places.

if os.getenv('TEST_STORE', 'fake') != 'fake':
@ralsina

ralsina Sep 27, 2016

Contributor

Done.

integration_tests/test_gated.py
+ self.assertEqual(0, self.gated('ubuntu-core', [
+ ('snap-1', '3'),
+ ('snap-2', '5'),
+ ]))
@cprov

cprov Sep 27, 2016

Contributor

this indentation is a little odd ... What about the following:

    validations = [('snap-1', '3'), ('snap-2', '5')]
    exit_code = self.gated('ubuntu-code', validations)
    self.assertEqual(0, exit_code)

(repeated in several tests below)

- raise RuntimeError(
- 'Snap {name} for {arch} cannot be found'
- ' in the {channel} channel'.format(name=snap_name, arch=arch,
- channel=channel))
@cprov

cprov Sep 27, 2016

Contributor

this code is gone because SnapNotFoundError will be raised to top-level exception handler with equivalent info, right ?

@ralsina

ralsina Sep 27, 2016

Contributor

Exactly.

snapcraft/_store.py
+
+def _get_snap_data(name, store):
+ details = store.cpi.get_package(name, 'stable', None)
+ return details
@cprov

cprov Sep 27, 2016

Contributor

I don't understand what this function buys us. Can't callsites call store.cpi.get_package() directly ?
Also, you've made "arch" optional, no need to pass None.

@ralsina

ralsina Sep 27, 2016

Contributor

Well, it was more complicated before :-) I agree it has become a bit too simple and can just be inlined.

snapcraft/storeapi/__init__.py
@@ -296,7 +305,8 @@ def get_package(self, snap_name, channel, arch):
}
params = {
'channel': channel,
- 'fields': 'status,download_url,download_sha512',
+ 'fields': 'status,download_url,download_sha512,snap_id,'
+ 'developer_id,release',
@cprov

cprov Sep 23, 2016

Contributor

as commented above, "developer_id" is not what you are looking for for validation assertions, it's always signed by the publisher.

@ralsina

ralsina Sep 27, 2016

Contributor

Done.

@cprov

cprov Sep 27, 2016

Contributor

So, you don't need 'developer_id' and 'release' in the current code.

@ralsina

ralsina Sep 27, 2016

Contributor

Fixed.

+ message = ('Invalid response from the server when pushing '
+ 'validations: {} {}').format(
+ response.status_code, response)
+ logger.debug(message)
@cprov

cprov Sep 27, 2016

Contributor

this is hardly a debug message, it's an error. What about making sure the raised exception has enough information to guide the user and that's it.
If our servers are not returning proper JSON and we don't know already ... it's more like our problem.

@ralsina

ralsina Sep 27, 2016

Contributor

This error usually will mean something like "we are getting HTML instead of JSON because we are pointing to the wrong URL" and the error message will say "Bad response". I think this was suggested by @sergiusens in the previous review, happy to change it if he's ok with it.

@cprov

cprov Sep 28, 2016

Contributor

ok, shows additional data if '-d' is given, I am sold.

@sergiusens

sergiusens Sep 29, 2016

Collaborator

@cprov I need this for the odd case of having issues in production where I can just tell people to add --debug :-)

snapcraft/storeapi/errors.py
+ try:
+ response_json = response.json()
+ # XXX This error is insanely verbose, SCA needs to trim it down
+ response_json['text'] = response.json()['error_list'][0]['message']
@ralsina

ralsina Sep 27, 2016

Contributor

Awesome. Want me to remove the XXX?

@cprov

cprov Sep 28, 2016

Contributor

Only if it fixes your problem.

snapcraft/tests/test_commands_gated.py
+
+ expected_output = """Name Approved
+snap-1 3
+snap-2 5"""
@cprov

cprov Sep 27, 2016

Contributor

would textwrap.dedent() help you here ?

@ralsina

ralsina Sep 27, 2016

Contributor

It will look nicer. Changing it.

.travis.yml
install:
- sudo apt-get -qq update
- sudo apt-get install -y python3-coverage
script:
- - docker run -e TEST_USER_PASSWORD=$TEST_USER_PASSWORD -v $(pwd):$(pwd) -t ubuntu:xenial sh -c "export LC_ALL=en_US.UTF-8 && locale-gen en_US.UTF-8 && apt update && cd $(pwd) && $DEPENDENCIES && ./runtests.sh $TEST_SUITE"
+ - docker run -e TEST_USER_EMAIL=$TEST_USER_EMAIL -e TEST_USER_PASSWORD=$TEST_USER_PASSWORD -e TEST_STORE=$TEST_STORE -v $(pwd):$(pwd) -t ubuntu:xenial sh -c "export LC_ALL=en_US.UTF-8 && locale-gen en_US.UTF-8 && apt update && cd $(pwd) && $DEPENDENCIES && ./runtests.sh $TEST_SUITE"
after_success:
- pip install coveralls
- coveralls
@elopio

elopio Sep 27, 2016

Member

There's something weird with your diff, this shouldn't be from your PR.

@ralsina

ralsina Sep 27, 2016

Contributor

I did something weird trying to collapse the commits I guess. I'd revert this one, but it's the same as master, right? I have no idea what to do about it :-)

integration_tests/test_gated.py
+ def setUp(self):
+ if os.getenv('TEST_STORE') != 'fake':
+ self.skipTest('Right combination of snaps and IDs is not '
+ 'available in real stores.')
@elopio

elopio Sep 27, 2016

Member

So, we will never be able to test this against a real system? There is no way to seed some valid values there?

@ralsina

ralsina Sep 27, 2016

Contributor

The main problem is that you need the logged in user to match against the publisher of several snaps in the store. I suppose we can make it work by creating a user account, uploading snaps for it and sharing the password between testers? (and also making the name of those snaps match the ones used internally on the tests... one is 'ubuntu-core' at this point)

@elopio

elopio Sep 29, 2016

Member

Yes, that sounds "good" to me. We have u1test+snapcraft@canonical.com, I can give you the password.

+ with open(fname, 'wb') as f:
+ f.write(assertion)
+
+ store.push_validation(snap_id, assertion)
@elopio

elopio Sep 27, 2016

Member

This function is too long, can you split it? You are splitting it with comments, it seems it could be nice to have one smaller function per comment.

@ralsina

ralsina Sep 27, 2016

Contributor

Sure! I'll do it tomorrow morning.

@ralsina

ralsina Sep 28, 2016

Contributor

I split it in 3, there is a largish chunk in the loop that's hard to split without writing functions with many args, which are a separate readability problem. Let me know if you like the current split or want me to split finer.

@cprov

cprov Sep 28, 2016

Contributor

It looks better now, thanks.

snapcraft/main.py
@@ -41,6 +41,9 @@
snapcraft [options] list-plugins
snapcraft [options] tour [<directory>]
snapcraft [options] update
+ snapcraft [options] gated <snap-name>
+ snapcraft [options] validate <snap-name> [--key-name=<key-name>] """ \
+"""[<snap-revision> ...]
@elopio

elopio Sep 27, 2016

Member

#noqa here wouldn't hurt, it could be nicer to read.

@ralsina

ralsina Sep 27, 2016

Contributor

Funny part: the NOQA comment goes 80 lines below here :-D

return any(args.get(command) for command in commands)
-def _run_store_command(args):
+# This function's complexity is correlated to the number of
+# commands, no point in checking that.
@elopio

elopio Sep 27, 2016

Member

well, actually ;)
I think it could be nice to split the commands that handle keys from the commands that handle snap uploads and releases. Not a big deal though, just if you agree.

Member

elopio commented Sep 27, 2016

Thanks Roberto, this looks pretty good. I've left some questions, I'm just worried about the lack of integration tests against the real server.
And please don't forget to leave your comments and suggestions :) If there's something you hated while implementing these features, let us know.

Contributor

ralsina commented Sep 27, 2016

Not hated, but docopt is weird once you have these many commands (I know sergio is removing it)

Collaborator

sergiusens commented Sep 28, 2016

El martes, 27 de septiembre de 2016 20h'19:10 ART, Roberto Alsina
notifications@github.com escribió:

Not hated, but docopt is weird once you have these many
commands (I know sergio is removing it)

I am sort of settled on click. Not sure if due to everything I read or the
irony 🙃

Enviado con Dekko desde mi dispositivo Ubuntu

ralsina added some commits Sep 28, 2016

Contributor

ralsina commented Sep 28, 2016

Ok, I think I covered all suggestions by @cprov and @elopio, so waiting for final reviews. Thanks for the effort reviewing this guys :-D

Thanks Roberto,

Few points to address and also make sure tests are passing.

integration_tests/test_gated.py
+class GatedTestCase(integration_tests.StoreTestCase):
+
+ def setUp(self):
+ if os.getenv('TEST_STORE') != 'fake':
@cprov

cprov Sep 28, 2016

Contributor

I would use os.getenv('TEST_STORE', 'fake') just to be on the safe-side in case TEST_STORE is not defined.

@ralsina

ralsina Sep 28, 2016

Contributor

getenv returns None for undefined vars...

@ralsina

ralsina Sep 28, 2016

Contributor

Doh. Of course someone may run this manually :-)

@ralsina

ralsina Sep 28, 2016

Contributor

done

integration_tests/test_validate.py
+class ValidateTestCase(integration_tests.StoreTestCase):
+
+ def setUp(self):
+ if os.getenv('TEST_STORE') != 'fake':
@cprov

cprov Sep 28, 2016

Contributor

ditto.

@ralsina

ralsina Sep 28, 2016

Contributor

done

snapcraft/_store.py
+ store = storeapi.StoreClient()
+ # Get data for the gating snap
+ with _requires_login():
+ snap_data = store.cpi.get_package(snap_name, 'stable', None)
@cprov

cprov Sep 28, 2016

Contributor

I think we should resolve the snap-id via account-info (logged users accessible snaps) instead of hitting CPI and require an active publication in 'stable'.

Also 'None' is not needed, arch is optional

@ralsina

ralsina Sep 28, 2016

Contributor

Argh, forgot to remove it when I made it optional :-P
I'll look into resolving via account-info now.

@ralsina

ralsina Sep 28, 2016

Contributor

Done

snapcraft/_store.py
+
+ # Get data for the gating snap
+ with _requires_login():
+ snap_data = store.cpi.get_package(snap_name, 'stable', None)
@cprov

cprov Sep 28, 2016

Contributor

Again, snap-id comes from account-info and 'None' is obsolete.

@ralsina

ralsina Sep 28, 2016

Contributor

Done

snapcraft/_store.py
+ # Then, for each requested validation, generate assertion
+ for validation in validations:
+ gated_name, rev = validation.split('=', 1)
+ approved_data = store.cpi.get_package(gated_name, 'stable', None)
@cprov

cprov Sep 28, 2016

Contributor

Right, for the validated snaps (3rd-part most likely) we hit CPI and require stable publications __ for now, because I wonder if we would even set things up while all snaps are in unstable channels, but I am sure we can sort this out later.
None is not needed.

@ralsina

ralsina Sep 28, 2016

Contributor

This only affects automatic updates, right? do we even do those for other channels?

@cprov

cprov Sep 28, 2016

Contributor

You are right, the happy path is enable refresh-control, no validation nothing gets updated even if the controlled snaps reach 'stable'. Once there one overrides refresh-control and installs the not-yet-validation snaps, run tests, then create the corresponding validations.
Thanks for clarifying this.

+ with open(fname, 'wb') as f:
+ f.write(assertion)
+
+ store.push_validation(snap_id, assertion)
@elopio

elopio Sep 27, 2016

Member

This function is too long, can you split it? You are splitting it with comments, it seems it could be nice to have one smaller function per comment.

@ralsina

ralsina Sep 27, 2016

Contributor

Sure! I'll do it tomorrow morning.

@ralsina

ralsina Sep 28, 2016

Contributor

I split it in 3, there is a largish chunk in the loop that's hard to split without writing functions with many args, which are a separate readability problem. Let me know if you like the current split or want me to split finer.

@cprov

cprov Sep 28, 2016

Contributor

It looks better now, thanks.

+ message = ('Invalid response from the server when pushing '
+ 'validations: {} {}').format(
+ response.status_code, response)
+ logger.debug(message)
@cprov

cprov Sep 27, 2016

Contributor

this is hardly a debug message, it's an error. What about making sure the raised exception has enough information to guide the user and that's it.
If our servers are not returning proper JSON and we don't know already ... it's more like our problem.

@ralsina

ralsina Sep 27, 2016

Contributor

This error usually will mean something like "we are getting HTML instead of JSON because we are pointing to the wrong URL" and the error message will say "Bad response". I think this was suggested by @sergiusens in the previous review, happy to change it if he's ok with it.

@cprov

cprov Sep 28, 2016

Contributor

ok, shows additional data if '-d' is given, I am sold.

@sergiusens

sergiusens Sep 29, 2016

Collaborator

@cprov I need this for the odd case of having issues in production where I can just tell people to add --debug :-)

snapcraft/storeapi/errors.py
+ try:
+ response_json = response.json()
+ # XXX This error is insanely verbose, SCA needs to trim it down
+ response_json['text'] = response.json()['error_list'][0]['message']
@ralsina

ralsina Sep 27, 2016

Contributor

Awesome. Want me to remove the XXX?

@cprov

cprov Sep 28, 2016

Contributor

Only if it fixes your problem.

ralsina added some commits Sep 28, 2016

i think I have the last set of feedback from your Initial skeleton for validate 😉

snapcraft/main.py
snapcraft [options] upload <snap-file>
snapcraft [options] push <snap-file> [--release <channels>]
snapcraft [options] release <snap-name> <revision> <channel>
snapcraft [options] list-plugins
snapcraft [options] tour [<directory>]
snapcraft [options] update
+ snapcraft [options] gated <snap-name>
+ snapcraft [options] validate <snap-name> [<snap-revision> ...] [--key-name=<key-name>]
@sergiusens

sergiusens Sep 29, 2016

Collaborator

Making [<snap-revision> ...] [--key-name=<key-name>] optional allows me to run
snapcraft validate <snap-name> which provides no useful output and probably invalid:

$ snapcraft validate telegram-sergiusens
Getting details for telegram-sergiusens
$
@sergiusens

sergiusens Sep 29, 2016

Collaborator

<snap-revision> is f the form <snap-name>=<revision>? This needs a better arg-name.

@sergiusens

sergiusens Sep 29, 2016

Collaborator

maybe <snap-name-revision-pair>

@ralsina

ralsina Sep 29, 2016

Contributor

Better arg-name ... maybe "validation"?

Yes, it's always snap-name=revision

And it's most definitely not optional, there must be at least one. How does one tell docopt "one or more of these"?

Yes, calling "snapcraft validate telegram-sergiusens" currently does nothing. I can add a check for "at least one validation" if there's no way to tell docopt to do it.

@ralsina

ralsina Sep 29, 2016

Contributor

@sergiusens I just pushed a change where it now is <validation>... which renames it, and makes it required at least once.

+ message = ('Invalid response from the server when pushing '
+ 'validations: {} {}').format(
+ response.status_code, response)
+ logger.debug(message)
@cprov

cprov Sep 27, 2016

Contributor

this is hardly a debug message, it's an error. What about making sure the raised exception has enough information to guide the user and that's it.
If our servers are not returning proper JSON and we don't know already ... it's more like our problem.

@ralsina

ralsina Sep 27, 2016

Contributor

This error usually will mean something like "we are getting HTML instead of JSON because we are pointing to the wrong URL" and the error message will say "Bad response". I think this was suggested by @sergiusens in the previous review, happy to change it if he's ok with it.

@cprov

cprov Sep 28, 2016

Contributor

ok, shows additional data if '-d' is given, I am sold.

@sergiusens

sergiusens Sep 29, 2016

Collaborator

@cprov I need this for the odd case of having issues in production where I can just tell people to add --debug :-)

Collaborator

sergiusens commented Sep 30, 2016

ok to test

@sergiusens sergiusens merged commit 2d429cf into snapcore:master Sep 30, 2016

4 of 6 checks passed

xenial autopkgtest integration
Details
yakkety autopkgtest integration
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
coverage/coveralls Coverage increased (+0.002%) to 98.15%
Details
xenial autopkgtest snaps Success
Details
yakkety autopkgtest snaps Success
Details

kalikiana pushed a commit to kalikiana/snapcraft that referenced this pull request Apr 6, 2017

@sergiusens sergiusens referenced this pull request Sep 20, 2017

Merged

store: handle revoked developers #1554

6 of 6 tasks complete
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment