Documentation
¶
Overview ¶
Package release implements VersionGate's Release domain concept: a published version of an Application for its platform (specs/domain/release.md). VersionGate does not distribute binaries — a Release is metadata only. Like internal/application, this package has no dependency on any specific storage technology.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrAlreadyExists = errors.New("release: version and build number already published for this application")
ErrAlreadyExists is returned by Create when the (Application, version, build number) triple already has a Release — this is the same invariant enforced by the database's unique constraint (specs/domain/release.md's Duplicate releases). It does not by itself distinguish an idempotent retry from a genuine conflict; that resolution is Publish's job (see publish.go).
var ErrApplicationInactive = errors.New("release: application is not active")
ErrApplicationInactive is returned by Publish when the target Application exists but is deactivated. Per specs/protocols/release-publishing.md, an unknown or inactive Application is a validation failure, not a not-found — distinct from how a direct Application lookup (specs/domain/application.md, #27) reports a missing Application.
var ErrApplicationNotFound = errors.New("release: application not found")
ErrApplicationNotFound is returned by Create when applicationID does not reference an existing Application. A Release cannot exist without an owning Application.
var ErrConflict = errors.New("release: version and build number already published with different metadata")
ErrConflict is returned by Publish when the (version, build number) pair already has a Release under this Application, but with different metadata (a different policy) than the request — a genuine conflict, distinct from an idempotent retry (specs/protocols/release-publishing.md's Duplicate publishing).
var ErrInvalidBuildNumber = errors.New("release: build number must be non-negative")
ErrInvalidBuildNumber is returned by Publish when buildNumber is negative.
var ErrInvalidPolicy = errors.New("release: policy must be optional or required")
ErrInvalidPolicy is returned by Publish when policy is not one of the known Policy values.
var ErrNotFound = errors.New("release: not found")
ErrNotFound is returned by GetByVersion when no Release matches the requested (Application, version, build number).
Functions ¶
This section is empty.
Types ¶
type ID ¶
type ID string
ID identifies a Release. Like application.ID, it is assigned by a Repository at creation time.
type Policy ¶
type Policy string
Policy is the update behavior a Release implies for clients relative to their reported version — optional (notify) or mandatory (require update). This is a closed set, not a free-form string.
type Release ¶
type Release struct {
ID ID
ApplicationID application.ID
Version version.Version
BuildNumber int
Policy Policy
CreatedAt time.Time
}
Release is a published version of an Application. Publishing is a single, atomic act — there is no mutable "in progress" state (specs/domain/release.md's Release lifecycle). Once created, a Release's Version, BuildNumber, and Policy are immutable: this package exposes no way to change them after Create. Revocation (specs/domain/release.md's Revocation section) is explicitly out of scope for this package — see #28's issue scope.
func Publish ¶
func Publish( ctx context.Context, releases Repository, applications application.Repository, projectID project.ID, applicationID application.ID, v version.Version, buildNumber int, policy Policy, ) (Release, bool, error)
Publish implements the idempotent publish decision from specs/protocols/release-publishing.md:
- Validate the request (build number, policy, and — via applications — that the target Application exists, is active, and belongs to projectID).
- Attempt to create the Release directly, relying on the database's uniqueness constraint (see Repository.Create) rather than checking for existence first — a check-then-insert would race under concurrent or retried requests.
- If creation hits an existing (version, build number) pair, resolve the ambiguity by reading back what's actually stored: identical metadata is a safe no-op (the publisher's request already happened), different metadata is a conflict.
v is expected to already be validated (e.g. via version.Parse) — Publish does not re-validate its structure, only that it is accompanied by a valid build number and policy.
The returned bool is true if this call created a new Release, and false if it resolved to an idempotent no-op against an existing one — both are success outcomes (specs/protocols/http.md), but callers (e.g. the HTTP layer) may want to distinguish 201 from 200.
type Repository ¶
type Repository interface {
Create(ctx context.Context, applicationID application.ID, v version.Version, buildNumber int, policy Policy) (Release, error)
GetByVersion(ctx context.Context, applicationID application.ID, v version.Version, buildNumber int) (Release, error)
// ListByApplication returns applicationID's current non-revoked
// Releases, for update policy evaluation
// (specs/domain/update-policy.md). Revocation doesn't exist yet (see
// #28's issue scope), so today this is simply every Release under
// the Application; a caller filters once revocation lands.
ListByApplication(ctx context.Context, applicationID application.ID) ([]Release, error)
}
Repository persists and retrieves Releases, scoped to an Application. Infrastructure provides the implementation; this package only declares what it needs from it. Create is expected to rely on a database-level uniqueness constraint on (applicationID, version, buildNumber) rather than a check-then-insert race — see Publish.