Skip to content

Commit

Permalink
Add support for build metadata in version string
Browse files Browse the repository at this point in the history
To be compatible with SemVer, we should be able to handle versions which
include optional build metadata
([see](https://semver.org/#spec-item-10)). In general, this is already
the case. However, (some) OCI registries don't allow `+` in their tags,
which is used as separator for these build metadata. That's why, the
version has to be sanitised prior to uploading to a registry or after
retrieving it from there.
  • Loading branch information
8R0WNI3 committed Jul 1, 2024
1 parent 184f1f9 commit 232dd6b
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 2 deletions.
24 changes: 22 additions & 2 deletions oci/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,10 @@ class OciRoutes:
def __init__(
self,
base_api_url_lookup: typing.Callable[[str], str]=base_api_url,
sanitise_tags: bool=False,
):
self.base_api_url_lookup = base_api_url_lookup
self.sanitise_tags = sanitise_tags

def artifact_base_url(
self,
Expand Down Expand Up @@ -226,6 +228,9 @@ def manifest_url(self, image_reference: typing.Union[str, om.OciImageReference])
if not (tag := image_reference.tag):
raise ValueError(f'{image_reference=} does not seem to contain a tag')

if self.sanitise_tags:
tag = oci.util.sanitise_tag(tag=tag)

return urljoin(
self.artifact_base_url(image_reference=image_reference),
'manifests',
Expand All @@ -251,15 +256,22 @@ def __init__(
disable_tls_validation=False,
timeout_seconds: int=None,
session: requests.Session=None,
sanitise_tags: bool=False,
):
self.credentials_lookup = credentials_lookup
self.token_cache = OauthTokenCache()
if not session:
self.session = requests.Session()
else:
self.session = session
self.routes = routes
if routes:
self.routes = routes
else:
self.routes = OciRoutes(
sanitise_tags=sanitise_tags,
)
self.disable_tls_validation = disable_tls_validation
self.sanitise_tags = sanitise_tags

if timeout_seconds:
timeout_seconds = int(timeout_seconds)
Expand Down Expand Up @@ -701,7 +713,15 @@ def tags(self, image_reference: str):
method='GET'
)

return res.json()['tags']
tags = res.json()['tags']

if self.sanitise_tags:
tags = [
oci.util.desanitise_tag(tag=tag)
for tag in tags
]

return tags

def has_multiarch(self, image_reference: str) -> bool:
res = self.head_manifest(
Expand Down
23 changes: 23 additions & 0 deletions oci/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import threading
import typing

META_SEPARATOR = '.build-'


def normalise_image_reference(image_reference: str):
if not isinstance(image_reference, str):
Expand Down Expand Up @@ -42,6 +44,27 @@ def urljoin(*parts):
return '/'.join([first] + middle + [last])


def sanitise_tag(tag: str) -> str:
'''
Additional build metadata as defined in SemVer can be added via `+` to the version. However,
OCI registries don't support `+` as tag character, which is why it has to be sanitised, for
example using `META_SEPARATOR`.
'''
sanitised_tag = tag.replace('+', META_SEPARATOR)

return sanitised_tag


def desanitise_tag(tag: str) -> str:
'''
This function reverts the sanitisation of the `sanitise_tag` function, which allows processing
the tag the same way as prior to uploading to the OCI registry using `sanitise_tag`.
'''
desanitised_tag = tag.replace(META_SEPARATOR, '+')

return desanitised_tag


class _TeeFilelikeProxy:
'''
Takes a filelike object (which may be a non-seekable stream) and patches its
Expand Down

0 comments on commit 232dd6b

Please sign in to comment.