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 --upgrade pip
|
||||||
python -m pip install build twine
|
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
|
- name: Set CI package version
|
||||||
|
env:
|
||||||
|
TWINE_USERNAME: ${{ secrets.REGISTRY_USER }}
|
||||||
|
TWINE_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
run: |
|
run: |
|
||||||
python - <<'PY'
|
python - <<'PY'
|
||||||
from pathlib import Path
|
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 os
|
||||||
import re
|
import re
|
||||||
|
import tomllib
|
||||||
|
|
||||||
path = Path('tryton.cfg')
|
OWNER = 'tryton-do'
|
||||||
text = path.read_text()
|
PACKAGE_TYPE = 'pypi'
|
||||||
match = re.search(r'(?m)^version=(.+)$', text)
|
API_ROOT = 'https://gitea.joseagrc.com/api/v1'
|
||||||
if not match:
|
|
||||||
|
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')
|
raise SystemExit('tryton.cfg has no version entry')
|
||||||
base_version = match.group(1).strip()
|
base_version = cfg_match.group(1).strip()
|
||||||
run_number = os.environ.get('GITHUB_RUN_NUMBER', '0')
|
version_match = re.fullmatch(r'(\d+)\.(\d+)\.(\d+)', base_version)
|
||||||
ci_version = f'{base_version}.post{run_number}'
|
if not version_match:
|
||||||
text = re.sub(r'(?m)^version=.+$', f'version={ci_version}', text)
|
raise SystemExit(
|
||||||
path.write_text(text)
|
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}')
|
print(f'Package version: {ci_version}')
|
||||||
PY
|
PY
|
||||||
|
|
||||||
@@ -52,14 +156,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
python -m twine check dist/*
|
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
|
- name: Publish to Gitea PyPI registry
|
||||||
env:
|
env:
|
||||||
TWINE_USERNAME: ${{ secrets.REGISTRY_USER }}
|
TWINE_USERNAME: ${{ secrets.REGISTRY_USER }}
|
||||||
|
|||||||
Reference in New Issue
Block a user