Define the internal forge delivery contract
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
# Implementation plan: neutral forge delivery and credential preflight
|
||||
|
||||
Status: ready for implementation; all stages are pending.
|
||||
Status: implementation in progress; Stage 1 is complete and Stages 2–10 are
|
||||
pending.
|
||||
|
||||
## Purpose and execution
|
||||
|
||||
@@ -219,7 +220,7 @@ buffering of complete binaries.
|
||||
|
||||
## Stage 1: Establish the internal backend contract
|
||||
|
||||
Status: pending.
|
||||
Status: complete.
|
||||
|
||||
1. Add `internal/forge` with the interface and logical values specified above.
|
||||
Define preflight classifications, neutral failures, and write-evidence types
|
||||
|
||||
152
internal/forge/forge.go
Normal file
152
internal/forge/forge.go
Normal file
@@ -0,0 +1,152 @@
|
||||
// Package forge defines the backend-neutral operations used by hosted delivery.
|
||||
package forge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Backend provides the hosted operations needed by the shared delivery
|
||||
// workflow. Implementations own destination-specific rules and protocol
|
||||
// details; the shared workflow uses only the logical values and evidence in
|
||||
// this package.
|
||||
type Backend interface {
|
||||
// Preflight verifies credentials and access to the configured destination
|
||||
// using read-only operations. It does not depend on a source tag or release.
|
||||
Preflight(ctx context.Context) error
|
||||
// TagCommit returns the commit observed for the complete tag.
|
||||
TagCommit(ctx context.Context, tag string) (string, error)
|
||||
// FindRelease returns the release for the complete tag. A missing release
|
||||
// is reported with FailureNotFound.
|
||||
FindRelease(ctx context.Context, tag string) (Release, error)
|
||||
// CreateRelease creates the requested release and returns the protocol
|
||||
// evidence even when an error occurs.
|
||||
CreateRelease(ctx context.Context, request CreateReleaseRequest) (Release, WriteEvidence, error)
|
||||
// ListAssets returns the assets attached to the release identified by handle.
|
||||
ListAssets(ctx context.Context, releaseHandle any) ([]Asset, error)
|
||||
// OpenAsset opens the remote asset identified by handle. The caller owns and
|
||||
// must close the returned stream, including when reading it fails.
|
||||
OpenAsset(ctx context.Context, assetHandle any) (io.ReadCloser, error)
|
||||
// UploadAsset uploads content to the release identified by handle. The
|
||||
// backend borrows content for the duration of this call and must finish any
|
||||
// request or multipart work before returning; it must not close content.
|
||||
UploadAsset(ctx context.Context, releaseHandle any, filename string, content io.Reader) (WriteEvidence, error)
|
||||
}
|
||||
|
||||
// Release contains the logical release properties used by shared delivery.
|
||||
// Handle is opaque to shared code and is interpreted only by the backend that
|
||||
// produced it.
|
||||
type Release struct {
|
||||
URL string
|
||||
Tag string
|
||||
Title string
|
||||
Body string
|
||||
Draft bool
|
||||
Prerelease bool
|
||||
Handle any
|
||||
}
|
||||
|
||||
// CreateReleaseRequest contains the release properties requested by shared
|
||||
// delivery.
|
||||
type CreateReleaseRequest struct {
|
||||
Tag string
|
||||
Commit string
|
||||
Title string
|
||||
Body string
|
||||
Draft bool
|
||||
Prerelease bool
|
||||
}
|
||||
|
||||
// Asset contains the remote properties needed to compare an attachment with a
|
||||
// declared local asset. Handle is opaque to shared code and is interpreted only
|
||||
// by the backend that produced it.
|
||||
type Asset struct {
|
||||
Filename string
|
||||
Size int64
|
||||
Handle any
|
||||
}
|
||||
|
||||
// PreflightFailureKind classifies a failure to verify credentials or access to
|
||||
// a delivery destination.
|
||||
type PreflightFailureKind string
|
||||
|
||||
const (
|
||||
PreflightMissingCredential PreflightFailureKind = "missing_credential"
|
||||
PreflightInvalidCredential PreflightFailureKind = "invalid_credential"
|
||||
PreflightInsufficientPermissions PreflightFailureKind = "insufficient_permissions"
|
||||
PreflightInaccessibleDestination PreflightFailureKind = "inaccessible_destination"
|
||||
PreflightUnavailable PreflightFailureKind = "unavailable"
|
||||
)
|
||||
|
||||
// PreflightFailure reports a classified credential or destination access
|
||||
// failure. Err retains the underlying cause for diagnostics and cancellation
|
||||
// identity.
|
||||
type PreflightFailure struct {
|
||||
Kind PreflightFailureKind
|
||||
Err error
|
||||
}
|
||||
|
||||
func (f *PreflightFailure) Error() string {
|
||||
if f.Err != nil {
|
||||
return string(f.Kind) + ": " + f.Err.Error()
|
||||
}
|
||||
return string(f.Kind)
|
||||
}
|
||||
|
||||
// Unwrap returns the cause of the preflight failure, if any.
|
||||
func (f *PreflightFailure) Unwrap() error { return f.Err }
|
||||
|
||||
// FailureKind classifies a backend-neutral delivery operation failure.
|
||||
type FailureKind string
|
||||
|
||||
const (
|
||||
FailureInvalid FailureKind = "invalid_request"
|
||||
FailureNotFound FailureKind = "not_found"
|
||||
FailureUnauthorized FailureKind = "unauthorized"
|
||||
FailureConflict FailureKind = "conflict"
|
||||
FailureMalformed FailureKind = "malformed_response"
|
||||
FailureUnverifiable FailureKind = "unverifiable"
|
||||
FailureTransport FailureKind = "transport"
|
||||
FailureRejected FailureKind = "rejected"
|
||||
FailureUnknown FailureKind = "unknown"
|
||||
)
|
||||
|
||||
// Failure reports a backend-neutral operation failure. Backend-specific
|
||||
// status and operation details belong in Err. Err is retained for errors.Is
|
||||
// and errors.As inspection.
|
||||
type Failure struct {
|
||||
Kind FailureKind
|
||||
Err error
|
||||
}
|
||||
|
||||
func (f *Failure) Error() string {
|
||||
if f.Err != nil {
|
||||
return string(f.Kind) + ": " + f.Err.Error()
|
||||
}
|
||||
return string(f.Kind)
|
||||
}
|
||||
|
||||
// Unwrap returns the cause of the operation failure, if any.
|
||||
func (f *Failure) Unwrap() error { return f.Err }
|
||||
|
||||
// WriteDisposition describes the backend's evidence about a write operation.
|
||||
type WriteDisposition string
|
||||
|
||||
const (
|
||||
WriteNotAttempted WriteDisposition = "not_attempted"
|
||||
WriteAccepted WriteDisposition = "accepted"
|
||||
WriteRejected WriteDisposition = "rejected"
|
||||
WriteConflict WriteDisposition = "conflict"
|
||||
WriteUncertain WriteDisposition = "uncertain"
|
||||
)
|
||||
|
||||
// WriteEvidence records whether a write was handed to the transport and what
|
||||
// its acknowledgment establishes. Accepted means the backend received and
|
||||
// decoded the required protocol acknowledgment; shared content checks still
|
||||
// apply. Uncertain means the outcome could not be established. Attempted marks
|
||||
// handoff to the transport and does not claim the server received the request.
|
||||
// Evidence remains meaningful when the operation also returns an error.
|
||||
type WriteEvidence struct {
|
||||
Attempted bool
|
||||
Disposition WriteDisposition
|
||||
}
|
||||
Reference in New Issue
Block a user