Define the internal forge delivery contract
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
# Implementation plan: neutral forge delivery and credential preflight
|
# 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
|
## Purpose and execution
|
||||||
|
|
||||||
@@ -219,7 +220,7 @@ buffering of complete binaries.
|
|||||||
|
|
||||||
## Stage 1: Establish the internal backend contract
|
## Stage 1: Establish the internal backend contract
|
||||||
|
|
||||||
Status: pending.
|
Status: complete.
|
||||||
|
|
||||||
1. Add `internal/forge` with the interface and logical values specified above.
|
1. Add `internal/forge` with the interface and logical values specified above.
|
||||||
Define preflight classifications, neutral failures, and write-evidence types
|
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