From bc7643dba694b3c17d94facb2660c85c584090d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=CC=81=20Arturo=20Garci=CC=81a?= Date: Wed, 30 Sep 2026 21:50:52 -0400 Subject: [PATCH] Add Tryton 7.0 series --- .gitea/workflows/publish.yml | 213 +++++++---------------------------- CHANGELOG | 7 ++ README.rst | 6 +- __init__.py | 13 ++- pyproject.toml | 60 ---------- setup.py | 84 ++++++++++++++ tryton.cfg | 2 +- 7 files changed, 144 insertions(+), 241 deletions(-) delete mode 100644 pyproject.toml create mode 100644 setup.py diff --git a/.gitea/workflows/publish.yml b/.gitea/workflows/publish.yml index a3e0327..9974415 100644 --- a/.gitea/workflows/publish.yml +++ b/.gitea/workflows/publish.yml @@ -1,201 +1,66 @@ -name: Publish Package +name: Publicar paquete PyPI on: push: - branches: - - "8.0" + branches: ["7.0"] workflow_dispatch: permissions: contents: read + packages: write jobs: publish: runs-on: ubuntu-latest - steps: - name: Checkout - uses: https://gitea.com/actions/checkout@v4 - with: - token: ${{ secrets.GITEA_TOKEN }} - github-server-url: "http://unpaismejor-gitea-app:3000" - ref: "8.0" - fetch-depth: 0 - - - name: Detect package branch - run: | - python - <<'PY' - import os - import re - - branch = ( - os.environ.get('GITEA_REF_NAME') - or os.environ.get('GITEA_HEAD_REF') - or 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>=1.2' 'twine>=7' 'packaging>=26' - - - name: Check publishing credentials - if: env.PUBLISH_PACKAGE == 'true' env: - TWINE_USERNAME: ${{ secrets.REGISTRY_USER }} - TWINE_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} run: | - test -n "$TWINE_USERNAME" || { echo "Missing REGISTRY_USER secret"; exit 1; } - test -n "$TWINE_PASSWORD" || { echo "Missing REGISTRY_PASSWORD secret"; exit 1; } + test -n "$GITEA_TOKEN" || { echo "Missing GITEA_TOKEN secret"; exit 1; } + git init . + git remote add origin http://unpaismejor-gitea-app:3000/tryton-do/account_invoice_do.git + git -c http.extraHeader="Authorization: token $GITEA_TOKEN" \ + fetch --depth=1 origin 7.0 + git checkout --detach FETCH_HEAD - - name: Set CI package version - if: env.PUBLISH_PACKAGE == 'true' - env: - TWINE_USERNAME: ${{ secrets.REGISTRY_USER }} - TWINE_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} + - name: Verificar Python + run: python3 --version + + - name: Instalar herramientas run: | - python - <<'PY' + python3 -m venv .ci-venv + .ci-venv/bin/python -m pip install --upgrade \ + "setuptools>=68,<81" "wheel>=0.41,<1" "twine>=6,<7" + + - name: Comprobar credenciales + env: + TWINE_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} + run: test -n "$TWINE_PASSWORD" || { echo "Missing REGISTRY_PASSWORD secret"; exit 1; } + + - name: Fijar versión CI + env: + PACKAGE_VERSION: 7.0.${{ gitea.run_number }} + run: | + .ci-venv/bin/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://code.unpaismejor.org.do/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}') + import os, re + path = Path('tryton.cfg') + text = path.read_text() + path.write_text(re.sub(r'(?m)^version=.*$', + f"version={os.environ['PACKAGE_VERSION']}", text)) PY - - name: Build package - if: env.PUBLISH_PACKAGE == 'true' + - name: Construir y validar run: | - python -m build + .ci-venv/bin/python setup.py sdist bdist_wheel + .ci-venv/bin/python -m twine check dist/* - - 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' + - name: Publicar en Gitea PyPI env: - TWINE_USERNAME: ${{ secrets.REGISTRY_USER }} + TWINE_USERNAME: unpaismejor TWINE_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} run: | - python -m twine upload \ + .ci-venv/bin/python -m twine upload --non-interactive \ --repository-url https://code.unpaismejor.org.do/api/packages/tryton-do/pypi \ dist/* diff --git a/CHANGELOG b/CHANGELOG index 70d6270..2be64f1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,10 @@ +Version 7.0.0 - 2026-09-30 +-------------------------- + +* Add a Tryton 7.0 compatible series. +* Restore explicit module registration for Tryton 7.0. +* Publish 7.0 packages to the Gitea PyPI registry. + Version 8.0.0 - 2026-08-14 -------------------------- diff --git a/README.rst b/README.rst index e418161..c9efe71 100644 --- a/README.rst +++ b/README.rst @@ -24,6 +24,6 @@ Install the package and activate the module in the database:: Publishing ********** -The Gitea Actions workflow builds and publishes the package from the ``8.0`` -series branch. Configure ``REGISTRY_USER`` and ``REGISTRY_PASSWORD`` as -repository secrets. +The Gitea Actions workflow builds and publishes the package from version-series +branches such as ``7.0``. Configure ``REGISTRY_PASSWORD`` as a repository +secret. diff --git a/__init__.py b/__init__.py index 8067eb1..908797e 100644 --- a/__init__.py +++ b/__init__.py @@ -1,5 +1,12 @@ +from trytond.pool import Pool + from . import invoice -__all__ = [ - 'invoice', -] +__all__ = ['register'] + + +def register(): + Pool.register( + invoice.Invoice, + invoice.InvoiceLine, + module='account_invoice_do', type_='model') diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 70895f0..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,60 +0,0 @@ -[build-system] -requires = ['hatchling >= 1', 'hatch-tryton'] -build-backend = 'hatchling.build' - -[project] -name = 'unpaismejor_account_invoice_do' -dynamic = ['version', 'dependencies', 'optional-dependencies', 'authors', 'readme'] -requires-python = '>=3.10' -maintainers = [ - {name = "Fundación Un País Mejor", email = "jgarcia@unpaismejor.org.do"}, - ] -description = "Dominican Republic invoices for Tryton" -license = 'GPL-3.0-or-later' -license-files = ["LICENSE", "COPYRIGHT"] -keywords = ["tryton", "accounting", "invoice", "dominican-republic"] -classifiers = [ - "Development Status :: 5 - Production/Stable", - "Environment :: Plugins", - "Framework :: Tryton", - "Topic :: Office/Business", - "Topic :: Office/Business :: Financial :: Accounting", - "Topic :: Software Development :: Libraries :: Python Modules", - ] - -[project.entry-points.'trytond.modules'] -account_invoice_do = 'trytond.modules.account_invoice_do' - -[project.urls] -homepage = "https://www.unpaismejor.org.do/" -repository = "https://code.unpaismejor.org.do/tryton-do/account_invoice_do" - -[tool.hatch.build] -include = [ - 'CHANGELOG', - 'COPYRIGHT', - 'LICENSE', - 'README.rst', - '**/tryton.cfg', - '**/*.py', - '**/*.xml', - 'view/**/*.xml', - 'locale/**/*.po', - 'doc/**/*.rst', - '**/*.fodt', - 'icons/**/*.svg', - 'tests/**/*.py', - 'tests/**/*.rst', - 'tests/**/*.json', - ] - -[tool.hatch.build.targets.wheel.sources] -"" = "trytond/modules/account_invoice_do" - -[tool.hatch.metadata.hooks.tryton] -copyright = 'COPYRIGHT' -readme = 'README.rst' -prefixes = {account_do = 'unpaismejor'} - -[tool.hatch.metadata.hooks.tryton.tryton-optional-dependencies] -test = ['proteus'] diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..fa51094 --- /dev/null +++ b/setup.py @@ -0,0 +1,84 @@ +from configparser import ConfigParser +from pathlib import Path + +from setuptools import setup + + +MODULE = 'account_invoice_do' +PREFIX = 'trytond.modules' +PACKAGE = f'{PREFIX}.{MODULE}' +MODULE2PREFIX = { + 'account_do': 'unpaismejor', +} + + +def read(path): + return Path(path).read_text(encoding='utf-8') + + +config = ConfigParser() +config.read('tryton.cfg') +version = config.get('tryton', 'version') +depends = [ + dep.strip() + for dep in config.get('tryton', 'depends', fallback='').splitlines() + if dep.strip() +] +major, minor, _ = version.split('.', 2) +series = f'{major}.{minor}' +next_series = f'{major}.{int(minor) + 1}' + + +def require(name): + prefix = MODULE2PREFIX.get(name, 'trytond') + return f'{prefix}_{name} >= {series}, < {next_series}' + + +setup( + name='unpaismejor_account_invoice_do', + version=version, + description='Dominican Republic invoices for Tryton', + long_description=read('README.rst'), + long_description_content_type='text/x-rst', + author='Fundación Un País Mejor', + maintainer='Fundación Un País Mejor', + maintainer_email='jgarcia@unpaismejor.org.do', + url='https://code.unpaismejor.org.do/tryton-do/account_invoice_do', + keywords='tryton accounting invoice dominican-republic', + license='GPL-3.0-or-later', + python_requires='>=3.8', + packages=[PACKAGE], + package_dir={PACKAGE: '.'}, + package_data={ + PACKAGE: [ + 'tryton.cfg', + '*.xml', + 'view/*.xml', + 'locale/*.po', + 'doc/*.rst', + ], + }, + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Environment :: Plugins', + 'Framework :: Tryton', + 'Topic :: Office/Business', + 'Topic :: Office/Business :: Financial :: Accounting', + 'Topic :: Software Development :: Libraries :: Python Modules', + ], + install_requires=[ + *(require(dep) for dep in depends if dep not in {'ir', 'res'}), + f'trytond >= {series}, < {next_series}', + ], + extras_require={ + 'test': [ + f'proteus >= {series}, < {next_series}', + ], + }, + entry_points={ + 'trytond.modules': [ + f'{MODULE} = {PACKAGE}', + ], + }, + zip_safe=False, +) diff --git a/tryton.cfg b/tryton.cfg index 7b2205f..ccab29d 100644 --- a/tryton.cfg +++ b/tryton.cfg @@ -1,5 +1,5 @@ [tryton] -version=8.0.0 +version=7.0.0 depends: account_do account_invoice