190 lines
9.2 KiB
Markdown
190 lines
9.2 KiB
Markdown
# 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.
|
|
|
|
Release-lock regeneration resolves a fresh immutable lock from the reviewed
|
|
candidate manifests; it does not seed resolution from the previous release
|
|
lock. This prevents removed transitive packages and stale peer metadata from
|
|
blocking or contaminating the new release. Candidate resolution also uses an
|
|
isolated temporary npm cache, so a locally replaced tag cannot reuse metadata
|
|
from a failed, unpushed release attempt.
|
|
|
|
Modules that retain the same WebUI package identity in both a root publish
|
|
manifest and `webui/package.json` use the WebUI manifest as the canonical peer
|
|
contract. The coordinated release synchronizes `peerDependencies` and
|
|
`peerDependenciesMeta` into the publish manifest before creating the module
|
|
tag, then synchronizes each lockfile root from the final package metadata. A
|
|
distinct root package remains independent.
|
|
|
|
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.
|
|
|
|
If the tag-triggered developer meta-package job fails before publication, rerun
|
|
`publish-developer-meta-package.yml` with the existing protected version. The
|
|
manual path validates that tag against `main`, checks out its exact commit, and
|
|
publishes only when the registry does not already contain the same wheel hash.
|
|
|
|
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.
|