73 lines
3.0 KiB
Python
73 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Resolve stable upstream releases on every build; pin their registry digests."""
|
|
import concurrent.futures
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import re
|
|
import subprocess
|
|
import urllib.request
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
STATE = ROOT / '.local' / 'dev'
|
|
|
|
|
|
def get(url):
|
|
with urllib.request.urlopen(url, timeout=45) as response:
|
|
return json.load(response)
|
|
|
|
|
|
def image(repo, alias):
|
|
url = f'https://hub.docker.com/v2/repositories/{repo}/tags'
|
|
selected = get(f'{url}/{alias}')
|
|
digest = selected['digest']
|
|
if not re.fullmatch(r'sha256:[a-f0-9]{64}', digest):
|
|
raise ValueError('Registry returned an invalid digest')
|
|
# Find the exact numeric tag represented by the maintained stable alias.
|
|
candidates = get(f'{url}?page_size=100')['results']
|
|
names = [x['name'] for x in candidates if x.get('digest') == digest
|
|
and re.fullmatch(r'\d+\.\d+(?:\.\d+)?(?:-alpine)?', x['name'])]
|
|
if not names:
|
|
raise ValueError(f'No stable version tag for {repo}:{alias}')
|
|
tag = max(names, key=lambda x: tuple(map(int, x.split('-')[0].split('.'))))
|
|
return f'{repo}:{tag}@{digest}'
|
|
|
|
|
|
def flutter():
|
|
commit = get('https://api.github.com/repos/flutter/flutter/commits/stable')['sha']
|
|
tags = subprocess.check_output(['git', 'ls-remote', '--tags',
|
|
'https://github.com/flutter/flutter.git'], text=True)
|
|
names = [line.split('refs/tags/')[1] for line in tags.splitlines()
|
|
if line.startswith(commit + '\trefs/tags/')
|
|
and re.fullmatch(r'\d+\.\d+\.\d+', line.split('refs/tags/')[1])]
|
|
if not names:
|
|
raise ValueError('Flutter stable does not point at a stable release tag')
|
|
return max(names, key=lambda x: tuple(map(int, x.split('.')))), commit
|
|
|
|
|
|
def main():
|
|
go = get('https://go.dev/dl/?mode=json')[0]
|
|
if not go['stable'] or not re.fullmatch(r'go\d+\.\d+\.\d+', go['version']):
|
|
raise ValueError('Go release is not stable')
|
|
specs = {'VAULT_IMAGE': ('hashicorp/vault', 'latest'),
|
|
'MONGO_IMAGE': ('library/mongo', 'latest'),
|
|
'NATS_IMAGE': ('library/nats', 'latest'),
|
|
'NGINX_IMAGE': ('library/nginx', 'stable-alpine'),
|
|
'GO_IMAGE': ('library/golang', go['version'][2:] + '-alpine'),
|
|
'UBUNTU_IMAGE': ('library/ubuntu', 'latest')}
|
|
with concurrent.futures.ThreadPoolExecutor() as pool:
|
|
futures = {key: pool.submit(image, *spec) for key, spec in specs.items()}
|
|
refs = {key: future.result() for key, future in futures.items()}
|
|
refs['FLUTTER_VERSION'], refs['FLUTTER_COMMIT'] = flutter()
|
|
STATE.mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
target = STATE / 'images.env'
|
|
temporary = target.with_suffix('.tmp')
|
|
temporary.write_text(''.join(f'{key}={value}\n' for key, value in refs.items()))
|
|
os.replace(temporary, target)
|
|
for key, value in refs.items():
|
|
print(f'{key}={value}')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|