Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc7643dba6 |
+39
-174
@@ -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/*
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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')
|
||||||
|
|||||||
@@ -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']
|
|
||||||
@@ -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
@@ -1,5 +1,5 @@
|
|||||||
[tryton]
|
[tryton]
|
||||||
version=8.0.0
|
version=7.0.0
|
||||||
depends:
|
depends:
|
||||||
account_do
|
account_do
|
||||||
account_invoice
|
account_invoice
|
||||||
|
|||||||
Reference in New Issue
Block a user