Plan the internal forge delivery boundary
This commit is contained in:
202
docs/roadmap/forge-boundary.md
Normal file
202
docs/roadmap/forge-boundary.md
Normal file
@@ -0,0 +1,202 @@
|
||||
# Roadmap: separate shared delivery from forge adapters
|
||||
|
||||
Status: proposed; implementation has not started.
|
||||
|
||||
## Objective and scope
|
||||
|
||||
Introduce an internal boundary between shared hosted-release delivery and the
|
||||
Gitea implementation. The result should support adding another forge through
|
||||
an adapter while reusing ReleaseKit's release and asset-delivery policy.
|
||||
|
||||
ReleaseKit's `Publish` operation validates and publishes source through Git.
|
||||
Its `Deliver` operation verifies tagged source and delivers release notes and
|
||||
declared assets to a hosting service. This refactor concerns the latter operation
|
||||
and its composition through the existing public facade and CLI.
|
||||
|
||||
Gitea is the concrete backend for this work. Forgejo, GitHub, GitLab, and other
|
||||
backends remain future implementation work. The boundary should express the
|
||||
operations used by the current delivery workflow and allow refinement when a
|
||||
second backend supplies concrete requirements.
|
||||
|
||||
## Current baseline
|
||||
|
||||
The source baseline is `ac78e7d`, following the published v0.5.0 module migration
|
||||
and the subsequent token-file and forbidden-file changes.
|
||||
|
||||
- [delivery.go](../../delivery.go) exposes `GiteaDestination` through
|
||||
`DeliveryRequest.Destination` and converts it into `gitea.Destination`.
|
||||
- [internal/delivery](../../internal/delivery/delivery.go) resolves source
|
||||
identity, checks tagged source, reads notes, coordinates builds, and directly
|
||||
calls `gitea.Client` for release and asset operations. Its result conversion
|
||||
and failure classification use Gitea-owned internal types.
|
||||
- [release.go](../../internal/gitea/release.go) combines Gitea HTTP operations
|
||||
with hosted-tag verification, matching-page acceptance, release creation,
|
||||
and reconciliation of ambiguous creates.
|
||||
- [assets.go](../../internal/gitea/assets.go) and
|
||||
[upload.go](../../internal/gitea/upload.go) combine HTTP operations with
|
||||
manifests, local-file verification, remote-content comparison, asset
|
||||
observations, upload sequencing, and retry bookkeeping.
|
||||
- CLI credential resolution supplies an explicit token from the configured
|
||||
environment variable or token file. The profile describes a Gitea repository;
|
||||
it has no forge selector.
|
||||
|
||||
The [Gitea integration guide](../integrations/gitea.md) and public Go declarations
|
||||
document the baseline contracts. Current behavioral tests provide regression
|
||||
coverage to assess against those contracts. The
|
||||
[architecture policy](../policy/architecture.md) governs facade ownership and
|
||||
dependency direction.
|
||||
|
||||
## Targeted end state
|
||||
|
||||
### Shared delivery responsibilities
|
||||
|
||||
Shared internal code owns:
|
||||
|
||||
- Captured source identity, local tag/commit checks, committed release notes,
|
||||
managed or prebuilt artifact coordination, and local artifact verification.
|
||||
- The existing logical rules for accepting matching hosted content, reporting
|
||||
conflicts, creating missing content, and reconciling uncertain writes.
|
||||
- Manifest generation, content comparison, and asset observations used to decide
|
||||
which declared assets require delivery.
|
||||
- Aggregation into the existing public delivery outcomes and asset categories.
|
||||
|
||||
These operations consume backend-neutral internal values and operations. The
|
||||
generic delivery workflow has no dependency on `internal/gitea`. The facade or
|
||||
private composition code constructs the concrete backend and supplies it to
|
||||
shared delivery.
|
||||
|
||||
### Backend responsibilities
|
||||
|
||||
The Gitea adapter owns destination validation specific to Gitea, API routes,
|
||||
authentication headers, request and response formats, pagination, multipart
|
||||
uploads, attachment downloads, and HTTP-specific error interpretation.
|
||||
|
||||
The internal contract supplies the logical operations needed to inspect a
|
||||
hosted tag, find or create a release, enumerate assets, read asset content, and
|
||||
upload an asset. Release and asset references carry backend-owned handles;
|
||||
shared orchestration works with logical identity and content rather than
|
||||
interpreting Gitea's positive integer IDs.
|
||||
|
||||
The contract also carries the evidence needed for existing recovery behavior:
|
||||
whether a write was attempted, whether its outcome is established or uncertain,
|
||||
and actionable failure information. Generic code interprets delivery outcomes;
|
||||
the adapter interprets protocol responses. A logical backend operation may
|
||||
involve multiple HTTP requests.
|
||||
|
||||
The operations and values stay internal under the existing facade policy.
|
||||
Current Gitea behavior is the concrete compatibility target. This work does
|
||||
not establish compatibility guarantees for unimplemented forges or a public
|
||||
plugin protocol.
|
||||
|
||||
## Work required
|
||||
|
||||
### 1. Define the internal seam and dependency direction
|
||||
|
||||
- Inventory the data and operations currently crossing from `internal/delivery`
|
||||
into `internal/gitea`, including asset identities, inspections, failure
|
||||
categories, release references, and write-attempt evidence.
|
||||
- Introduce a small internal contract shared by orchestration and adapters.
|
||||
Choose its package placement to keep dependencies acyclic. Exact private
|
||||
package and method names can follow the implementation's responsibilities.
|
||||
- Represent the observed release data needed by current comparisons without
|
||||
making Gitea wire structures part of the generic model. Keep numeric-ID and
|
||||
repository-path-segment validation with the Gitea implementation.
|
||||
- Put concrete backend construction in composition code. Apply the public
|
||||
destination choice recorded under Open Decisions.
|
||||
|
||||
### 2. Extract shared policy and artifact mechanics
|
||||
|
||||
- Move manifest generation and local content verification to shared internal
|
||||
ownership, reusing the existing artifact package where appropriate.
|
||||
- Move matching-page decisions, remote-content comparisons, missing-asset
|
||||
selection, upload sequencing, reconciliation decisions, and result
|
||||
aggregation out of the Gitea HTTP implementation.
|
||||
- Preserve the current Gitea behavior for complete note bodies, existing page
|
||||
titles, stable release state, content conflicts, unrelated attachments,
|
||||
source-only delivery, and managed or prebuilt binary delivery.
|
||||
- Preserve current write-history and uncertainty handling. In particular, a
|
||||
read-only inspection failure and an unresolved write retain their distinct
|
||||
meanings. Keep existing Gitea reconciliation behavior where the integration
|
||||
contract specifies it, including the current follow-up lookup behavior.
|
||||
- Retain the existing reuse of local verification and streamed content hashing
|
||||
as responsibilities move. Preserve cancellation, stream closure, temporary
|
||||
output ownership, and cleanup around uploads.
|
||||
|
||||
### 3. Adapt Gitea and reconnect the facade and CLI
|
||||
|
||||
- Implement the internal contract with the Gitea client. Keep Gitea JSON
|
||||
decoding, HTTP status interpretation, URL escaping, credential forwarding,
|
||||
redirect behavior, and upload transport in the adapter.
|
||||
- Map protocol errors into shared outcomes while retaining useful causes and
|
||||
Gitea HTTP details. Preserve documented diagnostics and cancellation behavior;
|
||||
use existing contracts to decide which wording or interactions are meaningful.
|
||||
- Reconnect `Deliver`, CLI `deliver` and `ci-deliver`, managed builds, and
|
||||
prebuilt delivery through the same shared workflow and concrete Gitea adapter.
|
||||
- Continue resolving credentials in the CLI and passing them explicitly. Token
|
||||
environment/file selection and forbidden-file validation retain their
|
||||
existing responsibilities.
|
||||
|
||||
### 4. Align tests and documentation with ownership
|
||||
|
||||
- Retain Gitea HTTP fixture tests for routes, payloads, authentication,
|
||||
pagination, escaping, redirects, malformed responses, and transport failures.
|
||||
- Move tests of manifests, content comparison, conflict handling, and result
|
||||
aggregation to the shared layer as those responsibilities move. Use small
|
||||
controlled backends where protocol-independent recovery needs an external
|
||||
boundary; reuse existing behavioral cases and consolidate redundant coverage.
|
||||
- Retain representative public-facade and CLI workflows proving that Gitea
|
||||
page-only delivery, binary delivery, tagged retries, and failure reporting
|
||||
continue to work through the assembled boundary.
|
||||
- Update the internal inventory and delivery documentation when implementation
|
||||
lands. Keep Gitea protocol details in the integration guide and shared
|
||||
orchestration details in internal documentation. Update public GoDoc,
|
||||
configuration, examples, and migration guidance if the destination API changes.
|
||||
- Record the accepted architectural boundary in an ADR when its design is
|
||||
settled, following the [documentation policy](../policy/documentation.md#architecture-decision-records).
|
||||
|
||||
## Verification and completion
|
||||
|
||||
Follow the [testing policy](../policy/testing.md) and reuse existing tests where
|
||||
they protect the relevant behavior. Run focused checks while moving
|
||||
responsibilities, then the complete offline suite and compiled
|
||||
API/dependency-boundary checks from the [development guide](../development.md#validation).
|
||||
The final full-suite run also covers the affected executable workflows.
|
||||
|
||||
Review the resulting imports and data flow as well as test results: generic
|
||||
orchestration and artifact policy should consume the internal contract, with
|
||||
Gitea transport selected through composition. Exercise an internal test backend
|
||||
at that seam to verify that shared logic can operate independently of the Gitea
|
||||
client. Keep the test collaborator proportionate to the cases it protects.
|
||||
|
||||
Completion means the shared workflow and asset policy have independent internal
|
||||
ownership, the Gitea adapter implements the boundary, the existing Gitea
|
||||
contracts pass through that boundary, and documentation describes the resulting
|
||||
structure. The public API/configuration choice is reflected consistently in
|
||||
declarations, profiles, examples, and applicable migration guidance.
|
||||
|
||||
This is an architectural refactor of the implemented Gitea delivery path.
|
||||
Support claims and compatibility testing for additional forges belong to the
|
||||
work that implements those backends. Release preparation and publication
|
||||
continue under the [existing procedure](../release.md).
|
||||
|
||||
## Open Decisions
|
||||
|
||||
### Public destination shape during this refactor
|
||||
|
||||
**Recommendation:** Retain `GiteaDestination` and the existing profile shape
|
||||
for this pass, with facade/private composition adapting them into the internal
|
||||
backend. The public type accurately describes the implemented destination, and
|
||||
the internal seam can be introduced without a consumer migration. A second
|
||||
backend will provide concrete evidence for repository addressing, API base
|
||||
URLs, and any provider selection fields. The tradeoff is that the public API
|
||||
continues to name Gitea until that follow-up API design is implemented.
|
||||
|
||||
**Alternative:** Introduce a facade-owned neutral destination type, such as
|
||||
`DeliveryDestination`, and an explicit forge selection now, initially dispatching
|
||||
to Gitea. This makes the consumer-facing shape match the intended multi-forge
|
||||
direction and allows its migration to happen alongside the internal refactor.
|
||||
It also adds public/configuration choices before a second backend establishes
|
||||
their requirements; GitLab project addressing and GitHub API/upload endpoints
|
||||
may still require later evolution. If selected, define the initial Gitea
|
||||
selection behavior explicitly and supply migration guidance in the next minor
|
||||
release, consistent with the project's direct-cutover approach.
|
||||
Reference in New Issue
Block a user