Use semantic CI package versions
This commit is contained in:
+113
-17
@@ -24,23 +24,127 @@ jobs:
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install build twine
|
||||
|
||||
- name: Check publishing credentials
|
||||
env:
|
||||
TWINE_USERNAME: ${{ secrets.REGISTRY_USER }}
|
||||
TWINE_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
run: |
|
||||
test -n "$TWINE_USERNAME" || { echo "Missing REGISTRY_USER secret"; exit 1; }
|
||||
test -n "$TWINE_PASSWORD" || { echo "Missing REGISTRY_PASSWORD secret"; exit 1; }
|
||||
|
||||
- name: Set CI package version
|
||||
env:
|
||||
TWINE_USERNAME: ${{ secrets.REGISTRY_USER }}
|
||||
TWINE_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
run: |
|
||||
python - <<'PY'
|
||||
from pathlib import Path
|
||||
from urllib.error import HTTPError
|
||||
from urllib.parse import urlencode
|
||||
from urllib.request import Request, urlopen
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import tomllib
|
||||
|
||||
path = Path('tryton.cfg')
|
||||
text = path.read_text()
|
||||
match = re.search(r'(?m)^version=(.+)$', text)
|
||||
if not match:
|
||||
OWNER = 'tryton-do'
|
||||
PACKAGE_TYPE = 'pypi'
|
||||
API_ROOT = 'https://gitea.joseagrc.com/api/v1'
|
||||
|
||||
def normalize(name):
|
||||
return re.sub(r'[-_.]+', '-', name).lower()
|
||||
|
||||
cfg_path = Path('tryton.cfg')
|
||||
cfg_text = cfg_path.read_text()
|
||||
cfg_match = re.search(r'(?m)^version=(.+)$', cfg_text)
|
||||
if not cfg_match:
|
||||
raise SystemExit('tryton.cfg has no version entry')
|
||||
base_version = match.group(1).strip()
|
||||
run_number = os.environ.get('GITHUB_RUN_NUMBER', '0')
|
||||
ci_version = f'{base_version}.post{run_number}'
|
||||
text = re.sub(r'(?m)^version=.+$', f'version={ci_version}', text)
|
||||
path.write_text(text)
|
||||
base_version = cfg_match.group(1).strip()
|
||||
version_match = re.fullmatch(r'(\d+)\.(\d+)\.(\d+)', base_version)
|
||||
if not version_match:
|
||||
raise SystemExit(
|
||||
f'tryton.cfg version must be X.Y.Z, got {base_version!r}')
|
||||
major, minor, patch = map(int, version_match.groups())
|
||||
|
||||
pyproject = tomllib.loads(Path('pyproject.toml').read_text())
|
||||
package_name = pyproject['project']['name']
|
||||
normalized_name = normalize(package_name)
|
||||
|
||||
username = os.environ['TWINE_USERNAME']
|
||||
password = os.environ['TWINE_PASSWORD']
|
||||
token = base64.b64encode(
|
||||
f'{username}:{password}'.encode()).decode()
|
||||
|
||||
def request_json(path, params=None, missing_ok=False):
|
||||
url = f'{API_ROOT}{path}'
|
||||
if params:
|
||||
url = f'{url}?{urlencode(params)}'
|
||||
request = Request(url, headers={
|
||||
'Accept': 'application/json',
|
||||
'Authorization': f'Basic {token}',
|
||||
})
|
||||
try:
|
||||
with urlopen(request, timeout=30) as response:
|
||||
return json.load(response)
|
||||
except HTTPError as error:
|
||||
if missing_ok and error.code == 404:
|
||||
return []
|
||||
raise
|
||||
|
||||
def collect_versions(payload, package_specific=False):
|
||||
versions = []
|
||||
if isinstance(payload, list):
|
||||
for item in payload:
|
||||
versions.extend(collect_versions(item, package_specific))
|
||||
elif isinstance(payload, dict):
|
||||
item_name = payload.get('name') or payload.get('package_name')
|
||||
item_type = payload.get('type') or payload.get('package_type')
|
||||
item_version = payload.get('version')
|
||||
if item_version and (
|
||||
package_specific
|
||||
or (item_type == PACKAGE_TYPE
|
||||
and normalize(item_name or '') == normalized_name)):
|
||||
versions.append(str(item_version))
|
||||
for value in payload.values():
|
||||
if isinstance(value, (list, dict)):
|
||||
versions.extend(collect_versions(value, package_specific))
|
||||
return versions
|
||||
|
||||
versions = []
|
||||
for page in range(1, 11):
|
||||
payload = request_json(
|
||||
f'/packages/{OWNER}/{PACKAGE_TYPE}/{package_name}',
|
||||
{'page': page, 'limit': 100},
|
||||
missing_ok=True)
|
||||
page_versions = collect_versions(payload, package_specific=True)
|
||||
versions.extend(page_versions)
|
||||
if not page_versions:
|
||||
break
|
||||
|
||||
for page in range(1, 11):
|
||||
payload = request_json(
|
||||
f'/packages/{OWNER}',
|
||||
{'page': page, 'limit': 100, 'type': PACKAGE_TYPE,
|
||||
'q': package_name})
|
||||
page_versions = collect_versions(payload)
|
||||
versions.extend(page_versions)
|
||||
if not page_versions:
|
||||
break
|
||||
|
||||
patch_pattern = re.compile(rf'^{major}\.{minor}\.(\d+)$')
|
||||
patches = [patch]
|
||||
for version in set(versions):
|
||||
match = patch_pattern.fullmatch(version)
|
||||
if match:
|
||||
patches.append(int(match.group(1)))
|
||||
|
||||
ci_version = f'{major}.{minor}.{max(patches) + 1}'
|
||||
cfg_text = re.sub(
|
||||
r'(?m)^version=.+$', f'version={ci_version}', cfg_text)
|
||||
cfg_path.write_text(cfg_text)
|
||||
print(f'Package name: {package_name}')
|
||||
print(f'Published versions found: {sorted(set(versions))}')
|
||||
print(f'Package version: {ci_version}')
|
||||
PY
|
||||
|
||||
@@ -52,14 +156,6 @@ jobs:
|
||||
run: |
|
||||
python -m twine check dist/*
|
||||
|
||||
- name: Check publishing credentials
|
||||
env:
|
||||
TWINE_USERNAME: ${{ secrets.REGISTRY_USER }}
|
||||
TWINE_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
run: |
|
||||
test -n "$TWINE_USERNAME" || { echo "Missing REGISTRY_USER secret"; exit 1; }
|
||||
test -n "$TWINE_PASSWORD" || { echo "Missing REGISTRY_PASSWORD secret"; exit 1; }
|
||||
|
||||
- name: Publish to Gitea PyPI registry
|
||||
env:
|
||||
TWINE_USERNAME: ${{ secrets.REGISTRY_USER }}
|
||||
|
||||
Reference in New Issue
Block a user