2026-10-25 21:48:59 +01:00
package mirror
import (
"context"
"fmt"
"strings"
"github.com/git-pkgs/proxy/internal/handler"
)
// mirrorSwift mirrors the source archive of a Swift registry release, with
// its release metadata, from the registry the proxy serves Swift packages
// from.
func ( m * Mirror ) mirrorSwift ( ctx context . Context , pv PackageVersion , tracker * progressTracker ) {
2026-10-26 16:43:42 +01:00
upstream , err := m . registryURL ( "swift" , pv . Name )
2026-10-25 21:48:59 +01:00
if err != nil {
m . recordFailure ( tracker , pv , err )
return
}
2026-10-26 16:43:42 +01:00
h := handler . NewSwiftHandler ( m . proxy , "" , upstream )
var scope , name string
Accept Swift source repository PURLs in the denylist and retention rules (#641)
* Accept Swift source repository PURLs in the denylist and retention rules
Closes #613. pkg:swift/github.com/apple/swift-nio, as SBOM tools and GHSA
write Swift packages, is resolved to its registry identity through the
upstream's /identifiers:
- Denylist entries are resolved at startup, and the proxy refuses to start
when one can't be, since a skipped entry would let the version through.
- Retention keys are accepted at validation and kept pending. The
retention loop resolves them before each sweep, and Swift isn't swept
while any is pending, so a rule that keeps a package is never skipped.
The mirror's source URL helper moves to the handler as SwiftSourceURL.
* Resolve Swift source denylist entries in proxy mirror too
serve resolved source-form Swift denylist entries through the upstream,
but proxy mirror built its denylist directly, so such an entry never
matched a mirror download. Both now use server.ConfigureDenylist, and
neither runs when an entry can't be resolved.
* Apply Swift source policies to every listed identity
A Swift registry may list several identities for one source repository,
but a denylist entry or retention rule naming the repository only
covered the first. Denying pkg:swift/github.com/acme/widget@1.0.0 let
acme.widget through when the registry listed other.widget first, and a
never-evict rule cleared the cached apple.swift-nio archive when
other.swift-nio came first.
Both now cover every identity the registry lists. A retention rule that
conflicts with another rule for any of them stays pending, with none
applied. Mirroring a source PURL still downloads the first identity.
2026-10-31 12:34:05 +01:00
if source := handler . SwiftSourceURL ( pv . Name ) ; source != "" {
2026-10-26 16:43:42 +01:00
scope , name , err = h . ResolveIdentity ( ctx , source )
} else {
scope , name , err = swiftScopeAndName ( pv . Name )
}
2026-10-25 21:48:59 +01:00
if err != nil {
m . recordFailure ( tracker , pv , err )
return
}
2026-10-26 16:43:42 +01:00
result , err := h . MirrorRelease ( ctx , scope , name , pv . Version )
2026-10-25 21:48:59 +01:00
if err != nil {
m . recordFailure ( tracker , pv , err )
return
}
m . recordResult ( tracker , pv , result )
}
// registryURL returns the registry base URL Versions lists purlType packages
// from, which is the configured upstream, or empty for the default one.
func ( m * Mirror ) registryURL ( purlType , name string ) ( string , error ) {
if r , ok := m . Versions . ( RegistryVersions ) ; ok && r . URL != nil {
return r . URL ( purlType , name )
}
return "" , nil
}
2026-10-26 16:43:42 +01:00
// swiftScopeAndName splits the name of a pkg:swift PURL whose namespace is
// a registry scope, as in pkg:swift/apple/swift-nio for the identity
// apple.swift-nio.
func swiftScopeAndName ( name string ) ( scope , pkg string , _ error ) {
scope , pkg , ok := strings . Cut ( name , "/" )
if ! ok || scope == "" || pkg == "" || strings . ContainsAny ( scope , ".:" ) || strings . Contains ( pkg , "/" ) {
return "" , "" , fmt . Errorf ( "pkg:swift/%s needs a registry scope or a source repository as the namespace, such as pkg:swift/apple/swift-nio or pkg:swift/github.com/apple/swift-nio" , name )
2026-10-25 21:48:59 +01:00
}
2026-10-26 16:43:42 +01:00
return scope , pkg , nil
2026-10-25 21:48:59 +01:00
}