Skip to content

Commit

Permalink
Add support for Alfred 3-only updates
Browse files Browse the repository at this point in the history
  • Loading branch information
deanishe committed Jun 25, 2016
1 parent 5b0c72c commit b87f117
Show file tree
Hide file tree
Showing 18 changed files with 481 additions and 120 deletions.
Binary file added alfred-workflow-1.17.4.zip
Binary file not shown.
36 changes: 26 additions & 10 deletions docs/user-manual/update.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,10 @@ Users can turn off automatic checks for updates with the ``workflow:noautoupdate
:ref:`magic argument <magic-arguments>` and back on again with
``workflow:autoupdate``.

.. danger::

If you are not careful, you might accidentally overwrite a local version of
the workflow you're working on and lose all your changes! It's a good idea
to make sure you increase the version number *before* you start making any
changes.


Currently, only updates from `GitHub releases`_ are supported.


GitHub releases
===============

Expand All @@ -39,9 +33,10 @@ For your workflow to be able to recognise and download newer versions, the
be one of the versions (i.e. tags) in the corresponding GitHub repo's
releases list. See :ref:`version-numbers` for more information.

There must be **one (and only one)** ``.alfredworkflow`` binary attached to a
release otherwise the release will be ignored. This is the file that will be
downloaded and installed via Alfred's default installation mechanism.
There must be **one (and only one)** ``.alfredworkflow`` and/or **one (and only
one)** ``.alfred3workflow`` binary attached to a release otherwise the release
will be ignored. This is the file that will be downloaded and installed via
Alfred's default installation mechanism.

.. important::

Expand All @@ -50,6 +45,27 @@ downloaded and installed via Alfred's default installation mechanism.
been enabled or the ``prereleases`` key is set to ``True`` in the
``update_settings`` :class:`dict`.


Supporting Alfred 2 and Alfred 3
--------------------------------

Alfred 3 workflows are fundamentally incompatible with Alfred 2.

If you want to make a new release of an existing workflow that breaks
compatibility with Alfred 2, it's important that you use the alternate
``.alfred3workflow`` file extension for your release binaries to prevent Alfred
2 installations trying to update themselves to death.

You can have both an ``.alfredworkflow`` file and an ``.alfred3workflow`` file
in the same release. If Alfred-Workflow is running under Alfred 3, it will
prefer the ``.alfred3workflow`` if present. Under Alfred 2, or if the release
contains no ``.alfred3workflow`` file, Alfred-Workflow will use the
``.alfredworkflow`` file.

There may only be one file of each type, however, or the release will be
considered invalid.


Configuration
=============

Expand Down
5 changes: 5 additions & 0 deletions requirements-test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pytest==2.8.7
pytest-cov==2.2.1
pytest-httpbin==0.2.0
pytest-localserver==0.3.5
tox==2.3.1
File renamed without changes.
78 changes: 78 additions & 0 deletions tests/data/info.plist.alfred3
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>bundleid</key>
<string>net.deanishe.alfred-workflow</string>
<key>connections</key>
<dict/>
<key>createdby</key>
<string>Dean Jackson</string>
<key>description</key>
<string>Test alfred-workflow library</string>
<key>disabled</key>
<false/>
<key>name</key>
<string>Alfred-Workflow Test</string>
<key>objects</key>
<array>
<dict>
<key>config</key>
<dict>
<key>alfredfiltersresults</key>
<false/>
<key>argumenttype</key>
<integer>0</integer>
<key>escaping</key>
<integer>102</integer>
<key>keyword</key>
<string>wftest</string>
<key>queuedelaycustom</key>
<integer>1</integer>
<key>queuedelayimmediatelyinitially</key>
<false/>
<key>queuedelaymode</key>
<integer>0</integer>
<key>queuemode</key>
<integer>1</integer>
<key>runningsubtext</key>
<string>Doin' stuff…</string>
<key>script</key>
<string>python test.py "$@"</string>
<key>scriptargtype</key>
<integer>1</integer>
<key>scriptfile</key>
<string></string>
<key>subtext</key>
<string>Test alfred-workflow Python lib</string>
<key>title</key>
<string>Alfred-Workflow Test</string>
<key>type</key>
<integer>0</integer>
<key>withspace</key>
<true/>
</dict>
<key>type</key>
<string>alfred.workflow.input.scriptfilter</string>
<key>uid</key>
<string>5F480F88-2088-4D34-B621-ACEBCB5E6753</string>
<key>version</key>
<integer>2</integer>
</dict>
</array>
<key>readme</key>
<string></string>
<key>uidata</key>
<dict>
<key>5F480F88-2088-4D34-B621-ACEBCB5E6753</key>
<dict>
<key>xpos</key>
<integer>30</integer>
<key>ypos</key>
<integer>30</integer>
</dict>
</dict>
<key>webaddress</key>
<string></string>
</dict>
</plist>
62 changes: 0 additions & 62 deletions tests/info.plist.test

This file was deleted.

2 changes: 2 additions & 0 deletions tests/test_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@
def info(request):
"""Ensure `info.plist` exists in the working directory."""
create_info_plist()
os.environ['alfred_version'] = '2.4'
update._wf = None
request.addfinalizer(delete_info_plist)


Expand Down
135 changes: 135 additions & 0 deletions tests/test_update_alfred3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#!/usr/bin/env python
# encoding: utf-8
#
# Copyright (c) 2014 Fabio Niephaus <[email protected]>,
# Dean Jackson <[email protected]>
#
# MIT Licence. See http://opensource.org/licenses/MIT
#
# Created on 2014-08-16
#

# TODO: Offline tests using pytest_localserver

"""
test_update_alfred3.py
"""

from __future__ import print_function

import os

import pytest

from util import create_info_plist, delete_info_plist, INFO_PLIST_TEST3
from workflow import update


@pytest.fixture(scope='module')
def info(request):
"""Ensure `info.plist` exists in the working directory."""
create_info_plist()
os.environ['alfred_version'] = '2.4'
update._wf = None
request.addfinalizer(delete_info_plist)


@pytest.fixture(scope='module')
def info3(request):
"""Ensure `info.plist` exists in the working directory."""
create_info_plist(INFO_PLIST_TEST3)
os.environ['alfred_version'] = '3.0.2'
update._wf = None
request.addfinalizer(delete_info_plist)


def test_valid_releases_alfred2(info):
"""Valid releases for Alfred 2."""
# Valid release for 2 & 3
r = update._validate_release({'tag_name': 'v1.2', 'assets': [
{'browser_download_url':
'blah.alfredworkflow'}],
'prerelease': False})

assert r is not None
assert r['prerelease'] is False
assert r['download_url'] == 'blah.alfredworkflow'

# Valid release for 3 only
r = update._validate_release({'tag_name': 'v1.2', 'assets': [
{'browser_download_url':
'blah.alfred3workflow'}],
'prerelease': False})

assert r is None

# Invalid release
r = update._validate_release({'tag_name': 'v1.2', 'assets': [
{'browser_download_url':
'blah.alfred3workflow'},
{'browser_download_url':
'blah2.alfred3workflow'}],
'prerelease': False})

assert r is None

# Valid for 2 & 3 with separate workflows
r = update._validate_release({'tag_name': 'v1.2', 'assets': [
{'browser_download_url':
'blah.alfredworkflow'},
{'browser_download_url':
'blah.alfred3workflow'}],
'prerelease': False})

assert r is not None
assert r['version'] == 'v1.2'
assert r['download_url'] == 'blah.alfredworkflow'


def test_valid_releases_alfred3(info3):
"""Valid releases for Alfred 3."""
# Valid release for 2 & 3
r = update._validate_release({'tag_name': 'v1.2', 'assets': [
{'browser_download_url':
'blah.alfredworkflow'}],
'prerelease': False})

assert r is not None
assert r['download_url'] == 'blah.alfredworkflow'

# Valid release for 3 only
print('alfred_version=', os.environ['alfred_version'])
print('version=', update.wf().alfred_version)
r = update._validate_release({'tag_name': 'v1.2', 'assets': [
{'browser_download_url':
'blah.alfred3workflow'}],
'prerelease': False})

assert r is not None
assert r['download_url'] == 'blah.alfred3workflow'

# Invalid release
r = update._validate_release({'tag_name': 'v1.2', 'assets': [
{'browser_download_url':
'blah.alfred3workflow'},
{'browser_download_url':
'blah2.alfred3workflow'}],
'prerelease': False})

assert r is None

# Valid for 2 & 3 with separate workflows
r = update._validate_release({'tag_name': 'v1.2', 'assets': [
{'browser_download_url':
'blah.alfredworkflow'},
{'browser_download_url':
'blah.alfred3workflow'}],
'prerelease': False})

assert r is not None
assert r['version'] == 'v1.2'
assert r['download_url'] == 'blah.alfred3workflow'


if __name__ == '__main__': # pragma: no cover
pytest.main([__file__])
5 changes: 2 additions & 3 deletions tests/test_update_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
#
# Created on 2014-08-16
#
"""
test_versions.py
"""

"""Test `update.Version` class."""

from __future__ import print_function

Expand Down
10 changes: 8 additions & 2 deletions tests/test_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,12 @@ def test_no_encoding(self):

def test_xml_encoding(self):
"""XML is decoded"""
url = self.httpbin.url + '/response-headers'
# Why doesn't this work with a local httpbin?
# url = self.httpbin.url + '/response-headers'
url = 'http://httpbin.org/response-headers'
params = {'Content-Type': 'text/xml;charset=UTF-8'}
r = web.get(url, params)
r.raise_for_status()
self.assertEqual(r.encoding, 'utf-8')
self.assert_(isinstance(r.text, unicode))

Expand All @@ -224,11 +227,14 @@ def test_html_encoding(self):

def test_default_encoding(self):
"""Default encodings for mimetypes"""
url = self.httpbin.url + '/response-headers'
# Why doesn't this work with a local httpbin?
# url = self.httpbin.url + '/response-headers'
url = 'http://httpbin.org/response-headers'
# params = {'Content-Type': 'application/json'}
# httpbin returns JSON by default. web.py should automatically
# set `encoding` to UTF-8 when mimetype = 'application/json'
r = web.get(url)
r.raise_for_status()
self.assertEqual(r.encoding, 'utf-8')
self.assert_(isinstance(r.text, unicode))

Expand Down
Loading

0 comments on commit b87f117

Please sign in to comment.