Files
ss-tools/scripts/full-flow-git.py

67 lines
3.5 KiB
Python

#!/usr/bin/env python3
#region FullFlow.GitBootstrap [C:4] [TYPE Module] [SEMANTICS gitea,fixture,publication,secrets]
# @PURPOSE Create a real local Git publication repository and save its PAT in isolated credentials.
# @PRE Gitea admin exists; the fixture env is mode 0600; localhost:18300 is the isolated Gitea.
# @POST baseline-catalogs repository exists and deployment publication coordinates are saved.
# @INVARIANT Credentials and tokens are never printed; existing credentials are reused unless rotation is explicit.
import base64
import json
import os
import secrets
import time
from pathlib import Path
import sys
import urllib.error
import urllib.request
path = Path(sys.argv[1] if len(sys.argv) > 1 else '/tmp/ss-tools-full-flow.env')
if path.stat().st_mode & 0o077:
raise SystemExit('Fixture env must have mode 0600')
values = dict(line.split('=', 1) for line in path.read_text().splitlines() if '=' in line)
authorization = 'Basic ' + base64.b64encode(f'admin:{values["FIXTURE_PASSWORD"]}'.encode()).decode()
api_base = os.environ.get('GITEA_API_BASE_URL', 'http://127.0.0.1:18300/api/v1').rstrip('/')
# #region FullFlow.GitBootstrap.ApiRequest [C:2] [TYPE Function]
# @BRIEF Call the isolated Gitea API with privately loaded fixture credentials.
# @INVARIANT Credentials and response tokens are not printed.
def call(route, payload=None):
request = urllib.request.Request(api_base + route, data=json.dumps(payload).encode() if payload is not None else None, headers={'Authorization':authorization,'Content-Type':'application/json'})
last_error = None
for attempt in range(20):
try:
with urllib.request.urlopen(request, timeout=10) as response:
return json.load(response)
except urllib.error.HTTPError:
raise
except (OSError, urllib.error.URLError) as error:
last_error = error
if attempt == 19:
break
time.sleep(1)
raise RuntimeError(f'GITEA_API_UNAVAILABLE:{type(last_error).__name__}') from None
# #endregion FullFlow.GitBootstrap.ApiRequest
try:
call('/repos/admin/baseline-catalogs')
except urllib.error.HTTPError as error:
if error.code != 404:
raise
call('/user/repos', {'name':'baseline-catalogs','private':True,'auto_init':True,'default_branch':'main','description':'Isolated actual baseline publication evidence'})
if not values.get('PUBLISHED_CATALOG_GITEA_TOKEN') or '--rotate-token' in sys.argv[2:]:
token = call('/users/admin/tokens', {'name':f'full-flow-publication-{secrets.token_hex(6)}','scopes':['write:repository','write:user']})['sha1']
values.update(PUBLISHED_CATALOG_GITEA_URL='http://gitea:3000', PUBLISHED_CATALOG_GITEA_TOKEN=token, PUBLISHED_CATALOG_REPO='admin/baseline-catalogs', PUBLISHED_CATALOG_REF='main')
temporary = path.with_name(path.name + '.new')
owner_uid = os.environ.get('FULL_FLOW_ENV_FILE_UID')
owner_gid = os.environ.get('FULL_FLOW_ENV_FILE_GID')
if bool(owner_uid) != bool(owner_gid):
raise SystemExit('PRIVATE_ENV_OWNER_INCOMPLETE')
with os.fdopen(os.open(temporary, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600), 'w') as file:
file.write(''.join(f'{key}={value}\n' for key, value in values.items()))
file.flush()
os.fsync(file.fileno())
if owner_uid and owner_gid:
os.fchown(file.fileno(), int(owner_uid), int(owner_gid))
os.replace(temporary, path)
print('Local Git publication repository ready; credentials saved privately. Recreate backend to apply publication coordinates.')
#endregion FullFlow.GitBootstrap