diff --git a/README.rst b/README.rst
index e59bfae..fb2ad8d 100644
--- a/README.rst
+++ b/README.rst
@@ -49,6 +49,7 @@ Contents
* `Check Repology (repology.org) <#check-repology>`_
* `Check Anitya (release-monitoring.org) <#check-anitya>`_
* `Check Android SDK <#check-android-sdk>`_
+ * `Check Sparkle framework <#check-sparkle-framework>`_
* `Manually updating <#manually-updating>`_
* `Version Control System (VCS) (git, hg, svn, bzr) <#version-control-system-vcs-git-hg-svn-bzr>`_
* `Other <#other>`_
@@ -506,6 +507,13 @@ android_sdk
repo
Should be one of ``addon`` or ``package``. Packages in ``addon2-1.xml`` use ``addon`` and packages in ``repository2-1.xml`` use ``package``.
+Check Sparkle framework
+-----------------------
+This enables you to track updates of macOS applications which using `Sparkle framework `_.
+
+sparkle
+ The url of the sparkle appcast.
+
Manually updating
-----------------
This enables you to manually specify the version (maybe because you want to approve each release before it gets to the script).
diff --git a/nvchecker/get_version.py b/nvchecker/get_version.py
index 68ef721..79a9f07 100644
--- a/nvchecker/get_version.py
+++ b/nvchecker/get_version.py
@@ -15,7 +15,7 @@ handler_precedence = (
'gems', 'pacman',
'cmd', 'bitbucket', 'regex', 'manual', 'vcs',
'cratesio', 'npm', 'hackage', 'cpan', 'gitlab', 'packagist',
- 'repology', 'anitya', 'android_sdk',
+ 'repology', 'anitya', 'android_sdk', 'sparkle',
)
def substitute_version(version, name, conf):
diff --git a/nvchecker/source/sparkle.py b/nvchecker/source/sparkle.py
new file mode 100644
index 0000000..ca2859f
--- /dev/null
+++ b/nvchecker/source/sparkle.py
@@ -0,0 +1,31 @@
+# MIT licensed
+# Copyright (c) 2020 Sunlei
+
+from xml.etree import ElementTree
+
+from . import session
+
+
+async def get_version(name, conf, **kwargs):
+ sparkle = conf['sparkle']
+
+ async with session.get(sparkle) as res:
+ resp = await res.read()
+
+ root = ElementTree.fromstring(resp)
+ item = root.find('./channel/item[1]/enclosure')
+
+ version_string = item.get('{http://www.andymatuschak.org/xml-namespaces/sparkle}shortVersionString')
+ build_number = item.get('{http://www.andymatuschak.org/xml-namespaces/sparkle}version')
+
+ if (version_string and version_string.isdigit()) and (build_number and not build_number.isdigit()):
+ version_string, build_number = build_number, version_string
+
+ version = []
+
+ if version_string:
+ version.append(version_string)
+ if build_number:
+ version.append(build_number)
+
+ return '-'.join(version) if version else None
diff --git a/tests/test_sparkle.py b/tests/test_sparkle.py
new file mode 100644
index 0000000..8c210c6
--- /dev/null
+++ b/tests/test_sparkle.py
@@ -0,0 +1,8 @@
+# MIT licensed
+# Copyright (c) 2020 Sunlei
+
+import pytest
+pytestmark = [pytest.mark.asyncio, pytest.mark.needs_net]
+
+async def test_sparkle(get_version):
+ assert await get_version("example", {"sparkle": "https://sparkle-project.org/files/sparkletestcast.xml"}) == "2.0"