Files
account_do/.gitea/workflows/publish.yml
T
José Arturo García 5595296401 Use non-editable CI install for account_do
Hatchling cannot build editable installs when project sources rewrite a prefix.

Install the package normally with test extras in Gitea Actions and keep the workflow coverage test aligned with that supported install path.
2026-08-09 13:27:38 -04:00

231 lines
7.6 KiB
YAML

name: Test and Publish Package
on:
push:
branches:
- "**"
pull_request:
branches:
- "**"
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install package and test dependencies
run: |
python -m pip install --upgrade pip
python -m pip install '.[test]'
- name: Compile Python sources
run: |
python -m compileall .
- name: Run module tests
run: |
python -m unittest discover -s tests -p 'test*.py'
- name: Build package
run: |
python -m pip install build twine
python -m build
- name: Check package
run: |
python -m twine check dist/*
publish:
runs-on: ubuntu-latest
needs: test
if: github.event_name != 'pull_request'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Detect package branch
run: |
python - <<'PY'
import os
import re
branch = (
os.environ.get('GITHUB_REF_NAME')
or os.environ.get('GITHUB_HEAD_REF')
or '')
publish = bool(re.fullmatch(r'\d+\.\d+', branch))
with open(os.environ['GITHUB_ENV'], 'a') as env:
env.write(f'PUBLISH_PACKAGE={"true" if publish else "false"}\n')
if publish:
env.write(f'PACKAGE_SERIES={branch}\n')
if publish:
print(f'Publishing package series: {branch}')
else:
print(f'Skipping package publish for branch: {branch}')
PY
- name: Set up Python
if: env.PUBLISH_PACKAGE == 'true'
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install build tools
if: env.PUBLISH_PACKAGE == 'true'
run: |
python -m pip install --upgrade pip
python -m pip install build twine
- name: Check publishing credentials
if: env.PUBLISH_PACKAGE == 'true'
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
if: env.PUBLISH_PACKAGE == 'true'
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
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')
series = os.environ['PACKAGE_SERIES']
major, minor = map(int, series.split('.'))
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 = []
for version in set(versions):
match = patch_pattern.fullmatch(version)
if match:
patches.append(int(match.group(1)))
next_patch = max(patches) + 1 if patches else 0
ci_version = f'{major}.{minor}.{next_patch}'
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
- name: Build package
if: env.PUBLISH_PACKAGE == 'true'
run: |
python -m build
- name: Check package
if: env.PUBLISH_PACKAGE == 'true'
run: |
python -m twine check dist/*
- name: Publish to Gitea PyPI registry
if: env.PUBLISH_PACKAGE == 'true'
env:
TWINE_USERNAME: ${{ secrets.REGISTRY_USER }}
TWINE_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
run: |
python -m twine upload \
--repository-url https://gitea.joseagrc.com/api/packages/tryton-do/pypi \
dist/*