# Package Registry Releases GovOPlaN publishes reusable module artifacts through Gitea's native PyPI and npm registries. These packages improve developer installation, release resolution, cacheability, and artifact inspection. They do not replace the signed runtime distribution: the signed manifest and digest-pinned OCI images remain the production deployment authority. ## Publication boundary Every repository with a `pyproject.toml` contains `.gitea/workflows/module-package-release.yml`. The meta repository owns the canonical template and installs it with: ```bash python tools/repo/sync-module-package-workflows.py --write python tools/repo/sync-module-package-workflows.py --check ``` The workflow runs for `v*` tags and may be dispatched manually for an existing tag. The organization preflight verifies that every package repository protects the `v*` namespace. Before building, the workflow itself verifies that: - the tagged commit is contained in `main`; - the tag, Python project version, and optional WebUI package version agree; - package names remain in the `govoplan-*` and `@govoplan/*-webui` namespaces. The workflow binds the repository explicitly from the Gitea Actions context. Do not rely on GitHub-compatible environment variables being injected by the runner image; Gitea runners may expose only the context values. Gitea 1.24 job tokens cannot read repository tag-protection settings, so package jobs must not receive a broad administrator token merely to repeat the organization preflight. Run the following before the first publication and after repository or tag-rule changes: ```bash python tools/gitea/gitea-configure-package-releases.py ``` Preview and dispatch the exact wheel/WebUI versions selected by the developer meta-package with: ```bash python tools/gitea/gitea-dispatch-package-set.py \ --env-file ~/.config/gitea/gitea.env python tools/gitea/gitea-dispatch-package-set.py \ --env-file ~/.config/gitea/gitea.env \ --apply ``` The dispatcher reads exact versions from `packages/govoplan-meta/pyproject.toml`, inspects the selected tag to determine whether a WebUI package is expected, skips complete registry pairs and does not duplicate an active workflow. Use `--repository govoplan-core` for a bounded dispatch or `--verify-existing` to rebuild and hash-verify versions already present in both registries. For coordinated lockstep tags, `push-release-tag.sh` pushes module tags first, Core next, and the meta tag last. This is a dependency guarantee for a single-capacity Actions runner: the developer package cannot run before its exact Core and module versions have entered the queue. The same release entry point first validates the migration graph, then records the reviewed current Alembic heads under the target release version and reruns the strict migration audit before it changes package versions, commits, or tags. The default preflight intentionally does not require those heads to exist in the previous release baseline. A failed candidate-baseline check therefore cannot produce a protected package release. The source gate validates `pyproject.toml`, the module version declaration (`MODULE_VERSION` or the top-level `ModuleManifest.version`), public package `__version__`, and WebUI metadata before creating tags. Release-tag artifact checks run only after the candidate tags and immutable WebUI lock have been created locally. It builds one wheel and, where applicable, one npm tarball. The workflow records the source tag, source commit, filename, size, and SHA-256 in `package-artifacts.json` before publishing. Gitea rejects a second upload of the same package version, so correction requires a new version rather than artifact replacement. A retry after partial publication is safe. Before upload, the workflow reads the native package registry file record and compares its SHA-256 with the artifact rebuilt from the protected tag. An exact existing artifact is skipped; a same-version artifact with another digest or an unexpected file set fails closed. This permits a failed npm publication to resume without weakening package immutability or accepting `--skip-existing` blindly. The npm tarball is always published through an explicit local `./dist/...` path. Without that prefix, npm may interpret a relative tarball name as a Git package shorthand before it ever contacts the configured registry. Published WebUI packages contain registry-compatible dependencies only. The workflow converts an internal dependency pinned to a protected `vX.Y.Z` Git tag into the exact `X.Y.Z` registry version and rejects unresolved `file:` or Git dependencies. Repository development metadata may therefore keep local or Git references without leaking them into the published package contract. Historical `add-ideas` and current `GovOPlaN` organization URLs are accepted for immutable tagged releases; both normalize to the same exact registry dependency and no branch or unversioned Git reference is accepted. ## One-time Gitea setup Protect `v*` tags in every package repository and the meta repository. Allow only the `Owners` team to create or delete those tags. ```bash set -a . ~/.config/gitea/gitea.env set +a python tools/gitea/gitea-configure-package-releases.py --apply ``` Create a dedicated personal access token with only `write:package` scope and store these organization-level Actions secrets on `GovOPlaN`: - `GOVOPLAN_PACKAGE_USERNAME`: account owning the package token; - `GOVOPLAN_PACKAGE_TOKEN`: dedicated package-write token. Do not use an administrator or general release token. Gitea 1.24 does not grant package publication to the automatic Actions job token. Organization secrets allow the same least-privilege credential to serve every module workflow. ## Exact release consumption `tools/release/generate-release-package-set.py` translates the reviewed Git source refs in `requirements-release.txt` into an exact registry package set. It resolves each version tag to its commit and verifies the package metadata in that tag. `tools/release/resolve-package-artifacts.py` then downloads exactly those wheel and WebUI versions from Gitea. It reads the identity embedded in every wheel and npm tarball, rejects missing, duplicate, unexpected, or oversized artifacts, and writes `package-artifacts.lock.json` with SHA-256 values and npm integrity values. Credentials are accepted only through environment variables and are never written to the lock. Python resolution ignores ambient pip configuration and extra indexes for GovOPlaN roots, preventing an internal package name from being selected from an undeclared registry. The runtime distribution workflow uses the verified wheelhouse directly and installs module WebUI tarballs only after matching them to the lock. It publishes the package set, package lock, and hash-locked requirements as release assets. The package-lock SHA-256 is part of the signed distribution manifest. Runtime finalization also requires the lock's package versions and hashes to match the wheel composition embedded in the images. OCI assembly remains network-free after package and third-party dependency resolution. The source refs remain in the module catalog for source provenance and release planning. Production installation consumes the signed runtime images rather than invoking `pip`, `npm`, or Git on the target host. ## Developer meta-package `packages/govoplan-meta` builds the optional `govoplan` package. Its default dependencies mirror the reviewed runtime roots; `govoplan[full]` adds all currently packageable workspace modules. Regenerate it after changing release requirements or package versions: ```bash python tools/release/generate-developer-meta-package.py python tools/release/generate-developer-meta-package.py --check ``` `push-release-tag.sh` performs this synchronization before release commits and tags. The meta-package is for editable/developer setup and composition tests. It does not enable modules, apply migrations, provision services, or establish backup and recovery evidence. Generic Packages are intentionally not used. Add that transport only when a consumer needs an artifact format unsupported by PyPI, npm, Gitea Releases, or the OCI registry.