From c15f90b09e4d1dd6d76ecd18a17bd6df6c33a0c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20G=C3=B6ttsch?= Date: Mon, 8 Jun 2026 21:45:27 +0200 Subject: [PATCH] [CI] add wheelhouse: publish addon wheels to Forgejo PyPI registry Build a wheel per changed Odoo addon (whool backend, uv) on push to *.0 branches and publish to the Forgejo PyPI package registry. Versions use whool's native ".N" post-version strategy so each push publishes without a manual manifest bump. Adds README install instructions. Co-Authored-By: Claude Opus 4.8 (1M context) --- .forgejo/workflows/wheelhouse.yml | 100 ++++++++++++++++++++++++++++++ README.md | 29 +++++++++ 2 files changed, 129 insertions(+) create mode 100644 .forgejo/workflows/wheelhouse.yml diff --git a/.forgejo/workflows/wheelhouse.yml b/.forgejo/workflows/wheelhouse.yml new file mode 100644 index 0000000..8051e1e --- /dev/null +++ b/.forgejo/workflows/wheelhouse.yml @@ -0,0 +1,100 @@ +name: wheelhouse + +# Build a wheel for every CHANGED Odoo addon (via the `whool` build backend) +# and publish them to this Forgejo instance's PyPI package registry, so the +# modules can be `pip install`ed into production deployments. +# +# Versions are computed by whool's native post-version strategy: for the 16.0 +# series this is ".N", where N is the number of commits to the addon since its +# manifest version last changed. So a fresh, PEP 440-newer wheel is published +# on every push (e.g. 16.0.2.4.3 -> 16.0.2.4.3.1, .2, ...) without having to +# manually bump __manifest__.py. Bumping the manifest version drops the suffix +# and publishes the clean release (16.0.2.5.0). This requires full git history, +# hence `fetch-depth: 0`. +# +# Install example (read token recommended for private repos): +# uv pip install \ +# --index-url https://:@git.ziemlichoptimal.de/api/packages/odoo/pypi/simple/ \ +# odoo-addon-sale-order-batch + +on: + push: + branches: + # All Odoo series branches: 16.0, 17.0, 18.0, ... + # Deliberately excludes 16.0-ocabot-* / feature branches. + - "*.0" + +jobs: + wheelhouse: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + # Full history is required by whool to count commits since the last + # manifest version change (the ".N" post-version strategy). + fetch-depth: 0 + persist-credentials: false + + - name: Install uv + run: | + curl -LsSf https://astral.sh/uv/install.sh | sh + echo "$HOME/.local/bin" >> "$GITHUB_PATH" + + - name: Determine changed addons + id: changed + run: | + set -euo pipefail + before="${{ github.event.before }}" + # On the first push to a branch (or a force-push), `before` is all + # zeros / unreachable; fall back to the parent commit. + if [ -z "$before" ] || ! git cat-file -e "${before}^{commit}" 2>/dev/null ; then + before="$(git rev-parse HEAD~1 2>/dev/null || true)" + fi + if [ -n "$before" ]; then + changed_files="$(git diff --name-only "$before" HEAD)" + else + changed_files="$(git ls-files)" + fi + # Reduce to top-level addon dirs that actually changed and are addons. + addons="" + for addon in */ ; do + name="${addon%/}" + if [ -f "${addon}__manifest__.py" ] && [ -f "${addon}pyproject.toml" ] \ + && echo "$changed_files" | grep -q "^${name}/" ; then + addons="${addons} ${name}" + fi + done + addons="$(echo "$addons" | xargs || true)" + echo "Changed addons:${addons:- (none)}" + echo "addons=${addons}" >> "$GITHUB_OUTPUT" + + - name: Build wheels for changed addons + if: steps.changed.outputs.addons != '' + run: | + set -euo pipefail + rm -rf dist && mkdir -p dist + for name in ${{ steps.changed.outputs.addons }} ; do + echo "::group::Building ${name}" + # whool reads the manifest version and appends ".N" based on git + # history (16.0 series default), so no manual version bump needed. + uv build --wheel --out-dir dist "${name}" + echo "::endgroup::" + done + echo "Built wheels:" + ls -1 dist + + - name: Publish to Forgejo PyPI registry + if: steps.changed.outputs.addons != '' + env: + # The PyPI registry lives under the repo owner (here: odoo). + TWINE_REPOSITORY_URL: ${{ github.server_url }}/api/packages/${{ github.repository_owner }}/pypi + # Defaults to the pushing user; set WHEELHOUSE_USER if the token below + # belongs to a different account (e.g. a dedicated publish user). + TWINE_USERNAME: ${{ secrets.WHEELHOUSE_USER || github.actor }} + # Forgejo's automatic Actions token can write packages for the + # repo owner. Override with a dedicated PAT in the WHEELHOUSE_TOKEN + # secret if you tighten the default token permissions. + TWINE_PASSWORD: ${{ secrets.WHEELHOUSE_TOKEN || secrets.GITHUB_TOKEN }} + run: | + # --skip-existing keeps re-runs idempotent should a version collide. + uvx twine upload --non-interactive --skip-existing dist/* diff --git a/README.md b/README.md index 51a909f..1a92ed2 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,35 @@ Open Source Odoo Apps i develop and maintain for projects of mine. Should be contributed to the OCA at some point. +## Installation (wheelhouse) + +Every push to a series branch (`16.0`, `17.0`, …) builds a wheel for each +changed addon and publishes it to this Forgejo instance's PyPI package +registry (see [`.forgejo/workflows/wheelhouse.yml`](.forgejo/workflows/wheelhouse.yml)). +Each module is published as `odoo-addon-` and installs straight +into Odoo's addons path. + +Point pip/uv at the registry index (a read token is required for a private +repo): + +```bash +uv pip install \ + --index-url "https://:@git.ziemlichoptimal.de/api/packages/odoo/pypi/simple/" \ + odoo-addon-sale-order-batch +``` + +Or pin it in `requirements.txt` for a production deployment: + +```text +--extra-index-url https://:@git.ziemlichoptimal.de/api/packages/odoo/pypi/simple/ +odoo-addon-sale-order-batch +``` + +Versions follow whool's `.N` post-version scheme: a manifest version of +`16.0.2.4.3` is published as `16.0.2.4.3.`, where `N` increments with each +commit to the addon. Pin an exact version for reproducible deploys, or omit +the version to always get the latest build. +