Release-Tags nach Paketbuild direkt veröffentlichen
This commit is contained in:
1 parent
5a1a284505
commit
ccc372778c
19 files changed
+278
-387
No files matched your search
+12
-43
@@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Gitea-Entwurf befüllen; erst nach exakten Paket-/Zielnachweisen freigeben."""
|
||||
"""Taggebundene Vierzielpakete hochladen und direkt auf Gitea veröffentlichen."""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
@@ -37,12 +36,6 @@ class Gitea:
|
||||
with self.open(Request(self.api + path, data=payload, headers=headers, method=method), timeout=120) as response:
|
||||
return json.load(response)
|
||||
|
||||
def download(self, asset):
|
||||
url = asset['browser_download_url']
|
||||
dist.require(urlparse(url).netloc == urlparse(self.base).netloc, 'Fremder Assetserver')
|
||||
with self.open(Request(url, headers={'Authorization': 'token ' + self.token}), timeout=120) as response:
|
||||
return response.read()
|
||||
|
||||
|
||||
def release_version(tag):
|
||||
dist.require(re.fullmatch(r'v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)', tag),
|
||||
@@ -51,10 +44,10 @@ def release_version(tag):
|
||||
|
||||
|
||||
def expected(version):
|
||||
return [n for t in dist.TARGETS for n in (dist.name(version, t), dist.name(version, t) + '.sha256')] + ['acceptance.json']
|
||||
return [n for t in dist.TARGETS for n in (dist.name(version, t), dist.name(version, t) + '.sha256')]
|
||||
|
||||
|
||||
def stage(api, directory, tag, revision):
|
||||
def publish(api, directory, tag, revision):
|
||||
version = release_version(tag)
|
||||
dist.require(re.fullmatch('[0-9a-f]{40}', revision), 'Tag/Revision ungültig')
|
||||
dist.require(api.request('GET', '/tags/' + quote(tag, safe=''))['commit']['sha'] == revision, 'Tag zeigt auf andere Revision')
|
||||
@@ -64,27 +57,28 @@ def stage(api, directory, tag, revision):
|
||||
manifest = dist.verify(path, target, dist.ROOT / 'target/release/tb-template')
|
||||
dist.require(manifest['revision'] == revision and not manifest['source_dirty'], 'Paketrevision falsch/uncommitted')
|
||||
dist.require(path.with_name(path.name + '.sha256').read_text() == f'{dist.digest(path)} {path.name}\n', 'Paketprüfsumme falsch')
|
||||
acceptance = json.loads((directory / 'acceptance.json').read_text())
|
||||
dist.require(acceptance.get('revision') == revision and acceptance.get('passed') is True, 'Grundabnahme fehlt')
|
||||
try:
|
||||
release = api.request('GET', '/releases/tags/' + quote(tag, safe=''))
|
||||
except HTTPError as error:
|
||||
if error.code != 404:
|
||||
raise
|
||||
error.close()
|
||||
release = api.request('POST', '/releases', dict(tag_name=tag, target_commitish=revision,
|
||||
name='Terminal Basic ' + version, draft=True,
|
||||
body='Vierzielpakete im Entwurf. Zielnachweise fehlen noch; keine Freigabe.'))
|
||||
body=f'Vierzielpakete für Revision {revision}. Upload läuft.'))
|
||||
dist.require(release['draft'], 'Bereits veröffentlichten Release nicht verändern')
|
||||
# Wiederholungen dürfen identische Uploads ergänzen, keine vorhandenen Assets ersetzen.
|
||||
# Wiederholungen ergänzen fehlende Uploads; vorhandene Assets werden nicht ersetzt.
|
||||
existing = {a['name']: a for a in release.get('assets', [])}
|
||||
dist.require(set(existing) <= set(names), 'Unerwartete Assets im Entwurf')
|
||||
for name in names:
|
||||
data = (directory / name).read_bytes()
|
||||
if name in existing:
|
||||
dist.require(api.download(existing[name]) == data, f'Abweichendes vorhandenes Asset: {name}')
|
||||
dist.require(existing[name]['size'] == len(data), f'Abweichende Größe des vorhandenen Assets: {name}')
|
||||
else:
|
||||
api.request('POST', f'/releases/{release["id"]}/assets?name=' + quote(name), data)
|
||||
verify_uploads(api, release['id'], directory, names)
|
||||
api.request('PATCH', f'/releases/{release["id"]}', dict(draft=False,
|
||||
body=f'Terminal Basic {version}, Revision {revision}. Vier Zielpakete mit SHA-256-Prüfsummen.'))
|
||||
return release['id']
|
||||
|
||||
|
||||
@@ -93,42 +87,17 @@ def verify_uploads(api, release_id, directory, names):
|
||||
assets = release.get('assets', [])
|
||||
dist.require(len(assets) == len(names) and {a['name'] for a in assets} == set(names), 'Unvollständiger Asset-Upload')
|
||||
for asset in assets:
|
||||
dist.require(hashlib.sha256(api.download(asset)).hexdigest() == dist.digest(directory / asset['name']), 'Uploadprüfsumme falsch')
|
||||
dist.require(asset['size'] == (directory / asset['name']).stat().st_size,
|
||||
f'Uploadgröße falsch: {asset["name"]}')
|
||||
return release
|
||||
|
||||
|
||||
def publish(api, directory, evidence, tag, revision):
|
||||
version = release_version(tag)
|
||||
dist.require(api.request('GET', '/tags/' + quote(tag, safe=''))['commit']['sha'] == revision, 'Tag zeigt auf andere Revision')
|
||||
dist.gate(directory, evidence, revision, version)
|
||||
release = api.request('GET', '/releases/tags/' + quote(tag, safe=''))
|
||||
verify_uploads(api, release['id'], directory, expected(version))
|
||||
dist.require(release['draft'], 'Bereits freigegebenen Release nicht verändern')
|
||||
api.request('PATCH', f'/releases/{release["id"]}', dict(draft=False,
|
||||
body=f'Geprüfte Vierzielpakete für Revision {revision}. Zugehörige Zielnachweise: {os.environ.get("TB_EVIDENCE_REVISION", "lokaler Nachweissatz")}.'))
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('command', choices=['stage', 'download', 'publish'])
|
||||
parser.add_argument('--packages', type=Path, required=True)
|
||||
parser.add_argument('--tag', required=True); parser.add_argument('--revision', required=True)
|
||||
parser.add_argument('--evidence', type=Path)
|
||||
args = parser.parse_args()
|
||||
api = Gitea()
|
||||
if args.command == 'stage': stage(api, args.packages, args.tag, args.revision)
|
||||
elif args.command == 'publish':
|
||||
dist.require(args.evidence, '--evidence erforderlich')
|
||||
publish(api, args.packages, args.evidence, args.tag, args.revision)
|
||||
else:
|
||||
args.packages.mkdir(parents=True) # Nie lokale Kandidaten überschreiben.
|
||||
release = api.request('GET', '/releases/tags/' + quote(args.tag, safe=''))
|
||||
names = expected(release_version(args.tag))
|
||||
dist.require(release['draft'], 'Nur bestehende Entwürfe freigeben')
|
||||
assets = release.get('assets', [])
|
||||
dist.require(len(assets) == len(names) and {a['name'] for a in assets} == set(names), 'Pflichtassets fehlen')
|
||||
for asset in assets:
|
||||
(args.packages / asset['name']).write_bytes(api.download(asset))
|
||||
publish(Gitea(), args.packages, args.tag, args.revision)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Reference in new issue
Block a user