Initial server source import
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
SHA256_RE = re.compile(r'^[0-9a-f]{64}$')
|
||||
DOCKER_DIGEST_RE = re.compile(r'^sha256:[0-9a-f]{64}$')
|
||||
DOCKER_TAG_RE = r'[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}'
|
||||
DOCKER_NAME_COMPONENT_RE = r'[a-z0-9]+(?:(?:[._]|__|[-]+)[a-z0-9]+)*'
|
||||
DOCKER_DOMAIN_COMPONENT_RE = r'[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?'
|
||||
DOCKER_PORT_RE = r'[0-9]{1,5}'
|
||||
DOCKER_REGISTRY_RE = (
|
||||
rf'(?:\[[0-9A-Fa-f:.]+\](?::{DOCKER_PORT_RE})?'
|
||||
rf'|(?:localhost|{DOCKER_DOMAIN_COMPONENT_RE}(?:\.{DOCKER_DOMAIN_COMPONENT_RE})*)(?::{DOCKER_PORT_RE})?)'
|
||||
)
|
||||
DOCKER_IMAGE_RE = re.compile(
|
||||
rf'^(?=.{{1,512}}$)(?:{DOCKER_REGISTRY_RE}/)?'
|
||||
rf'{DOCKER_NAME_COMPONENT_RE}(?:/{DOCKER_NAME_COMPONENT_RE})*'
|
||||
rf'(?::{DOCKER_TAG_RE})?(?:@sha256:[0-9a-f]{{64}})?$'
|
||||
)
|
||||
DOCKER_REVISION_RE = re.compile(r'^[A-Za-z0-9:+._-]{1,128}$')
|
||||
DOCKER_TAG_TARGET_SCHEMA = 'docker-tag-v1'
|
||||
DOCKERHUB_REGISTRIES = frozenset((
|
||||
'docker.io', 'index.docker.io', 'registry-1.docker.io',
|
||||
))
|
||||
HUGGINGFACE_SPACE_COMPONENT_RE = re.compile(
|
||||
r'^[A-Za-z0-9_](?:[A-Za-z0-9._-]{0,94}[A-Za-z0-9_])?$'
|
||||
)
|
||||
|
||||
|
||||
def _postman_data(target):
|
||||
if isinstance(target, dict):
|
||||
return dict(target)
|
||||
text = str(target or '').strip()
|
||||
if not text:
|
||||
return {}
|
||||
if text.startswith('{'):
|
||||
value = json.loads(text)
|
||||
if not isinstance(value, dict):
|
||||
raise ValueError('Postman target JSON must be an object')
|
||||
return value
|
||||
if text.lower().startswith('file:'):
|
||||
return {'source': 'local_file', 'local_path': text[5:]}
|
||||
if '://' in text:
|
||||
return {'source': 'url', 'url': text}
|
||||
return {'source': 'local_file', 'local_path': text}
|
||||
|
||||
|
||||
def postman_target_identity(target):
|
||||
"""Return artifact identity independent of discovery-origin metadata."""
|
||||
try:
|
||||
data = _postman_data(target)
|
||||
except (TypeError, ValueError, json.JSONDecodeError):
|
||||
return str(target or '').strip().lower()
|
||||
digest = str(data.get('sha256') or data.get('hash') or data.get('cache_key') or '').strip().lower()
|
||||
if SHA256_RE.fullmatch(digest):
|
||||
return f'postman:sha256:{digest}'
|
||||
cache_path = str(data.get('cache_path') or '')
|
||||
cache_name = os.path.basename(cache_path).lower()
|
||||
cache_digest = cache_name.split('.', 1)[0]
|
||||
if SHA256_RE.fullmatch(cache_digest):
|
||||
return f'postman:sha256:{cache_digest}'
|
||||
source = str(data.get('source') or '').lower()
|
||||
if source == 'github_code':
|
||||
return 'postman:github_code:{}:{}:{}'.format(
|
||||
str(data.get('repo') or '').lower(),
|
||||
str(data.get('path') or '').replace('\\', '/').lower(),
|
||||
str(data.get('sha') or '').lower(),
|
||||
)
|
||||
if source == 'url':
|
||||
return f'postman:url:{str(data.get("url") or "").rstrip("/").lower()}'
|
||||
local_path = data.get('local_path') or data.get('path') or cache_path
|
||||
if local_path:
|
||||
canonical = os.path.normcase(os.path.realpath(os.path.abspath(os.fspath(local_path))))
|
||||
return f'postman:file:{canonical}'
|
||||
stable = {
|
||||
key: value for key, value in data.items()
|
||||
if key not in {'origin', 'cached_at', 'discovered_at', 'size', 'bytes'}
|
||||
}
|
||||
payload = json.dumps(stable, ensure_ascii=True, sort_keys=True, separators=(',', ':'), default=str)
|
||||
return 'postman:metadata:' + hashlib.sha256(payload.encode('utf-8')).hexdigest()
|
||||
|
||||
|
||||
def normalize_docker_digest(value):
|
||||
digest = str(value or '').strip().lower()
|
||||
return digest if DOCKER_DIGEST_RE.fullmatch(digest) else ''
|
||||
|
||||
|
||||
def validate_docker_image_reference(value, require_digest=True):
|
||||
image = str(value or '').strip()
|
||||
if not image or image.startswith('{') or '://' in image or not DOCKER_IMAGE_RE.fullmatch(image):
|
||||
raise ValueError('invalid Docker image reference')
|
||||
digest = image.rsplit('@', 1)[1] if '@' in image else ''
|
||||
if require_digest and not normalize_docker_digest(digest):
|
||||
raise ValueError('Docker image reference must include a sha256 digest')
|
||||
return image
|
||||
|
||||
|
||||
def serialize_docker_tag_target(image, revision):
|
||||
image = validate_docker_image_reference(image)
|
||||
revision = str(revision or '').strip()
|
||||
if not DOCKER_REVISION_RE.fullmatch(revision):
|
||||
raise ValueError('invalid Docker tag revision')
|
||||
return json.dumps(
|
||||
{
|
||||
'image': image,
|
||||
'revision': revision,
|
||||
'schema': DOCKER_TAG_TARGET_SCHEMA,
|
||||
},
|
||||
ensure_ascii=True,
|
||||
sort_keys=True,
|
||||
separators=(',', ':'),
|
||||
)
|
||||
|
||||
|
||||
def parse_docker_target(target):
|
||||
structured = isinstance(target, dict)
|
||||
if structured:
|
||||
data = dict(target)
|
||||
else:
|
||||
text = str(target or '').strip()
|
||||
if not text.startswith('{'):
|
||||
image = validate_docker_image_reference(text)
|
||||
return {'image': image, 'revision': '', 'structured': False, 'target': image}
|
||||
try:
|
||||
data = json.loads(text)
|
||||
except json.JSONDecodeError as exc:
|
||||
raise ValueError('invalid Docker target JSON') from exc
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError('Docker target JSON must be an object')
|
||||
structured = True
|
||||
|
||||
if set(data) != {'schema', 'image', 'revision'} or data.get('schema') != DOCKER_TAG_TARGET_SCHEMA:
|
||||
raise ValueError('unsupported Docker target structure')
|
||||
canonical = serialize_docker_tag_target(data.get('image'), data.get('revision'))
|
||||
return {
|
||||
'image': validate_docker_image_reference(data.get('image')),
|
||||
'revision': str(data.get('revision')).strip(),
|
||||
'structured': structured,
|
||||
'target': canonical,
|
||||
}
|
||||
|
||||
|
||||
def parse_dockerhub_digest_target(target):
|
||||
"""Return canonical parts for a public immutable DockerHub image target."""
|
||||
parsed = parse_docker_target(target)
|
||||
image = parsed['image']
|
||||
if image != image.lower():
|
||||
raise ValueError('DockerHub image reference must be canonical lowercase')
|
||||
image_name, separator, digest = image.rpartition('@')
|
||||
if not separator or not normalize_docker_digest(digest):
|
||||
raise ValueError('DockerHub image reference must include a sha256 digest')
|
||||
repository_with_registry = image_name.rsplit(':', 1)[0] if ':' in image_name.rsplit('/', 1)[-1] else image_name
|
||||
parts = repository_with_registry.split('/')
|
||||
first = parts[0]
|
||||
has_registry = first == 'localhost' or '.' in first or ':' in first or first.startswith('[')
|
||||
if has_registry:
|
||||
registry = first
|
||||
if registry not in DOCKERHUB_REGISTRIES or len(parts) < 2:
|
||||
raise ValueError('Docker direct execution requires a public DockerHub image')
|
||||
repository = '/'.join(parts[1:])
|
||||
else:
|
||||
registry = 'docker.io'
|
||||
repository = repository_with_registry
|
||||
if '/' not in repository:
|
||||
repository = 'library/' + repository
|
||||
return {
|
||||
**parsed,
|
||||
'image': image,
|
||||
'registry': registry,
|
||||
'repository': repository,
|
||||
'manifest_digest': digest,
|
||||
'normalized_target': image,
|
||||
}
|
||||
|
||||
|
||||
def normalize_huggingface_space_id(value):
|
||||
"""Validate and return one canonical case-preserved HuggingFace Space ID."""
|
||||
if not isinstance(value, str) or value != value.strip() or not 3 <= len(value) <= 96:
|
||||
raise ValueError('invalid HuggingFace Space identifier')
|
||||
if value.count('/') != 1 or '\\' in value or '..' in value or '--' in value:
|
||||
raise ValueError('invalid HuggingFace Space identifier')
|
||||
namespace, name = value.split('/', 1)
|
||||
if (
|
||||
HUGGINGFACE_SPACE_COMPONENT_RE.fullmatch(namespace) is None
|
||||
or HUGGINGFACE_SPACE_COMPONENT_RE.fullmatch(name) is None
|
||||
or name.lower().endswith('.git')
|
||||
):
|
||||
raise ValueError('invalid HuggingFace Space identifier')
|
||||
return value
|
||||
|
||||
|
||||
def docker_target_identity(target):
|
||||
try:
|
||||
data = parse_docker_target(target)
|
||||
except (TypeError, ValueError):
|
||||
return str(target or '').strip().lower()
|
||||
return data['image'].lower()
|
||||
Reference in New Issue
Block a user