Skip to content

Commit

Permalink
Read all available releases from code hoster
Browse files Browse the repository at this point in the history
addresses: wbond/packagecontrol.io#143

This commit introduces a generator to fetch all tags until desired
amount of releases is found.

Before this commit clients downloaded first 100 tags to fetch
releases. If a repository however contains many releases with
version-prefixes, it is likely for some of them to be ignored due to
this restriction.
  • Loading branch information
deathaxe committed Aug 26, 2022
1 parent f5fa90f commit f8e2e49
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 68 deletions.
51 changes: 26 additions & 25 deletions package_control/clients/bitbucket_client.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import re
from urllib.parse import quote
from urllib.parse import urlencode, quote

from ..downloaders.downloader_exception import DownloaderException
from ..versions import version_sort, version_process
from ..versions import version_match_prefix
from .json_api_client import JSONApiClient


Expand Down Expand Up @@ -173,36 +173,37 @@ def download_info_from_tags(self, url, tag_prefix=None):
if not tags_match:
return None

user_repo = tags_match.group(1)

tags_list = {}
tags_url = self._api_url(user_repo, '/refs/tags?pagelen=100')
while tags_url:
tags_json = self.fetch_json(tags_url)
for tag in tags_json['values']:
tags_list[tag['name']] = tag['target']['date'][0:19].replace('T', ' ')
tags_url = tags_json['next'] if 'next' in tags_json else None
def _get_releases(user_repo, tag_prefix, page_size=100):
used_versions = set()
query_string = urlencode({'pagelen': page_size})
tags_url = self._api_url(user_repo, '/refs/tags?%s' % query_string)
while tags_url:
tags_json = self.fetch_json(tags_url)
for tag in tags_json['values']:
version = version_match_prefix(tag['name'], tag_prefix)
if version and version not in used_versions:
used_versions.add(version)
yield (
version,
tag['name'],
tag['target']['date'][0:19].replace('T', ' ')
)

tags_url = tags_json.get('next')

tag_info = version_process(tags_list.keys(), tag_prefix)
tag_info = version_sort(tag_info, reverse=True)
if not tag_info:
return False
user_repo = tags_match.group(1)

max_releases = self.settings.get('max_releases', 0)
num_releases = 0

output = []
used_versions = set()
for info in tag_info:
version = info['version']
if version in used_versions:
continue

tag = info['prefix'] + version
for release in sorted(_get_releases(user_repo, tag_prefix), reverse=True):
version, tag, timestamp = release

output.append(self._make_download_info(user_repo, tag, version, tags_list[tag]))
output.append(self._make_download_info(user_repo, tag, str(version), timestamp))

used_versions.add(version)
if max_releases > 0 and len(used_versions) >= max_releases:
num_releases += not version.prerelease
if max_releases > 0 and num_releases >= max_releases:
break

return output
Expand Down
45 changes: 25 additions & 20 deletions package_control/clients/github_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from urllib.parse import urlencode, quote

from ..downloaders.downloader_exception import DownloaderException
from ..versions import version_sort, version_process
from ..versions import version_match_prefix
from .json_api_client import JSONApiClient


Expand Down Expand Up @@ -158,32 +158,37 @@ def download_info_from_tags(self, url, tag_prefix=None):
if not tags_match:
return None

user_repo = tags_match.group(1)
tags_url = self._api_url(user_repo, '/tags?per_page=100')
tags_json = self.fetch_json(tags_url)
tag_urls = {tag['name']: tag['commit']['url'] for tag in tags_json}
tag_info = version_process(tag_urls.keys(), tag_prefix)
tag_info = version_sort(tag_info, reverse=True)
if not tag_info:
return False
def _get_releases(user_repo, tag_prefix=None, page_size=100):
used_versions = set()
for page in range(100):
query_string = urlencode({'page': page * page_size, 'per_page': page_size})
tags_url = self._api_url(user_repo, '/tags?%s' % query_string)
tags_json = self.fetch_json(tags_url)

for tag in tags_json:
version = version_match_prefix(tag['name'], tag_prefix)
if version and version not in used_versions:
used_versions.add(version)
yield (version, tag['name'], tag['commit']['url'])

if len(tags_json) < page_size:
return

user_repo = tags_match.group(1)
max_releases = self.settings.get('max_releases', 0)
num_releases = 0

output = []
used_versions = set()
for info in tag_info:
version = info['version']
if version in used_versions:
continue

tag = info['prefix'] + version
tag_info = self.fetch_json(tag_urls[tag])
for release in sorted(_get_releases(user_repo, tag_prefix), reverse=True):
version, tag, tag_url = release

tag_info = self.fetch_json(tag_url)
timestamp = tag_info['commit']['committer']['date'][0:19].replace('T', ' ')

output.append(self._make_download_info(user_repo, tag, version, timestamp))
output.append(self._make_download_info(user_repo, tag, str(version), timestamp))

used_versions.add(version)
if max_releases > 0 and len(used_versions) >= max_releases:
num_releases += not version.prerelease
if max_releases > 0 and num_releases >= max_releases:
break

return output
Expand Down
52 changes: 29 additions & 23 deletions package_control/clients/gitlab_client.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import re
from urllib.parse import quote
from urllib.parse import urlencode, quote

from ..downloaders.downloader_exception import DownloaderException
from ..versions import version_process, version_sort
from ..versions import version_match_prefix
from .json_api_client import JSONApiClient


Expand Down Expand Up @@ -161,34 +161,40 @@ def download_info_from_tags(self, url, tag_prefix=None):
if not tags_match:
return None

user_name, repo_name = tags_match.groups()
repo_id = '%s%%2F%s' % (user_name, repo_name)
tags_url = self._api_url(repo_id, '/repository/tags?per_page=100')
tags_json = self.fetch_json(tags_url)
tags_list = {
tag['name']: tag['commit']['committed_date'][0:19].replace('T', ' ')
for tag in tags_json
}
def _get_releases(user_repo, tag_prefix=None, page_size=100):
used_versions = set()
for page in range(100):
query_string = urlencode({'page': page * page_size, 'per_page': page_size})
tags_url = self._api_url(user_repo, '/repository/tags?%s' % query_string)
tags_json = self.fetch_json(tags_url)

for tag in tags_json:
version = version_match_prefix(tag['name'], tag_prefix)
if version and version not in used_versions:
used_versions.add(version)
yield (
version,
tag['name'],
tag['commit']['committed_date'][0:19].replace('T', ' ')
)

if len(tags_json) < page_size:
return

tag_info = version_process(tags_list.keys(), tag_prefix)
tag_info = version_sort(tag_info, reverse=True)
if not tag_info:
return False
user_name, repo_name = tags_match.groups()
user_repo = '%s%%2F%s' % (user_name, repo_name)

max_releases = self.settings.get('max_releases', 0)
num_releases = 0

output = []
used_versions = set()
for info in tag_info:
version = info['version']
if version in used_versions:
continue
for release in sorted(_get_releases(user_repo, tag_prefix), reverse=True):
version, tag, timestamp = release

tag = info['prefix'] + version
output.append(self._make_download_info(user_name, repo_name, tag, version, tags_list[tag]))
output.append(self._make_download_info(user_name, repo_name, tag, str(version), timestamp))

used_versions.add(version)
if max_releases > 0 and len(used_versions) >= max_releases:
num_releases += not version.prerelease
if max_releases > 0 and num_releases >= max_releases:
break

return output
Expand Down
3 changes: 3 additions & 0 deletions package_control/semver.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,9 @@ def __repr__(self):
return 'SemVer("%s")' % str(self)
# return 'SemVer(%s)' % ', '.join('%s=%r' % (k, getattr(self, k)) for k in self._fields)

def __hash__(self):
return hash(str(self))

def __len__(self):
return 3 + (self.build is not None and 2 or self.prerelease is not None)

Expand Down
20 changes: 20 additions & 0 deletions package_control/versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,26 @@ def version_exclude_prerelease(versions):
return output


def version_match_prefix(version, filter_prefix):
"""
Create a SemVer for a given version, if it matches filter_prefix.
:param version: The version
:type version: { type_description }
:param filter_prefix: The filter prefix
:type filter_prefix: { type_description }
"""
try:
if filter_prefix:
if version.startswith(filter_prefix):
return version_comparable(version[len(filter_prefix):])
else:
return version_comparable(version)
except ValueError:
pass
return None


def version_process(versions, filter_prefix):
"""
Filter a list of versions to ones that are valid SemVers, if a prefix
Expand Down

0 comments on commit f8e2e49

Please sign in to comment.