Finish publication recovery workflow

This commit is contained in:
2026-11-02 19:37:29 +00:00
parent 6915a86132
commit 599ad81bec
10 changed files with 697 additions and 76 deletions

View File

@@ -9,40 +9,47 @@ import (
"go.maximumdirect.net/releasekit/internal/publish"
)
// PublicationEvidence identifies one attempted publication. Endpoint may
// contain credentials; treat this value and saved records as sensitive.
// PublicationEvidence identifies one attempted publication independently of
// current repository state. Reconciliation uses RepositoryDir as Git's working
// directory, Tag and Endpoint as the observed ref and destination, and the
// captured object IDs to classify the observation. Endpoint may contain
// credentials; treat this value and saved records as sensitive.
type PublicationEvidence struct {
RepositoryDir string
ModuleDir string
ModulePath string
Version string
Tag string
CandidateCommit string
TagObject string
Endpoint string
Remote string
RecordedAt string
RepositoryDir string // Git working-directory context for relative endpoints; empty uses the caller's current directory.
ModuleDir string // physical module directory captured for this attempt.
ModulePath string // declared module path captured for this attempt.
Version string // semantic version selected for this attempt.
Tag string // complete Git tag ref selected for this attempt.
CandidateCommit string // candidate commit captured before publication.
TagObject string // annotated tag object ID captured before pushing, when available.
Endpoint string // exact effective push endpoint captured for this attempt; may contain credentials.
Remote string // remote name supplying supported transport configuration during later observation.
RecordedAt string // UTC RFC3339Nano time when this attempt's evidence was initialized, when available.
}
// PublicationReconciliationResult combines evidence with current remote state.
// PublicationReconciliationResult combines saved evidence with one later
// observation. It does not update local or remote state or modify the record.
type PublicationReconciliationResult struct {
Evidence PublicationEvidence
State PublicationState
ObservedTagObject string
ObservedCommit string
Evidence PublicationEvidence // original selected evidence.
State PublicationState // published, not_published, conflict, or unknown.
ObservedTagObject string // observed remote tag-object ID, when available.
ObservedCommit string // observed peeled commit ID, when available.
}
// PublicationRecoveryError describes an unresolved recovery operation.
// PublicationRecoveryError describes an unresolved recovery operation or a
// record/storage failure. Its Error text is credential-safe; Unwrap retains
// the underlying cause for errors.Is and errors.As.
// Kind includes missing_evidence, invalid_evidence, canceled, endpoint_changed,
// transport_configuration, remote_observation, inconsistent_observation,
// record_format, record_read, record_write, and storage_enumeration.
type PublicationRecoveryError struct {
Kind string
RecordPath string
MissingFields []string
RecordPath string // selected or affected record path, when applicable.
MissingFields []string // private-record field names required for the attempted classification.
cause error
}
// Error returns the credential-safe recovery diagnostic.
func (e *PublicationRecoveryError) Error() string {
if e == nil {
return ""
@@ -56,6 +63,8 @@ func (e *PublicationRecoveryError) Error() string {
}
return message
}
// Unwrap returns the underlying recovery or storage cause.
func (e *PublicationRecoveryError) Unwrap() error {
if e == nil {
return nil
@@ -78,18 +87,24 @@ func LoadPublicationEvidence(path string) (PublicationEvidence, error) {
return publicEvidence(evidence), publicRecoveryError(err, path)
}
// PublicationAttemptsRequest selects local publication-attempt storage.
type PublicationAttemptsRequest struct {
RepositoryDir string
RecordDir string
RepositoryDir string // repository used to resolve an empty or relative RecordDir; empty means the current directory.
RecordDir string // optional storage directory; relative paths use the repository root, and absolute paths can be listed outside a checkout.
}
// PublicationAttempt pairs one readable record path with the evidence it stores.
type PublicationAttempt struct {
RecordPath string
Evidence PublicationEvidence
RecordPath string // absolute path of the selected record.
Evidence PublicationEvidence // saved identity, including any partial fields.
}
// PublicationAttemptsResult contains readable attempts and per-record
// diagnostics from one local directory enumeration.
type PublicationAttemptsResult struct {
RecordDir string
Attempts []PublicationAttempt
Diagnostics []*PublicationRecoveryError
RecordDir string // resolved absolute directory selected for enumeration, when available.
Attempts []PublicationAttempt // readable records in deterministic path order.
Diagnostics []*PublicationRecoveryError // unreadable or unsupported records; other entries remain available.
}
// ListPublicationAttempts discovers saved local records without querying a