diff --git a/docs/usage.rst b/docs/usage.rst index c5ea879..60f3cc4 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -365,8 +365,8 @@ Check GitHub source = "github" Check `GitHub `_ for updates. The version returned is in -date format ``%Y%m%d.%H%M%S``, e.g. ``20130701.012212``, unless ``use_latest_release`` -or ``use_max_tag`` is used. See below. +date format ``%Y%m%d.%H%M%S``, e.g. ``20130701.012212``, unless ``use_latest_release``, +``use_max_tag``, or ``use_commit_name`` is used. See below. github The github repository, with author, e.g. ``lilydjwg/nvchecker``. @@ -393,6 +393,10 @@ use_latest_tag This requires a token because it's using the v4 GraphQL API. +use_commit_name + Set this to ``true`` to append a plus and the commit name to the version, e.g. + ``20130701.012212+e1457aadd30f53f4d50d6c4828d517355c09b8ae``. + query When ``use_latest_tag`` is ``true``, this sets a query for the tag. The exact matching method is not documented by GitHub. diff --git a/nvchecker_source/github.py b/nvchecker_source/github.py index 0920d54..84eedde 100644 --- a/nvchecker_source/github.py +++ b/nvchecker_source/github.py @@ -1,9 +1,10 @@ # MIT licensed # Copyright (c) 2013-2020 lilydjwg , et al. +import itertools import time from urllib.parse import urlencode -from typing import Tuple +from typing import Optional, Tuple import structlog @@ -14,6 +15,9 @@ from nvchecker.api import ( logger = structlog.get_logger(logger_name=__name__) +def add_commit_name(version: str, commit_name: Optional[str]) -> str: + return version if commit_name is None else version + '+' + commit_name + GITHUB_URL = 'https://api.github.com/repos/%s/commits' GITHUB_LATEST_RELEASE = 'https://api.github.com/repos/%s/releases/latest' # https://developer.github.com/v3/git/refs/#get-all-references @@ -27,38 +31,45 @@ async def get_version(name, conf, **kwargs): check_ratelimit(e, name) QUERY_LATEST_TAG = ''' -{{ - repository(name: "{name}", owner: "{owner}") {{ - refs(refPrefix: "refs/tags/", first: 1, - query: "{query}", - orderBy: {{field: TAG_COMMIT_DATE, direction: DESC}}) {{ - edges {{ - node {{ +query latestTag( + $owner: String!, $name: String!, + $query: String, $includeCommitName: Boolean = false, +) { + repository(owner: $owner, name: $name) { + refs( + refPrefix: "refs/tags/", query: $query, + first: 1, orderBy: {field: TAG_COMMIT_DATE, direction: DESC}, + ) { + edges { + node { name - }} - }} - }} - }} -}} + ... @include(if: $includeCommitName) { target { oid } } + } + } + } + } +} ''' -async def get_latest_tag(key: Tuple[str, str, str]) -> str: - repo, query, token = key +async def get_latest_tag(key: Tuple[str, Optional[str], str, bool]) -> str: + repo, query, token, use_commit_name = key owner, reponame = repo.split('/') headers = { 'Authorization': f'bearer {token}', 'Content-Type': 'application/json', } - q = QUERY_LATEST_TAG.format( - owner = owner, - name = reponame, - query = query, - ) + variables = { + 'owner': owner, + 'name': reponame, + 'includeCommitName': use_commit_name, + } + if query is not None: + variables['query'] = query res = await session.post( GITHUB_GRAPHQL_URL, headers = headers, - json = {'query': q}, + json = {'query': QUERY_LATEST_TAG, 'variables': variables}, ) j = res.json() @@ -66,7 +77,10 @@ async def get_latest_tag(key: Tuple[str, str, str]) -> str: if not refs: raise GetVersionError('no tag found') - return refs[0]['node']['name'] + return next(add_commit_name( + ref['node']['name'], + ref['node']['target']['oid'] if use_commit_name else None, + ) for ref in refs) async def get_version_real( name: str, conf: Entry, *, @@ -82,12 +96,13 @@ async def get_version_real( token = keymanager.get_key('github') use_latest_tag = conf.get('use_latest_tag', False) + use_commit_name = conf.get('use_commit_name', False) if use_latest_tag: if not token: raise GetVersionError('token not given but it is required') - query = conf.get('query', '') - return await cache.get((repo, query, token), get_latest_tag) # type: ignore + query = conf.get('query') + return await cache.get((repo, query, token, use_commit_name), get_latest_tag) # type: ignore br = conf.get('branch') path = conf.get('path') @@ -114,7 +129,10 @@ async def get_version_real( data = await cache.get_json(url, headers = headers) if use_max_tag: - tags = [ref['ref'].split('/', 2)[-1] for ref in data] + tags = [add_commit_name( + ref['ref'].split('/', 2)[-1], + ref['object']['sha'] if use_commit_name else None, + ) for ref in data] if not tags: raise GetVersionError('No tag found in upstream repository.') return tags @@ -126,8 +144,11 @@ async def get_version_real( else: # YYYYMMDD.HHMMSS - version = data[0]['commit']['committer']['date'] \ - .rstrip('Z').replace('-', '').replace(':', '').replace('T', '.') + version = add_commit_name( + data[0]['commit']['committer']['date'] \ + .rstrip('Z').replace('-', '').replace(':', '').replace('T', '.'), + data[0]['sha'] if use_commit_name else None, + ) return version diff --git a/tests/test_github.py b/tests/test_github.py index c505c83..2c9b3e6 100644 --- a/tests/test_github.py +++ b/tests/test_github.py @@ -15,6 +15,13 @@ async def test_github(get_version): "github": "harry-sanabria/ReleaseTestRepo", }) == "20140122.012101" +async def test_github_commit_name(get_version): + assert await get_version("example", { + "source": "github", + "github": "harry-sanabria/ReleaseTestRepo", + "use_commit_name": True, + }) == "20140122.012101+2b3cdf6134b07ae6ac77f11b586dc1ae6d1521db" + async def test_github_default_not_master(get_version): assert await get_version("example", { "source": "github", @@ -35,6 +42,14 @@ async def test_github_max_tag(get_version): "use_max_tag": True, }) == "second_release" +async def test_github_max_tag_commit_name(get_version): + assert await get_version("example", { + "source": "github", + "github": "harry-sanabria/ReleaseTestRepo", + "use_max_tag": True, + "use_commit_name": True, + }) == "second_release+0f01b10ee72809e7ec0d903db95bcb6eef18c925" + async def test_github_max_tag_with_ignored(get_version): assert await get_version("example", { "source": "github", @@ -74,3 +89,11 @@ async def test_github_latest_tag(get_version): "use_latest_tag": True, }) == "release3" +async def test_github_latest_tag_commit_name(get_version): + assert await get_version("example", { + "source": "github", + "github": "harry-sanabria/ReleaseTestRepo", + "use_latest_tag": True, + "use_commit_name": True, + }) == "release3+2b3cdf6134b07ae6ac77f11b586dc1ae6d1521db" +