Merge origin/master into benvin/virtual-deb
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful

This commit is contained in:
2026-10-20 22:39:07 +11:00
230 changed files with 12882 additions and 1152 deletions
+40 -4
View File
@@ -28,6 +28,26 @@ type Engine struct {
getRemote func(context.Context, string) (*models.Remote, error)
rpmMember func(context.Context, string) (*RPMMember, error)
debMember func(ctx context.Context, name, base string) (*DebMember, error)
npmMember func(ctx context.Context, name, pkg string) (*NPMMember, error)
// NPMLocal returns a local npm repo's packument with tarball paths relative
// to the repo, or ErrNotFound.
NPMLocal func(ctx context.Context, repo, pkg string) ([]byte, error)
gemsMember func(ctx context.Context, name, path string) (*GemsMember, error)
// GemsLocal returns a local gems repo's versions, names or info/<gem>, or
// ErrNotFound.
GemsLocal func(ctx context.Context, repo, path string) ([]byte, error)
// AnsibleLocal returns a local ansible repo's collection, full versions list
// or version detail with root-relative URLs, or ErrNotFound.
AnsibleLocal func(ctx context.Context, repo, path string) ([]byte, error)
ansibleDoc func(ctx context.Context, remote models.Remote, path string) ([]byte, error)
goMember func(ctx context.Context, name, path string) (*GoMember, error)
puppetDoc func(ctx context.Context, remote models.Remote, path string) ([]byte, error)
mergeTTL time.Duration
@@ -63,6 +83,11 @@ func NewEngine(db *database.DB, proxyEngine *proxy.Engine) *Engine {
e := &Engine{db: db, proxyEngine: proxyEngine, getRemote: db.GetRemote, mergeTTL: rpmMergeTTL}
e.rpmMember = e.fetchRPMMember
e.debMember = e.fetchDebMember
e.npmMember = e.fetchNPMMember
e.gemsMember = e.fetchGemsMember
e.ansibleDoc = e.ansibleMemberDoc
e.goMember = e.fetchGoMember
e.puppetDoc = e.fetchMemberPath
return e
}
@@ -190,9 +215,10 @@ var (
ErrBadPath = errors.New("invalid path")
)
// MemberRedirect maps an rpm or deb virtual package path (<member>/<href>, as
// written by MergeRPM/MergeDeb) to the owning member's route. ok is false for
// any other path; ErrBadPath means the path is absolute or escapes the member.
// MemberRedirect maps an rpm, deb, npm or ansible virtual package path
// (<member>/<href>, as written by MergeRPM/MergeDeb/MergeNPM/AnsibleVersion)
// to the owning member's route. ok is false for any other path; ErrBadPath
// means the path is absolute or escapes the member.
func (e *Engine) MemberRedirect(ctx context.Context, virt models.Virtual, path, rawQuery, proxyBaseURL string) (string, bool, error) {
switch virt.PackageType {
case models.PackageRPM:
@@ -205,6 +231,7 @@ func (e *Engine) MemberRedirect(ctx context.Context, virt models.Virtual, path,
if strings.HasPrefix(path, "dists/") {
return "", false, nil
}
case models.PackageNPM, models.PackageAnsible:
default:
return "", false, nil
}
@@ -212,6 +239,11 @@ func (e *Engine) MemberRedirect(ctx context.Context, virt models.Virtual, path,
if !found || !slices.Contains(virt.Members, name) {
return "", false, nil
}
return e.memberLocation(ctx, name, rest, rawQuery, proxyBaseURL)
}
// memberLocation returns the URL of rest on member name's own route.
func (e *Engine) memberLocation(ctx context.Context, name, rest, rawQuery, proxyBaseURL string) (string, bool, error) {
segs, err := memberSegments(rest)
if err != nil {
return "", false, err
@@ -488,8 +520,9 @@ func (e *Engine) fetchMemberPath(ctx context.Context, remote models.Remote, path
if !ok {
return nil, fmt.Errorf("provider %q does not serve a local index", remote.PackageType)
}
reqPath, _, _ := strings.Cut(path, "?")
serve = func(w http.ResponseWriter, r *http.Request) bool {
return indexer.ServeLocalIndex(w, r, e.db, remote.Name, path)
return indexer.ServeLocalIndex(w, r, e.db, remote.Name, reqPath)
}
} else if rs, ok := prov.(provider.RemoteServer); ok {
serve = func(w http.ResponseWriter, r *http.Request) bool {
@@ -500,6 +533,9 @@ func (e *Engine) fetchMemberPath(ctx context.Context, remote models.Remote, path
if serve != nil {
rec := httptest.NewRecorder()
if serve(rec, httptest.NewRequestWithContext(ctx, http.MethodGet, "/"+path, nil)) {
if rec.Code == http.StatusNotFound || rec.Code == http.StatusGone {
return nil, fmt.Errorf("%s/%s: %w", remote.Name, path, ErrNotFound)
}
if rec.Code != http.StatusOK {
return nil, fmt.Errorf("%s/%s: status %d", remote.Name, path, rec.Code)
}