Skip to content

Commit

Permalink
Add push on top of snapcraft 2.39.2 (git submodule)
Browse files Browse the repository at this point in the history
  • Loading branch information
JohanLorenzo committed Mar 12, 2018
1 parent b7a79a1 commit 44fcf6c
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 11 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "snapcraft"]
path = snapcraft
url = https://github.com/snapcore/snapcraft.git
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ env:

install:
- travis_retry pip install tox
# XXX Avoid snapcraft from loading useless libs when running on Ubuntu
- sudo truncate -s 0 /etc/os-release
script:
- tox -e py35
after_success:
Expand Down
4 changes: 2 additions & 2 deletions pushsnapscript/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""
from scriptworker import client

from pushsnapscript import artifacts, task
from pushsnapscript import artifacts, snap_store, task


async def async_main(context):
Expand All @@ -12,7 +12,7 @@ async def async_main(context):
# TODO Sanity checks on the file
snap_file_path = artifacts.get_snap_file_path(context)
channel = task.pluck_channel(context.task)
print(snap_file_path, channel)
snap_store.push(context, snap_file_path, channel)


__name__ == '__main__' and client.sync_main(async_main)
34 changes: 34 additions & 0 deletions pushsnapscript/snap_store.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import base64
import os

from scriptworker.utils import makedirs

from pushsnapscript.utils import cwd

# XXX Hack to only import a subset of snapcraft. Otherwise snapcraft can't be built on any other
# distribution than Ubuntu. The prod instance runs CentOS 6. There isn't a package version of
# snapcraft on that platform either.
import sys
dir_path = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(dir_path, '..', 'snapcraft'))
from snapcraft import _store as snapcraft_store_client # noqa


def push(context, snap_file_path, channel):
# Snapcraft requires credentials to be stored at $CWD/.snapcraft/snapcraft.cfg. Let's store them
# in a folder that gets purged at the end of the run.
_craft_credentials_file(context, channel)
with cwd(context.config['work_dir']):
snapcraft_store_client.push(snap_file_path, channel)


def _craft_credentials_file(context, channel):
base64_creds = context.config['base64_macaroons_configs'][channel]
decoded_creds_bytes = base64.b64decode(base64_creds)
decoded_creds = decoded_creds_bytes.decode()

snapcraft_dir = os.path.join(context.config['work_dir'], '.snapcraft')
makedirs(snapcraft_dir)
snapcraft_config_file = os.path.join(snapcraft_dir, 'snapcraft.cfg')
with open(snapcraft_config_file, 'w') as f:
f.write(decoded_creds)
20 changes: 14 additions & 6 deletions pushsnapscript/test/test_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,27 @@
from scriptworker import client
from unittest.mock import MagicMock

from pushsnapscript import artifacts, task
from pushsnapscript import artifacts, task, snap_store
from pushsnapscript.script import async_main


@pytest.mark.asyncio
async def test_hello_world(capsys, monkeypatch):
async def test_async_main(monkeypatch):
function_call_counter = (n for n in range(0, 2))

context = MagicMock()
monkeypatch.setattr(client, 'get_task', lambda _: {})
monkeypatch.setattr(artifacts, 'get_snap_file_path', lambda _: '/some/file.snap')
monkeypatch.setattr(task, 'pluck_channel', lambda _: 'edge')
context = MagicMock()

def assert_push(context_, file_, channel):
assert context_ == context
assert file_ == '/some/file.snap'
assert channel == 'edge'
next(function_call_counter)

monkeypatch.setattr(snap_store, 'push', assert_push)

await async_main(context)

captured = capsys.readouterr()
assert captured.out == '/some/file.snap edge\n'
assert captured.err == ''
assert next(function_call_counter) == 1
53 changes: 53 additions & 0 deletions pushsnapscript/test/test_snap_store.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import pytest
import os
import tempfile

from unittest.mock import MagicMock

from pushsnapscript.snap_store import snapcraft_store_client, push, _craft_credentials_file

SNAPCRAFT_SAMPLE_CONFIG = '''[login.ubuntu.com]
macaroon = SomeBase64
unbound_discharge = SomeOtherBase64
email = release@m.c
'''

SNAPCRAFT_SAMPLE_CONFIG_BASE64 = 'W2xvZ2luLnVidW50dS5jb21dCm1hY2Fyb29uID0gU29tZUJhc2U2NAp1bmJvdW5kX\
2Rpc2NoYXJnZSA9IFNvbWVPdGhlckJhc2U2NAplbWFpbCA9IHJlbGVhc2VAbS5jCg=='


@pytest.mark.parametrize('channel', ('edge', 'candidate'))
def test_push(monkeypatch, channel):
generator = (n for n in range(0, 2))

context = MagicMock()
context.config = {'base64_macaroons_configs': {channel: SNAPCRAFT_SAMPLE_CONFIG_BASE64}}

with tempfile.TemporaryDirectory() as d:
def snapcraft_store_client_push_fake(snap_file_path, channel):
# This function can't be a regular mock because of the following check:
assert os.getcwd() == d # Push must be done from the work_dir

assert snap_file_path == '/some/file.snap'
assert channel == channel
next(generator)

context.config['work_dir'] = d
monkeypatch.setattr(snapcraft_store_client, 'push', snapcraft_store_client_push_fake)
push(context, '/some/file.snap', channel)

assert os.getcwd() != d

assert next(generator) == 1 # Check fake function was called once


@pytest.mark.parametrize('channel', ('edge', 'candidate'))
def test_craft_credentials_file(channel):
context = MagicMock()
context.config = {'base64_macaroons_configs': {channel: SNAPCRAFT_SAMPLE_CONFIG_BASE64}}

with tempfile.TemporaryDirectory() as d:
context.config['work_dir'] = d
_craft_credentials_file(context, channel)
with open(os.path.join(d, '.snapcraft', 'snapcraft.cfg')) as f:
assert f.read() == SNAPCRAFT_SAMPLE_CONFIG
15 changes: 15 additions & 0 deletions pushsnapscript/test/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import os
import tempfile

from pushsnapscript.utils import cwd


def test_cwd():
old_working_dir = os.getcwd()
with tempfile.TemporaryDirectory() as d:
with cwd(d):
current_working_dir = os.getcwd()
assert current_working_dir == d
assert old_working_dir != current_working_dir

assert old_working_dir == os.getcwd()
12 changes: 12 additions & 0 deletions pushsnapscript/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import contextlib
import os


@contextlib.contextmanager
def cwd(new_cwd):
current_dir = os.getcwd()
try:
os.chdir(new_cwd)
yield
finally:
os.chdir(current_dir)
16 changes: 15 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,16 @@
aiohttp
scriptworker

# XXX Dependencies required by snapcraft._store.push
click
progressbar33
pyelftools
pymacaroons
pysha3
python-debian
pyxdg
PyYAML
requests
requests-toolbelt
requests-unixsocket
simplejson
tabulate
1 change: 1 addition & 0 deletions snapcraft
Submodule snapcraft added at e7e331
4 changes: 2 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ commands=

[flake8]
max-line-length = 160
exclude = .ropeproject,.tox,sandbox
exclude = .ropeproject,.tox,sandbox,snapcraft
show-source = True

[pytest]
norecursedirs = .tox .git .hg sandbox
norecursedirs = .tox .git .hg sandbox snapcraft
python_files = test_*.py

0 comments on commit 44fcf6c

Please sign in to comment.