package releasekit import ( "context" "errors" "fmt" "go.maximumdirect.net/releasekit/internal/git" "go.maximumdirect.net/releasekit/internal/publish" ) // PublicationEvidence identifies one attempted publication. 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 } // PublicationReconciliationResult combines evidence with current remote state. type PublicationReconciliationResult struct { Evidence PublicationEvidence State PublicationState ObservedTagObject string ObservedCommit string } // PublicationRecoveryError describes an unresolved recovery operation. // 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 cause error } func (e *PublicationRecoveryError) Error() string { if e == nil { return "" } message := fmt.Sprintf("publication recovery %s", e.Kind) if e.RecordPath != "" { message += fmt.Sprintf(" for %q", e.RecordPath) } if e.cause != nil { message += ": " + git.DisplayError(e.cause).Error() } return message } func (e *PublicationRecoveryError) Unwrap() error { if e == nil { return nil } return e.cause } // ReconcilePublication observes the recorded endpoint without running candidate // validation or changing local or remote state. func ReconcilePublication(ctx context.Context, evidence PublicationEvidence) (PublicationReconciliationResult, error) { observation, err := publish.Reconcile(ctx, internalEvidence(evidence)) return publicObservation(observation), publicRecoveryError(err, "") } // LoadPublicationEvidence loads one explicit private attempt record. Relative // paths are interpreted from the caller's current directory; no Git lookup is // performed. func LoadPublicationEvidence(path string) (PublicationEvidence, error) { evidence, err := publish.LoadEvidence(path) return publicEvidence(evidence), publicRecoveryError(err, path) } func internalEvidence(e PublicationEvidence) publish.Evidence { return publish.Evidence(e) } func publicEvidence(e publish.Evidence) PublicationEvidence { return PublicationEvidence(e) } func publicObservation(o publish.Observation) PublicationReconciliationResult { return PublicationReconciliationResult{Evidence: publicEvidence(o.Evidence), State: PublicationState(o.State), ObservedTagObject: o.ObservedTagObject, ObservedCommit: o.ObservedCommit} } func publicRecoveryError(err error, path string) error { if err == nil { return nil } var recovery *publish.RecoveryError if errors.As(err, &recovery) { return &PublicationRecoveryError{Kind: recovery.Kind, RecordPath: path, MissingFields: append([]string(nil), recovery.MissingFields...), cause: recovery} } return &PublicationRecoveryError{Kind: "remote_observation", RecordPath: path, cause: err} }