Files

114 lines
7.2 KiB
Markdown

# Gitea release pages
ReleaseKit's public `Deliver` operation and `deliver` command select Gitea
explicitly, verify tagged source, find or create the selected module's release
page, inspect existing assets, and upload missing assets. Shared delivery code
owns content comparison and recovery; the Gitea adapter owns the HTTP protocol.
Page-only and binary-producing profiles use the same delivery path. The
[consumer delivery guide](../consumers/delivery.md) covers caller workflows;
this guide describes the Gitea protocol and its failure behavior.
The caller supplies the Gitea base URL, owner, repository name, and token
explicitly. The client does not read ambient credentials. The token is sent in
the `Authorization: token …` request header and is not included in errors or
results. The base URL must use HTTP or HTTPS and cannot contain URL credentials,
a query, or a fragment.
Before a build or full local artifact verification, Gitea preflight makes two
authenticated, read-only repository requests: `GET /api/v1/repos/{owner}/{repo}`
resolves the repository, then `GET /api/v1/repositories/{id}` confirms access
through the authenticated repository route. The response identity must match
the configured owner and repository. This check also works without a checkout
or a published tag and makes no release or asset writes.
A 401 response identifies a rejected credential. A 403 identifies denied
access but does not establish that the credential is invalid. A 404 can mean a
missing repository or access hidden by the service. Network failures, service
errors, and malformed responses that prevent verification are reported as
unavailable. Repository `permissions.push` describes Git code-push access; it
does not establish permission to create release pages or upload assets. Gitea
does not expose enough evidence through this probe to verify those write scopes,
so they remain unverified and later writes remain authoritative. Preflight does
not request unrelated user scopes or create a test release or asset.
The selected module derives a complete tag from its semantic version, declared
module path, and physical repository-relative directory. For example, version
`v1.2.3` in `integrations/ckconfig` uses
`integrations/ckconfig/v1.2.3`; a root module uses `v1.2.3`. A `/v2` module
stored at `integrations/core/v2` uses `integrations/core/v2.1.0`; the same
declared module stored at `integrations/core` derives the same tag. ReleaseKit
removes a final physical `/v2` directory from the tag prefix only when the
module declaration has that `/v2` suffix. A module-local note stays at
`<module>/docs/releases/<version>.md`, including a physical `/v2` directory.
The default shared validation convention uses repository-root `COPYRIGHT`,
`LICENSE`, and `docs/oss/attribution.md` when present. Consumers own their license
layout and release-note links. Use the complete derived tag as the immutable
source ref and the actual repository-relative file paths in those links.
Shared-file selection controls validation inputs; delivery preserves the
committed note and its footer links. After successful preflight, the Gitea
adapter reads
`/api/v1/repos/{owner}/{repo}/tags/{tag}` and requires the hosted tag's commit
SHA to equal the captured source commit. It then reads
`/api/v1/repos/{owner}/{repo}/releases/tags/{tag}`. A matching existing page
is accepted by shared delivery policy only when it has that tag, the complete
committed release note as its body, and stable
non-draft state. Its existing title is retained. ReleaseKit does not edit a
conflicting page. Slash-containing tags are escaped as one URL path segment.
Page-only delivery does not require a release ID in this lookup response. Asset
operations require a positive release ID; a missing or nonpositive ID is
reported as a malformed service observation before an asset request is made.
If the page is absent, the adapter posts to
`/api/v1/repos/{owner}/{repo}/releases` with the selected full tag and commit,
the release note's H1 as title, the complete note as body, and draft and
prerelease disabled. A create conflict, interrupted response, or malformed
create response (including inconsistent decoded fields) triggers one lookup by
the shared workflow. An exact page found there confirms the existing content;
a differing page is a conflict. If that lookup cannot determine the result
after a possibly successful write, delivery returns an unknown outcome. Other
HTTP failures retain their structured failure kind and status code. A recovered
create retains its write history; a later asset-inspection failure reports
partial page progress.
For a binary-producing release, shared delivery builds a deterministic
`SHA256SUMS` file from the declared artifact names and SHA-256 digests. Entries
are sorted by filename and use the standard two-space separator. A source-only
release produces no manifest.
The adapter lists release attachments and opens their download streams. Shared
delivery compares each declared existing file by SHA-256 digest and reports it
as matching, missing, conflicting, or unverifiable. Same-name attachments with
different bytes are conflicts even when their reported size matches. An existing
`SHA256SUMS` must exactly match the complete expected manifest; a subset or
different manifest is a conflict. An unreadable attachment is unverifiable,
not missing. A failed read-only inspection reports `not_attempted`; if this
invocation already delivered the page, its outcome retains `partial` progress.
Unknown-write outcomes are reserved for unresolved writes. Unrelated attachments are returned separately and are preserved.
Attachment downloads and redirects carry the Gitea authorization token only
when their scheme, host, and port match the configured service. Cross-origin
content is downloaded without that token and verified by its digest. Downloads
follow at most ten redirects; an additional redirect is unverifiable.
Comparison reads no more than the declared local size plus one byte; shorter
or longer content conflicts, regardless of the server's reported size.
Shared delivery uploads only after inspection finds no conflicts or
unverifiable assets. It uploads missing binaries in filename order and then the
missing `SHA256SUMS`, using the adapter's multipart field `attachment` and
filename query parameter `name`. Matching attachments are retained; unrelated and existing
attachments are never overwritten or deleted. Results use disjoint current
observations: matching, uploaded, pending, conflicting, and uncertain. An asset
moved from known missing work to another category is removed from `pending`;
later known-missing assets can remain pending when an earlier upload stops the
operation. Early failures may leave declarations unreported. After an ambiguous
upload or an upload name conflict, the client performs one list and content
check. It does not repeat the POST when the result is missing or cannot be
verified, so a caller can resolve the remote state before retrying.
Uploads use the configured service origin and the same explicit token header.
Source-only page delivery supplies no binary assets and creates no manifest.
The integration tests use a local HTTP fixture and assert request paths,
payloads, authorization, streaming comparisons, manifest handling, and failure
handling. They do not contact a live Gitea service or use real credentials.