1 Commits
Author SHA1 Message Date
José Arturo García bc7643dba6 Add Tryton 7.0 series
Publicar paquete PyPI / publish (push) Successful in 1m2s
2026-09-30 21:50:52 -04:00
7 changed files with 144 additions and 241 deletions
+39 -174
View File
@@ -1,201 +1,66 @@
name: Publish Package name: Publicar paquete PyPI
on: on:
push: push:
branches: branches: ["7.0"]
- "8.0"
workflow_dispatch: workflow_dispatch:
permissions: permissions:
contents: read contents: read
packages: write
jobs: jobs:
publish: publish:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - 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: env:
TWINE_USERNAME: ${{ secrets.REGISTRY_USER }} GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
TWINE_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
run: | run: |
test -n "$TWINE_USERNAME" || { echo "Missing REGISTRY_USER secret"; exit 1; } test -n "$GITEA_TOKEN" || { echo "Missing GITEA_TOKEN secret"; exit 1; }
test -n "$TWINE_PASSWORD" || { echo "Missing REGISTRY_PASSWORD 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 - name: Verificar Python
if: env.PUBLISH_PACKAGE == 'true' run: python3 --version
env:
TWINE_USERNAME: ${{ secrets.REGISTRY_USER }} - name: Instalar herramientas
TWINE_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
run: | 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 pathlib import Path
from urllib.error import HTTPError import os, re
from urllib.parse import urlencode path = Path('tryton.cfg')
from urllib.request import Request, urlopen text = path.read_text()
import base64 path.write_text(re.sub(r'(?m)^version=.*$',
import json f"version={os.environ['PACKAGE_VERSION']}", text))
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}')
PY PY
- name: Build package - name: Construir y validar
if: env.PUBLISH_PACKAGE == 'true'
run: | run: |
python -m build .ci-venv/bin/python setup.py sdist bdist_wheel
.ci-venv/bin/python -m twine check dist/*
- name: Check package - name: Publicar en Gitea PyPI
if: env.PUBLISH_PACKAGE == 'true'
run: |
python -m twine check dist/*
- name: Publish to Gitea PyPI registry
if: env.PUBLISH_PACKAGE == 'true'
env: env:
TWINE_USERNAME: ${{ secrets.REGISTRY_USER }} TWINE_USERNAME: unpaismejor
TWINE_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} TWINE_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
run: | 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 \ --repository-url https://code.unpaismejor.org.do/api/packages/tryton-do/pypi \
dist/* dist/*
+7
View File
@@ -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 Version 8.0.0 - 2026-08-14
-------------------------- --------------------------
+3 -3
View File
@@ -24,6 +24,6 @@ Install the package and activate the module in the database::
Publishing Publishing
********** **********
The Gitea Actions workflow builds and publishes the package from the ``8.0`` The Gitea Actions workflow builds and publishes the package from version-series
series branch. Configure ``REGISTRY_USER`` and ``REGISTRY_PASSWORD`` as branches such as ``7.0``. Configure ``REGISTRY_PASSWORD`` as a repository
repository secrets. secret.
+10 -3
View File
@@ -1,5 +1,12 @@
from trytond.pool import Pool
from . import invoice from . import invoice
__all__ = [ __all__ = ['register']
'invoice',
]
def register():
Pool.register(
invoice.Invoice,
invoice.InvoiceLine,
module='account_invoice_do', type_='model')
-60
View File
@@ -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']
+84
View File
@@ -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,
)
+1 -1
View File
@@ -1,5 +1,5 @@
[tryton] [tryton]
version=8.0.0 version=7.0.0
depends: depends:
account_do account_do
account_invoice account_invoice