Files
account_do/.gitea/workflows/publish.yml
T
José Arturo García fc5cef658f Strengthen account_do tests and publishing checks
Add static coverage for module configuration, XML inventory, tax views, translations, migration metadata, and the Gitea publishing workflow.

Make Gitea Actions run install, compileall, unittest, build, and twine check before publishing packages from series branches.

Fix packaging documentation and RST rendering issues so package validation passes before upload.
2026-08-09 13:24:59 -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 -e '.[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/*