repo-sync/mirror/options.go
James Coleman 5af1dd8928
Some checks failed
Go package / build (push) Has been cancelled
first commit
2026-07-28 19:22:20 -05:00

160 lines
5.7 KiB
Go

// Package mirror synchronizes remote package repositories into a local
// directory tree, preserving the upstream layout so the result can be
// served directly to package managers.
package mirror
import (
"net/url"
"path"
"strings"
"time"
"github.com/grmrgecko/repo-sync/fetch"
)
// RepoType identifies the repository format being synchronized.
type RepoType string
const (
// RepoRPM synchronizes yum/dnf style repositories.
RepoRPM RepoType = "rpm"
// RepoDeb synchronizes apt style repositories.
RepoDeb RepoType = "deb"
// RepoArch synchronizes pacman style repositories.
RepoArch RepoType = "arch"
// RepoApk synchronizes Alpine apk style repositories.
RepoApk RepoType = "apk"
)
// AllTypes lists every supported repository format, in the order a
// directory's metadata is checked against them. RPM and deb come first as
// their markers are the most specific.
var AllTypes = []RepoType{RepoRPM, RepoDeb, RepoArch, RepoApk}
// TypeNames lists the supported format names, for user facing messages.
func TypeNames() []string {
names := make([]string, 0, len(AllTypes))
for _, typ := range AllTypes {
names = append(names, string(typ))
}
return names
}
// Options configures a synchronization run.
type Options struct {
// Type selects the repository format. It is empty for a run whose
// repositories are identified by their metadata rather than named on the
// command line, and is pinned to the repository's own format before that
// repository is synchronized.
Type RepoType
// Types limits the formats a run identifies repositories as. An empty
// list accepts every supported format.
Types []RepoType
// URLs lists the repository or mirrorlist URLs to synchronize.
URLs []string
// Destination is the local directory repositories are placed under.
Destination string
// Trim drops this many leading URL path components when building the
// destination path.
Trim int
// Flat places repositories directly in Destination without copying the
// URL path.
Flat bool
// Discover crawls each URL's directory listings for repositories.
Discover bool
// DiscoverDepth limits how deep discovery crawls below each URL.
DiscoverDepth int
// DiscoverCache is how long a crawl's results are reused before the
// tree is scanned again. Zero scans on every run.
DiscoverCache time.Duration
// Exclude lists globs matched against the names of entries found while
// crawling; a match is neither crawled nor mirrored. A pattern
// containing a slash is matched against the entry's path below the
// crawled URL instead of its name, and a leading slash anchors it to
// that URL.
Exclude []string
// IncludeFiles lists globs matched against the names of files found
// outside of any repository while crawling; a match is mirrored as a
// plain file. Patterns match by path the same way Exclude does, so a
// leading slash limits one to the crawled directory itself. Files
// inside a repository come from its metadata and are not matched here.
IncludeFiles []string
// Workers is the number of concurrent download workers.
Workers int
// Verify re-verifies checksums of existing local files.
Verify bool
// Prune deletes local files no longer part of the repository.
Prune bool
// PruneGrace defers pruning a file until it has been unreferenced for
// this long across runs.
PruneGrace time.Duration
// DryRun reports what would change without downloading packages or
// altering the published repository.
DryRun bool
// Missing selects what happens when the upstream does not serve a
// package the repository metadata lists. The zero value is the strict
// mode, failing the repository at the first such file.
Missing fetch.MissingMode
// MissingRetries is how many consecutive runs must miss a file before
// the retry mode accepts it as gone and stops failing the run over it.
MissingRetries int
// Trace writes a trace file describing this mirror into every
// repository synchronized, and mirrors the traces upstream publishes
// beside it.
Trace bool
// TraceInfo describes the mirror recorded in that trace file.
TraceInfo TraceInfo
// destBase, when set by the mirror server, pins the repository root
// directory directly instead of deriving it from the URL path.
destBase string
// Components limits deb synchronization to these components.
Components []string
// Architectures limits deb synchronization to these architectures.
Architectures []string
}
// discoverTypes returns the formats a run identifies repositories as: the
// formats it was limited to, the single format it was given, or every
// supported format when it named none.
func (o *Options) discoverTypes() []RepoType {
if len(o.Types) > 0 {
return o.Types
}
if o.Type != "" {
return []RepoType{o.Type}
}
return AllTypes
}
// newMissing starts tracking the packages an upstream does not serve for
// one repository, recording them in dir. That is the repository's own
// directory, and for apt the suite directory rather than the archive root,
// so suites sharing a pool keep their own records.
func (o *Options) newMissing(dir string) *fetch.Missing {
return fetch.NewMissing(dir, o.Missing, o.MissingRetries, o.DryRun)
}
// repoDest maps a repository URL onto the destination directory according
// to the configured trimming rules.
func (o *Options) repoDest(repoURL string) (string, error) {
if o.destBase != "" {
return o.destBase, nil
}
if o.Flat {
return o.Destination, nil
}
u, err := url.Parse(repoURL)
if err != nil {
return "", err
}
var segs []string
for _, seg := range strings.Split(u.Path, "/") {
if seg != "" {
segs = append(segs, seg)
}
}
if o.Trim >= len(segs) {
return o.Destination, nil
}
return fetch.LocalJoin(o.Destination, path.Join(segs[o.Trim:]...))
}