package releasekit import ( "context" "io" internalbuild "gitea.maximumdirect.net/eric/ReleaseKit/internal/build" internaldelivery "gitea.maximumdirect.net/eric/ReleaseKit/internal/delivery" "gitea.maximumdirect.net/eric/ReleaseKit/internal/gitea" ) // DeliveryState describes the result of one delivery invocation. type DeliveryState string const ( // DeliveryNotAttempted means this invocation made no remote write. DeliveryNotAttempted DeliveryState = "not_attempted" // DeliveryComplete means the release page and all declared assets match. DeliveryComplete DeliveryState = "complete" // DeliveryPartial means a page or some assets were delivered, with work pending. DeliveryPartial DeliveryState = "partial" // DeliveryConflict means existing hosted content differs from the declaration. DeliveryConflict DeliveryState = "conflict" // DeliveryUnknown means a remote write may have succeeded but could not be resolved. DeliveryUnknown DeliveryState = "unknown" ) // GiteaDestination supplies the hosted repository and explicit access token. type GiteaDestination struct { BaseURL string Owner string Repository string Token string } // DeliveryAsset identifies a hosted asset by filename and SHA-256 digest. type DeliveryAsset struct { Filename string SHA256 string } // DeliveryRequest identifies a local tagged checkout and its explicit Gitea // destination. Build selects a managed build; Prebuilt supplies caller-owned // build output instead. They cannot both be set. Either may be nil for // page-only delivery when both are nil. Build with no targets, or an empty // Prebuilt result, also performs page-only delivery. type DeliveryRequest struct { RepositoryDir string // directory within the tagged source Git checkout. // ModuleDir selects a repository-relative module directory; empty selects // the repository root. ModuleDir string Product string Version string // ExpectedTag optionally requires the full CI event tag to match the // selected module's derived tag before building or making hosted requests. ExpectedTag string Destination GiteaDestination // Build selects binaries to build in temporary storage outside the checkout. Build *BuildSpec // Prebuilt supplies an existing build whose files remain caller-owned. Prebuilt *BuildResult // Output receives progress from a managed build. Output io.Writer } // DeliveryResult reports hosted content and the state reached by this // invocation. A partial result may accompany an error. The asset slices are // current observations: an asset appears in at most one slice, and early // failures may leave some declared assets unreported. type DeliveryResult struct { RepositoryDir string ModuleDir string ModulePath string Tag string // complete derived Git tag. Version string // selected stable semantic version. CandidateCommit string ReleaseURL string State DeliveryState // Matching lists existing attachments with the declared content. Matching []DeliveryAsset // Uploaded lists assets whose uploads were confirmed during this invocation. Uploaded []DeliveryAsset // Pending lists assets known to be missing. Pending []DeliveryAsset // Conflicting lists assets with detected content conflicts. Conflicting []DeliveryAsset // Uncertain lists assets whose remote state could not be resolved. Uncertain []DeliveryAsset } // Deliver verifies the selected local tag and hosted source, then creates or // finds its release page and adds any missing declared build assets. Build // requests are compiled into temporary storage that Deliver removes on return. // Prebuilt artifacts remain caller-owned and are not removed. Both paths // validate source identity and artifact contents before hosted writes. // Module selection and declaration links resolve from the captured commit. // Page-only delivery can use a sparse checkout with available committed objects; // managed builds require their selected source files in the working tree. func Deliver(ctx context.Context, request DeliveryRequest) (DeliveryResult, error) { result := DeliveryResult{Version: request.Version, State: DeliveryNotAttempted} destination := gitea.Destination{ BaseURL: request.Destination.BaseURL, Owner: request.Destination.Owner, Repository: request.Destination.Repository, Token: request.Destination.Token, } var prebuilt *internalbuild.Result if request.Prebuilt != nil { prebuilt = internalBuildResult(*request.Prebuilt) } internalResult, err := internaldelivery.Deliver(ctx, internaldelivery.Request{ RepositoryDir: request.RepositoryDir, ModuleDir: request.ModuleDir, Product: request.Product, Version: request.Version, ExpectedTag: request.ExpectedTag, Destination: destination, Build: internalBuildSpec(request.Build), Prebuilt: prebuilt, Output: request.Output, }) result.RepositoryDir = internalResult.RepositoryDir result.ModuleDir = internalResult.ModuleDir result.ModulePath = internalResult.ModulePath result.Tag = internalResult.Tag result.CandidateCommit = internalResult.CandidateCommit result.ReleaseURL = internalResult.ReleaseURL result.State = DeliveryState(internalResult.State) result.Matching = deliveryAssets(internalResult.Matching) result.Uploaded = deliveryAssets(internalResult.Uploaded) result.Pending = deliveryAssets(internalResult.Pending) result.Conflicting = deliveryAssets(internalResult.Conflicting) result.Uncertain = deliveryAssets(internalResult.Uncertain) return result, err } func deliveryAssets(assets []gitea.AssetIdentity) []DeliveryAsset { result := make([]DeliveryAsset, 0, len(assets)) for _, asset := range assets { result = append(result, DeliveryAsset{Filename: asset.Filename, SHA256: asset.SHA256}) } return result }