Merge remote-tracking branch 'origin/main' into test

This commit is contained in:
techknowlogick committed 2025-07-15 15:49:53 -04:00
commit c2e9532c38
482 files changed
+6150 -5726

No files matched your search

+1 -1
View File
@@ -43,7 +43,7 @@ func validateRunID(ctx *ArtifactContext) (*actions.ActionTask, int64, bool) {
return task, runID, true
}
func validateRunIDV4(ctx *ArtifactContext, rawRunID string) (*actions.ActionTask, int64, bool) { //nolint:unparam
func validateRunIDV4(ctx *ArtifactContext, rawRunID string) (*actions.ActionTask, int64, bool) { //nolint:unparam // ActionTask is never used
task := ctx.ActionTask
runID, err := strconv.ParseInt(rawRunID, 10, 64)
if err != nil || task.Job.RunID != runID {
+2
View File
@@ -467,7 +467,9 @@ func CommonRoutes() *web.Router {
g.MatchPath("HEAD", "/<group:*>/repodata/<filename>", rpm.CheckRepositoryFileExistence)
g.MatchPath("GET", "/<group:*>/repodata/<filename>", rpm.GetRepositoryFile)
g.MatchPath("PUT", "/<group:*>/upload", reqPackageAccess(perm.AccessModeWrite), rpm.UploadPackageFile)
// this URL pattern is only used internally in the RPM index, it is generated by us, the filename part is not really used (can be anything)
g.MatchPath("HEAD,GET", "/<group:*>/package/<name>/<version>/<architecture>", rpm.DownloadPackageFile)
g.MatchPath("HEAD,GET", "/<group:*>/package/<name>/<version>/<architecture>/<filename>", rpm.DownloadPackageFile)
g.MatchPath("DELETE", "/<group:*>/package/<name>/<version>/<architecture>", reqPackageAccess(perm.AccessModeWrite), rpm.DeletePackageFile)
}, reqPackageAccess(perm.AccessModeRead))
+1 -1
View File
@@ -51,7 +51,7 @@ func ListOrGetPackages(ctx *context.Context) {
DownloadPackageFile(ctx)
return
}
ctx.NotFound(nil)
http.NotFound(ctx.Resp, ctx.Req)
}
func EnumeratePackages(ctx *context.Context) {
+1 -1
View File
@@ -21,7 +21,7 @@ func (a *Auth) Name() string {
}
// Verify extracts the user from the Bearer token
// If it's an anonymous session a ghost user is returned
// If it's an anonymous session, a ghost user is returned
func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataStore, sess auth.SessionStore) (*user_model.User, error) {
packageMeta, err := packages.ParseAuthorizationRequest(req)
if err != nil {
+6 -13
View File
@@ -26,7 +26,7 @@ import (
// saveAsPackageBlob creates a package blob from an upload
// The uploaded blob gets stored in a special upload version to link them to the package/image
func saveAsPackageBlob(ctx context.Context, hsr packages_module.HashedSizeReader, pci *packages_service.PackageCreationInfo) (*packages_model.PackageBlob, error) { //nolint:unparam
func saveAsPackageBlob(ctx context.Context, hsr packages_module.HashedSizeReader, pci *packages_service.PackageCreationInfo) (*packages_model.PackageBlob, error) { //nolint:unparam // PackageBlob is never used
pb := packages_service.NewPackageBlob(hsr)
exists := false
@@ -95,15 +95,13 @@ func containerGlobalLockKey(piOwnerID int64, piName, usage string) string {
}
func getOrCreateUploadVersion(ctx context.Context, pi *packages_service.PackageInfo) (*packages_model.PackageVersion, error) {
var uploadVersion *packages_model.PackageVersion
releaser, err := globallock.Lock(ctx, containerGlobalLockKey(pi.Owner.ID, pi.Name, "package"))
if err != nil {
return nil, err
}
defer releaser()
err = db.WithTx(ctx, func(ctx context.Context) error {
return db.WithTx2(ctx, func(ctx context.Context) (*packages_model.PackageVersion, error) {
created := true
p := &packages_model.Package{
OwnerID: pi.Owner.ID,
@@ -115,7 +113,7 @@ func getOrCreateUploadVersion(ctx context.Context, pi *packages_service.PackageI
if p, err = packages_model.TryInsertPackage(ctx, p); err != nil {
if !errors.Is(err, packages_model.ErrDuplicatePackage) {
log.Error("Error inserting package: %v", err)
return err
return nil, err
}
created = false
}
@@ -123,7 +121,7 @@ func getOrCreateUploadVersion(ctx context.Context, pi *packages_service.PackageI
if created {
if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository, strings.ToLower(pi.Owner.LowerName+"/"+pi.Name)); err != nil {
log.Error("Error setting package property: %v", err)
return err
return nil, err
}
}
@@ -138,16 +136,11 @@ func getOrCreateUploadVersion(ctx context.Context, pi *packages_service.PackageI
if pv, err = packages_model.GetOrInsertVersion(ctx, pv); err != nil {
if !errors.Is(err, packages_model.ErrDuplicatePackageVersion) {
log.Error("Error inserting package: %v", err)
return err
return nil, err
}
}
uploadVersion = pv
return nil
return pv, nil
})
return uploadVersion, err
}
func createFileForBlob(ctx context.Context, pv *packages_model.PackageVersion, pb *packages_model.PackageBlob) error {
+16 -12
View File
@@ -13,6 +13,7 @@ import (
"regexp"
"strconv"
"strings"
"sync"
auth_model "code.gitea.io/gitea/models/auth"
packages_model "code.gitea.io/gitea/models/packages"
@@ -39,10 +40,14 @@ import (
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-manifests
const maxManifestSize = 10 * 1024 * 1024
var (
imageNamePattern = regexp.MustCompile(`\A[a-z0-9]+([._-][a-z0-9]+)*(/[a-z0-9]+([._-][a-z0-9]+)*)*\z`)
referencePattern = regexp.MustCompile(`\A[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}\z`)
)
var globalVars = sync.OnceValue(func() (ret struct {
imageNamePattern, referencePattern *regexp.Regexp
},
) {
ret.imageNamePattern = regexp.MustCompile(`\A[a-z0-9]+([._-][a-z0-9]+)*(/[a-z0-9]+([._-][a-z0-9]+)*)*\z`)
ret.referencePattern = regexp.MustCompile(`\A[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}\z`)
return ret
})
type containerHeaders struct {
Status int
@@ -84,9 +89,7 @@ func jsonResponse(ctx *context.Context, status int, obj any) {
Status: status,
ContentType: "application/json",
})
if err := json.NewEncoder(ctx.Resp).Encode(obj); err != nil {
log.Error("JSON encode: %v", err)
}
_ = json.NewEncoder(ctx.Resp).Encode(obj) // ignore network errors
}
func apiError(ctx *context.Context, status int, err error) {
@@ -134,7 +137,7 @@ func ReqContainerAccess(ctx *context.Context) {
// VerifyImageName is a middleware which checks if the image name is allowed
func VerifyImageName(ctx *context.Context) {
if !imageNamePattern.MatchString(ctx.PathParam("image")) {
if !globalVars().imageNamePattern.MatchString(ctx.PathParam("image")) {
apiErrorDefined(ctx, errNameInvalid)
}
}
@@ -216,7 +219,7 @@ func GetRepositoryList(ctx *context.Context) {
if len(repositories) == n {
v := url.Values{}
if n > 0 {
v.Add("n", strconv.Itoa(n))
v.Add("n", strconv.Itoa(n)) // FIXME: "n" can't be zero here, the logic is inconsistent with GetTagsList
}
v.Add("last", repositories[len(repositories)-1])
@@ -565,7 +568,7 @@ func PutManifest(ctx *context.Context) {
IsTagged: digest.Digest(reference).Validate() != nil,
}
if mci.IsTagged && !referencePattern.MatchString(reference) {
if mci.IsTagged && !globalVars().referencePattern.MatchString(reference) {
apiErrorDefined(ctx, errManifestInvalid.WithMessage("Tag is invalid"))
return
}
@@ -618,7 +621,7 @@ func getBlobSearchOptionsFromContext(ctx *context.Context) (*container_model.Blo
reference := ctx.PathParam("reference")
if d := digest.Digest(reference); d.Validate() == nil {
opts.Digest = string(d)
} else if referencePattern.MatchString(reference) {
} else if globalVars().referencePattern.MatchString(reference) {
opts.Tag = reference
opts.OnlyLead = true
} else {
@@ -782,7 +785,8 @@ func GetTagsList(ctx *context.Context) {
})
}
// FIXME: Workaround to be removed in v1.20
// FIXME: Workaround to be removed in v1.20.
// Update maybe we should never really remote it, as long as there is legacy data?
// https://github.com/go-gitea/gitea/issues/19586
func workaroundGetContainerBlob(ctx *context.Context, opts *container_model.BlobSearchOptions) (*packages_model.PackageFileDescriptor, error) {
blob, err := container_model.GetContainerBlob(ctx, opts)
+54 -113
View File
@@ -46,11 +46,9 @@ func processManifest(ctx context.Context, mci *manifestCreationInfo, buf *packag
if err := json.NewDecoder(buf).Decode(&index); err != nil {
return "", err
}
if index.SchemaVersion != 2 {
return "", errUnsupported.WithMessage("Schema version is not supported")
}
if _, err := buf.Seek(0, io.SeekStart); err != nil {
return "", err
}
@@ -77,24 +75,41 @@ func processManifest(ctx context.Context, mci *manifestCreationInfo, buf *packag
return "", errManifestInvalid
}
func processOciImageManifest(ctx context.Context, mci *manifestCreationInfo, buf *packages_module.HashedBuffer) (string, error) {
manifestDigest := ""
type processManifestTxRet struct {
pv *packages_model.PackageVersion
pb *packages_model.PackageBlob
created bool
digest string
}
err := func() error {
manifest, configDescriptor, metadata, err := container_service.ParseManifestMetadata(ctx, buf, mci.Owner.ID, mci.Image)
if err != nil {
return err
}
if _, err = buf.Seek(0, io.SeekStart); err != nil {
return err
func handleCreateManifestResult(ctx context.Context, err error, mci *manifestCreationInfo, contentStore *packages_module.ContentStore, txRet *processManifestTxRet) (string, error) {
if err != nil && txRet.created && txRet.pb != nil {
if err := contentStore.Delete(packages_module.BlobHash256Key(txRet.pb.HashSHA256)); err != nil {
log.Error("Error deleting package blob from content store: %v", err)
}
return "", err
}
pd, err := packages_model.GetPackageDescriptor(ctx, txRet.pv)
if err != nil {
log.Error("Error getting package descriptor: %v", err) // ignore this error
} else {
notify_service.PackageCreate(ctx, mci.Creator, pd)
}
return txRet.digest, nil
}
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return err
}
defer committer.Close()
func processOciImageManifest(ctx context.Context, mci *manifestCreationInfo, buf *packages_module.HashedBuffer) (manifestDigest string, errRet error) {
manifest, configDescriptor, metadata, err := container_service.ParseManifestMetadata(ctx, buf, mci.Owner.ID, mci.Image)
if err != nil {
return "", err
}
if _, err = buf.Seek(0, io.SeekStart); err != nil {
return "", err
}
contentStore := packages_module.NewContentStore()
var txRet processManifestTxRet
err = db.WithTx(ctx, func(ctx context.Context) (err error) {
blobReferences := make([]*blobReference, 0, 1+len(manifest.Layers))
blobReferences = append(blobReferences, &blobReference{
Digest: manifest.Config.Digest,
@@ -127,7 +142,7 @@ func processOciImageManifest(ctx context.Context, mci *manifestCreationInfo, buf
}
uploadVersion, err := packages_model.GetInternalVersionByNameAndVersion(ctx, mci.Owner.ID, packages_model.TypeContainer, mci.Image, container_module.UploadVersion)
if err != nil && err != packages_model.ErrPackageNotExist {
if err != nil && !errors.Is(err, packages_model.ErrPackageNotExist) {
return err
}
@@ -136,61 +151,26 @@ func processOciImageManifest(ctx context.Context, mci *manifestCreationInfo, buf
return err
}
}
txRet.pv = pv
txRet.pb, txRet.created, txRet.digest, err = createManifestBlob(ctx, contentStore, mci, pv, buf)
return err
})
pb, created, digest, err := createManifestBlob(ctx, mci, pv, buf)
removeBlob := false
defer func() {
if removeBlob {
contentStore := packages_module.NewContentStore()
if err := contentStore.Delete(packages_module.BlobHash256Key(pb.HashSHA256)); err != nil {
log.Error("Error deleting package blob from content store: %v", err)
}
}
}()
if err != nil {
removeBlob = created
return err
}
return handleCreateManifestResult(ctx, err, mci, contentStore, &txRet)
}
if err := committer.Commit(); err != nil {
removeBlob = created
return err
}
if err := notifyPackageCreate(ctx, mci.Creator, pv); err != nil {
return err
}
manifestDigest = digest
return nil
}()
if err != nil {
func processOciImageIndex(ctx context.Context, mci *manifestCreationInfo, buf *packages_module.HashedBuffer) (manifestDigest string, errRet error) {
var index oci.Index
if err := json.NewDecoder(buf).Decode(&index); err != nil {
return "", err
}
if _, err := buf.Seek(0, io.SeekStart); err != nil {
return "", err
}
return manifestDigest, nil
}
func processOciImageIndex(ctx context.Context, mci *manifestCreationInfo, buf *packages_module.HashedBuffer) (string, error) {
manifestDigest := ""
err := func() error {
var index oci.Index
if err := json.NewDecoder(buf).Decode(&index); err != nil {
return err
}
if _, err := buf.Seek(0, io.SeekStart); err != nil {
return err
}
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return err
}
defer committer.Close()
contentStore := packages_module.NewContentStore()
var txRet processManifestTxRet
err := db.WithTx(ctx, func(ctx context.Context) (err error) {
metadata := &container_module.Metadata{
Type: container_module.TypeOCI,
Manifests: make([]*container_module.Manifest, 0, len(index.Manifests)),
@@ -241,50 +221,12 @@ func processOciImageIndex(ctx context.Context, mci *manifestCreationInfo, buf *p
return err
}
pb, created, digest, err := createManifestBlob(ctx, mci, pv, buf)
removeBlob := false
defer func() {
if removeBlob {
contentStore := packages_module.NewContentStore()
if err := contentStore.Delete(packages_module.BlobHash256Key(pb.HashSHA256)); err != nil {
log.Error("Error deleting package blob from content store: %v", err)
}
}
}()
if err != nil {
removeBlob = created
return err
}
if err := committer.Commit(); err != nil {
removeBlob = created
return err
}
if err := notifyPackageCreate(ctx, mci.Creator, pv); err != nil {
return err
}
manifestDigest = digest
return nil
}()
if err != nil {
return "", err
}
return manifestDigest, nil
}
func notifyPackageCreate(ctx context.Context, doer *user_model.User, pv *packages_model.PackageVersion) error {
pd, err := packages_model.GetPackageDescriptor(ctx, pv)
if err != nil {
txRet.pv = pv
txRet.pb, txRet.created, txRet.digest, err = createManifestBlob(ctx, contentStore, mci, pv, buf)
return err
}
})
notify_service.PackageCreate(ctx, doer, pd)
return nil
return handleCreateManifestResult(ctx, err, mci, contentStore, &txRet)
}
func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, metadata *container_module.Metadata) (*packages_model.PackageVersion, error) {
@@ -437,7 +379,7 @@ func createFileFromBlobReference(ctx context.Context, pv, uploadVersion *package
return pf, nil
}
func createManifestBlob(ctx context.Context, mci *manifestCreationInfo, pv *packages_model.PackageVersion, buf *packages_module.HashedBuffer) (*packages_model.PackageBlob, bool, string, error) {
func createManifestBlob(ctx context.Context, contentStore *packages_module.ContentStore, mci *manifestCreationInfo, pv *packages_model.PackageVersion, buf *packages_module.HashedBuffer) (_ *packages_model.PackageBlob, created bool, manifestDigest string, _ error) {
pb, exists, err := packages_model.GetOrInsertBlob(ctx, packages_service.NewPackageBlob(buf))
if err != nil {
log.Error("Error inserting package blob: %v", err)
@@ -446,21 +388,20 @@ func createManifestBlob(ctx context.Context, mci *manifestCreationInfo, pv *pack
// FIXME: Workaround to be removed in v1.20
// https://github.com/go-gitea/gitea/issues/19586
if exists {
err = packages_module.NewContentStore().Has(packages_module.BlobHash256Key(pb.HashSHA256))
err = contentStore.Has(packages_module.BlobHash256Key(pb.HashSHA256))
if err != nil && (errors.Is(err, util.ErrNotExist) || errors.Is(err, os.ErrNotExist)) {
log.Debug("Package registry inconsistent: blob %s does not exist on file system", pb.HashSHA256)
exists = false
}
}
if !exists {
contentStore := packages_module.NewContentStore()
if err := contentStore.Save(packages_module.BlobHash256Key(pb.HashSHA256), buf, buf.Size()); err != nil {
log.Error("Error saving package blob in content store: %v", err)
return nil, false, "", err
}
}
manifestDigest := digestFromHashSummer(buf)
manifestDigest = digestFromHashSummer(buf)
pf, err := createFileFromBlobReference(ctx, pv, nil, &blobReference{
Digest: digest.Digest(manifestDigest),
MediaType: mci.MediaType,
+102 -42
View File
@@ -53,15 +53,23 @@ type RegistrationIndexPageItem struct {
// https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#catalog-entry
type CatalogEntry struct {
CatalogLeafURL string `json:"@id"`
PackageContentURL string `json:"packageContent"`
ID string `json:"id"`
Version string `json:"version"`
Description string `json:"description"`
ReleaseNotes string `json:"releaseNotes"`
Authors string `json:"authors"`
RequireLicenseAcceptance bool `json:"requireLicenseAcceptance"`
ProjectURL string `json:"projectURL"`
Copyright string `json:"copyright"`
DependencyGroups []*PackageDependencyGroup `json:"dependencyGroups"`
Description string `json:"description"`
IconURL string `json:"iconUrl"`
ID string `json:"id"`
IsPrerelease bool `json:"isPrerelease"`
Language string `json:"language"`
LicenseURL string `json:"licenseUrl"`
PackageContentURL string `json:"packageContent"`
ProjectURL string `json:"projectUrl"`
RequireLicenseAcceptance bool `json:"requireLicenseAcceptance"`
Summary string `json:"summary"`
Tags string `json:"tags"`
Version string `json:"version"`
ReleaseNotes string `json:"releaseNotes"`
Published time.Time `json:"published"`
}
// https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#package-dependency-group
@@ -109,15 +117,24 @@ func createRegistrationIndexPageItem(l *linkBuilder, pd *packages_model.PackageD
RegistrationLeafURL: l.GetRegistrationLeafURL(pd.Package.Name, pd.Version.Version),
PackageContentURL: l.GetPackageDownloadURL(pd.Package.Name, pd.Version.Version),
CatalogEntry: &CatalogEntry{
CatalogLeafURL: l.GetRegistrationLeafURL(pd.Package.Name, pd.Version.Version),
PackageContentURL: l.GetPackageDownloadURL(pd.Package.Name, pd.Version.Version),
ID: pd.Package.Name,
Version: pd.Version.Version,
Description: metadata.Description,
ReleaseNotes: metadata.ReleaseNotes,
Authors: metadata.Authors,
ProjectURL: metadata.ProjectURL,
DependencyGroups: createDependencyGroups(pd),
CatalogLeafURL: l.GetRegistrationLeafURL(pd.Package.Name, pd.Version.Version),
Authors: metadata.Authors,
Copyright: metadata.Copyright,
DependencyGroups: createDependencyGroups(pd),
Description: metadata.Description,
IconURL: metadata.IconURL,
ID: pd.Package.Name,
IsPrerelease: pd.Version.IsPrerelease(),
Language: metadata.Language,
LicenseURL: metadata.LicenseURL,
PackageContentURL: l.GetPackageDownloadURL(pd.Package.Name, pd.Version.Version),
ProjectURL: metadata.ProjectURL,
RequireLicenseAcceptance: metadata.RequireLicenseAcceptance,
Summary: metadata.Summary,
Tags: metadata.Tags,
Version: pd.Version.Version,
ReleaseNotes: metadata.ReleaseNotes,
Published: pd.Version.CreatedUnix.AsLocalTime(),
},
}
}
@@ -145,22 +162,42 @@ func createDependencyGroups(pd *packages_model.PackageDescriptor) []*PackageDepe
// https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-leaf
type RegistrationLeafResponse struct {
RegistrationLeafURL string `json:"@id"`
Type []string `json:"@type"`
Listed bool `json:"listed"`
PackageContentURL string `json:"packageContent"`
Published time.Time `json:"published"`
RegistrationIndexURL string `json:"registration"`
RegistrationLeafURL string `json:"@id"`
Type []string `json:"@type"`
PackageContentURL string `json:"packageContent"`
RegistrationIndexURL string `json:"registration"`
CatalogEntry CatalogEntry `json:"catalogEntry"`
}
func createRegistrationLeafResponse(l *linkBuilder, pd *packages_model.PackageDescriptor) *RegistrationLeafResponse {
registrationLeafURL := l.GetRegistrationLeafURL(pd.Package.Name, pd.Version.Version)
packageDownloadURL := l.GetPackageDownloadURL(pd.Package.Name, pd.Version.Version)
metadata := pd.Metadata.(*nuget_module.Metadata)
return &RegistrationLeafResponse{
Type: []string{"Package", "http://schema.nuget.org/catalog#Permalink"},
Listed: true,
Published: pd.Version.CreatedUnix.AsLocalTime(),
RegistrationLeafURL: l.GetRegistrationLeafURL(pd.Package.Name, pd.Version.Version),
PackageContentURL: l.GetPackageDownloadURL(pd.Package.Name, pd.Version.Version),
RegistrationLeafURL: registrationLeafURL,
RegistrationIndexURL: l.GetRegistrationIndexURL(pd.Package.Name),
PackageContentURL: packageDownloadURL,
Type: []string{"Package", "http://schema.nuget.org/catalog#Permalink"},
CatalogEntry: CatalogEntry{
CatalogLeafURL: registrationLeafURL,
Authors: metadata.Authors,
Copyright: metadata.Copyright,
DependencyGroups: createDependencyGroups(pd),
Description: metadata.Description,
IconURL: metadata.IconURL,
ID: pd.Package.Name,
IsPrerelease: pd.Version.IsPrerelease(),
Language: metadata.Language,
LicenseURL: metadata.LicenseURL,
PackageContentURL: packageDownloadURL,
ProjectURL: metadata.ProjectURL,
RequireLicenseAcceptance: metadata.RequireLicenseAcceptance,
Summary: metadata.Summary,
Tags: metadata.Tags,
Version: pd.Version.Version,
ReleaseNotes: metadata.ReleaseNotes,
Published: pd.Version.CreatedUnix.AsLocalTime(),
},
}
}
@@ -188,13 +225,24 @@ type SearchResultResponse struct {
// https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-result
type SearchResult struct {
ID string `json:"id"`
Version string `json:"version"`
Versions []*SearchResultVersion `json:"versions"`
Description string `json:"description"`
Authors string `json:"authors"`
ProjectURL string `json:"projectURL"`
RegistrationIndexURL string `json:"registration"`
Authors string `json:"authors"`
Copyright string `json:"copyright"`
DependencyGroups []*PackageDependencyGroup `json:"dependencyGroups"`
Description string `json:"description"`
IconURL string `json:"iconUrl"`
ID string `json:"id"`
IsPrerelease bool `json:"isPrerelease"`
Language string `json:"language"`
LicenseURL string `json:"licenseUrl"`
ProjectURL string `json:"projectUrl"`
RequireLicenseAcceptance bool `json:"requireLicenseAcceptance"`
Summary string `json:"summary"`
Tags string `json:"tags"`
Title string `json:"title"`
TotalDownloads int64 `json:"totalDownloads"`
Version string `json:"version"`
Versions []*SearchResultVersion `json:"versions"`
RegistrationIndexURL string `json:"registration"`
}
// https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-result
@@ -230,11 +278,12 @@ func createSearchResultResponse(l *linkBuilder, totalHits int64, pds []*packages
func createSearchResult(l *linkBuilder, pds []*packages_model.PackageDescriptor) *SearchResult {
latest := pds[0]
versions := make([]*SearchResultVersion, 0, len(pds))
totalDownloads := int64(0)
for _, pd := range pds {
if latest.SemVer.LessThan(pd.SemVer) {
latest = pd
}
totalDownloads += pd.Version.DownloadCount
versions = append(versions, &SearchResultVersion{
RegistrationLeafURL: l.GetRegistrationLeafURL(pd.Package.Name, pd.Version.Version),
Version: pd.Version.Version,
@@ -244,12 +293,23 @@ func createSearchResult(l *linkBuilder, pds []*packages_model.PackageDescriptor)
metadata := latest.Metadata.(*nuget_module.Metadata)
return &SearchResult{
ID: latest.Package.Name,
Version: latest.Version.Version,
Versions: versions,
Description: metadata.Description,
Authors: metadata.Authors,
ProjectURL: metadata.ProjectURL,
RegistrationIndexURL: l.GetRegistrationIndexURL(latest.Package.Name),
Authors: metadata.Authors,
Copyright: metadata.Copyright,
Description: metadata.Description,
DependencyGroups: createDependencyGroups(latest),
IconURL: metadata.IconURL,
ID: latest.Package.Name,
IsPrerelease: latest.Version.IsPrerelease(),
Language: metadata.Language,
LicenseURL: metadata.LicenseURL,
ProjectURL: metadata.ProjectURL,
RequireLicenseAcceptance: metadata.RequireLicenseAcceptance,
Summary: metadata.Summary,
Tags: metadata.Tags,
Title: metadata.Title,
TotalDownloads: totalDownloads,
Version: latest.Version.Version,
Versions: versions,
RegistrationIndexURL: l.GetRegistrationIndexURL(latest.Package.Name),
}
}
+1 -1
View File
@@ -36,7 +36,7 @@ func apiError(ctx *context.Context, status int, obj any) {
})
}
func xmlResponse(ctx *context.Context, status int, obj any) { //nolint:unparam
func xmlResponse(ctx *context.Context, status int, obj any) { //nolint:unparam // status is always StatusOK
ctx.Resp.Header().Set("Content-Type", "application/atom+xml; charset=utf-8")
ctx.Resp.WriteHeader(status)
if _, err := ctx.Resp.Write([]byte(xml.Header)); err != nil {
+1 -1
View File
@@ -29,7 +29,7 @@ func CreateOrg(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of the user that will own the created organization
// description: username of the user who will own the created organization
// type: string
// required: true
// - name: organization
+1 -1
View File
@@ -22,7 +22,7 @@ func CreateRepo(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of the user. This user will own the created repository
// description: username of the user who will own the created repository
// type: string
// required: true
// - name: repository
+7 -7
View File
@@ -175,7 +175,7 @@ func EditUser(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user to edit
// description: username of the user whose data is to be edited
// type: string
// required: true
// - name: body
@@ -240,7 +240,7 @@ func EditUser(ctx *context.APIContext) {
Description: optional.FromPtr(form.Description),
IsActive: optional.FromPtr(form.Active),
IsAdmin: user_service.UpdateOptionFieldFromPtr(form.Admin),
Visibility: optional.FromNonDefault(api.VisibilityModes[form.Visibility]),
Visibility: optional.FromMapLookup(api.VisibilityModes, form.Visibility),
AllowGitHook: optional.FromPtr(form.AllowGitHook),
AllowImportLocal: optional.FromPtr(form.AllowImportLocal),
MaxRepoCreation: optional.FromPtr(form.MaxRepoCreation),
@@ -272,7 +272,7 @@ func DeleteUser(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user to delete
// description: username of the user to delete
// type: string
// required: true
// - name: purge
@@ -328,7 +328,7 @@ func CreatePublicKey(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of the user
// description: username of the user who is to receive a public key
// type: string
// required: true
// - name: key
@@ -358,7 +358,7 @@ func DeleteUserPublicKey(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user
// description: username of the user whose public key is to be deleted
// type: string
// required: true
// - name: id
@@ -405,7 +405,7 @@ func SearchUsers(ctx *context.APIContext) {
// format: int64
// - name: login_name
// in: query
// description: user's login name to search for
// description: identifier of the user, provided by the external authenticator
// type: string
// - name: page
// in: query
@@ -456,7 +456,7 @@ func RenameUser(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: existing username of user
// description: current username of the user
// type: string
// required: true
// - name: body
+3 -3
View File
@@ -22,7 +22,7 @@ func ListUserBadges(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user
// description: username of the user whose badges are to be listed
// type: string
// required: true
// responses:
@@ -53,7 +53,7 @@ func AddUserBadges(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user
// description: username of the user to whom a badge is to be added
// type: string
// required: true
// - name: body
@@ -87,7 +87,7 @@ func DeleteUserBadges(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user
// description: username of the user whose badge is to be deleted
// type: string
// required: true
// - name: body
+1 -1
View File
@@ -145,7 +145,7 @@ func repoAssignment() func(ctx *context.APIContext) {
)
// Check if the user is the same as the repository owner.
if ctx.IsSigned && ctx.Doer.LowerName == strings.ToLower(userName) {
if ctx.IsSigned && strings.EqualFold(ctx.Doer.LowerName, userName) {
owner = ctx.Doer
} else {
owner, err = user_model.GetUserByName(ctx, userName)
+2 -2
View File
@@ -42,7 +42,7 @@ func Markup(ctx *context.APIContext) {
return
}
mode := util.Iif(form.Wiki, "wiki", form.Mode) //nolint:staticcheck
mode := util.Iif(form.Wiki, "wiki", form.Mode) //nolint:staticcheck // form.Wiki is deprecated
common.RenderMarkup(ctx.Base, ctx.Repo, mode, form.Text, form.Context, form.FilePath)
}
@@ -73,7 +73,7 @@ func Markdown(ctx *context.APIContext) {
return
}
mode := util.Iif(form.Wiki, "wiki", form.Mode) //nolint:staticcheck
mode := util.Iif(form.Wiki, "wiki", form.Mode) //nolint:staticcheck // form.Wiki is deprecated
common.RenderMarkup(ctx.Base, ctx.Repo, mode, form.Text, form.Context, "")
}
+3 -3
View File
@@ -47,7 +47,7 @@ func CheckUserBlock(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
// description: user to check
// description: username of the user to check
// type: string
// required: true
// responses:
@@ -71,7 +71,7 @@ func BlockUser(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
// description: user to block
// description: username of the user to block
// type: string
// required: true
// - name: note
@@ -101,7 +101,7 @@ func UnblockUser(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
// description: user to unblock
// description: username of the user to unblock
// type: string
// required: true
// responses:
+5 -5
View File
@@ -133,7 +133,7 @@ func IsMember(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
// description: username of the user
// description: username of the user to check for an organization membership
// type: string
// required: true
// responses:
@@ -186,7 +186,7 @@ func IsPublicMember(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
// description: username of the user
// description: username of the user to check for a public organization membership
// type: string
// required: true
// responses:
@@ -240,7 +240,7 @@ func PublicizeMember(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
// description: username of the user
// description: username of the user whose membership is to be publicized
// type: string
// required: true
// responses:
@@ -282,7 +282,7 @@ func ConcealMember(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
// description: username of the user
// description: username of the user whose membership is to be concealed
// type: string
// required: true
// responses:
@@ -324,7 +324,7 @@ func DeleteMember(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
// description: username of the user
// description: username of the user to remove from the organization
// type: string
// required: true
// responses:
+3 -3
View File
@@ -82,7 +82,7 @@ func ListUserOrgs(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user
// description: username of the user whose organizations are to be listed
// type: string
// required: true
// - name: page
@@ -112,7 +112,7 @@ func GetUserOrgsPermissions(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user
// description: username of the user whose permissions are to be obtained
// type: string
// required: true
// - name: org
@@ -391,7 +391,7 @@ func Edit(ctx *context.APIContext) {
Description: optional.Some(form.Description),
Website: optional.Some(form.Website),
Location: optional.Some(form.Location),
Visibility: optional.FromNonDefault(api.VisibilityModes[form.Visibility]),
Visibility: optional.FromMapLookup(api.VisibilityModes, form.Visibility),
RepoAdminChangeTeamAccess: optional.FromPtr(form.RepoAdminChangeTeamAccess),
}
if err := user_service.UpdateUser(ctx, ctx.Org.Organization.AsUser(), opts); err != nil {
+3 -3
View File
@@ -426,7 +426,7 @@ func GetTeamMember(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
// description: username of the member to list
// description: username of the user whose data is to be listed
// type: string
// required: true
// responses:
@@ -467,7 +467,7 @@ func AddTeamMember(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
// description: username of the user to add
// description: username of the user to add to a team
// type: string
// required: true
// responses:
@@ -509,7 +509,7 @@ func RemoveTeamMember(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
// description: username of the user to remove
// description: username of the user to remove from a team
// type: string
// required: true
// responses:
+1 -1
View File
@@ -747,7 +747,7 @@ func (Action) ListWorkflowRuns(ctx *context.APIContext) {
// type: integer
// responses:
// "200":
// "$ref": "#/responses/ArtifactsList"
// "$ref": "#/responses/WorkflowRunsList"
// "400":
// "$ref": "#/responses/error"
// "404":
+4 -4
View File
@@ -93,7 +93,7 @@ func IsCollaborator(ctx *context.APIContext) {
// required: true
// - name: collaborator
// in: path
// description: username of the collaborator
// description: username of the user to check for being a collaborator
// type: string
// required: true
// responses:
@@ -145,7 +145,7 @@ func AddOrUpdateCollaborator(ctx *context.APIContext) {
// required: true
// - name: collaborator
// in: path
// description: username of the collaborator to add
// description: username of the user to add or update as a collaborator
// type: string
// required: true
// - name: body
@@ -264,7 +264,7 @@ func GetRepoPermissions(ctx *context.APIContext) {
// required: true
// - name: collaborator
// in: path
// description: username of the collaborator
// description: username of the collaborator whose permissions are to be obtained
// type: string
// required: true
// responses:
@@ -276,7 +276,7 @@ func GetRepoPermissions(ctx *context.APIContext) {
// "$ref": "#/responses/forbidden"
collaboratorUsername := ctx.PathParam("collaborator")
if !ctx.Doer.IsAdmin && ctx.Doer.LowerName != strings.ToLower(collaboratorUsername) && !ctx.IsUserRepoAdmin() {
if !ctx.Doer.IsAdmin && !strings.EqualFold(ctx.Doer.LowerName, collaboratorUsername) && !ctx.IsUserRepoAdmin() {
ctx.APIError(http.StatusForbidden, "Only admins can query all permissions, repo admins can query all repo permissions, collaborators can query only their own")
return
}
+16 -3
View File
@@ -812,7 +812,8 @@ func GetContentsExt(ctx *context.APIContext) {
// required: true
// - name: filepath
// in: path
// description: path of the dir, file, symlink or submodule in the repo
// description: path of the dir, file, symlink or submodule in the repo. Swagger requires path parameter to be "required",
// you can leave it empty or pass a single dot (".") to get the root directory.
// type: string
// required: true
// - name: ref
@@ -823,7 +824,8 @@ func GetContentsExt(ctx *context.APIContext) {
// - name: includes
// in: query
// description: By default this API's response only contains file's metadata. Use comma-separated "includes" options to retrieve more fields.
// Option "file_content" will try to retrieve the file content, option "lfs_metadata" will try to retrieve LFS metadata.
// Option "file_content" will try to retrieve the file content, "lfs_metadata" will try to retrieve LFS metadata,
// "commit_metadata" will try to retrieve commit metadata, and "commit_message" will try to retrieve commit message.
// type: string
// required: false
// responses:
@@ -832,6 +834,9 @@ func GetContentsExt(ctx *context.APIContext) {
// "404":
// "$ref": "#/responses/notFound"
if treePath := ctx.PathParam("*"); treePath == "." || treePath == "/" {
ctx.SetPathParam("*", "") // workaround for swagger, it requires path parameter to be "required", but we need to list root directory
}
opts := files_service.GetContentsOrListOptions{TreePath: ctx.PathParam("*")}
for includeOpt := range strings.SplitSeq(ctx.FormString("includes"), ",") {
if includeOpt == "" {
@@ -842,6 +847,10 @@ func GetContentsExt(ctx *context.APIContext) {
opts.IncludeSingleFileContent = true
case "lfs_metadata":
opts.IncludeLfsMetadata = true
case "commit_metadata":
opts.IncludeCommitMetadata = true
case "commit_message":
opts.IncludeCommitMessage = true
default:
ctx.APIError(http.StatusBadRequest, fmt.Sprintf("unknown include option %q", includeOpt))
return
@@ -883,7 +892,11 @@ func GetContents(ctx *context.APIContext) {
// "$ref": "#/responses/ContentsResponse"
// "404":
// "$ref": "#/responses/notFound"
ret := getRepoContents(ctx, files_service.GetContentsOrListOptions{TreePath: ctx.PathParam("*"), IncludeSingleFileContent: true})
ret := getRepoContents(ctx, files_service.GetContentsOrListOptions{
TreePath: ctx.PathParam("*"),
IncludeSingleFileContent: true,
IncludeCommitMetadata: true,
})
if ctx.Written() {
return
}
+2 -2
View File
@@ -43,7 +43,7 @@ func AddIssueSubscription(ctx *context.APIContext) {
// required: true
// - name: user
// in: path
// description: user to subscribe
// description: username of the user to subscribe the issue to
// type: string
// required: true
// responses:
@@ -87,7 +87,7 @@ func DelIssueSubscription(ctx *context.APIContext) {
// required: true
// - name: user
// in: path
// description: user witch unsubscribe
// description: username of the user to unsubscribe from an issue
// type: string
// required: true
// responses:
+1 -1
View File
@@ -405,7 +405,7 @@ func ListTrackedTimesByUser(ctx *context.APIContext) {
// required: true
// - name: user
// in: path
// description: username of user
// description: username of the user whose tracked times are to be listed
// type: string
// required: true
// responses:
+1 -1
View File
@@ -669,7 +669,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
newRepoName = *opts.Name
}
// Check if repository name has been changed and not just a case change
if repo.LowerName != strings.ToLower(newRepoName) {
if !strings.EqualFold(repo.LowerName, newRepoName) {
if err := repo_service.ChangeRepositoryName(ctx, ctx.Doer, repo, newRepoName); err != nil {
switch {
case repo_model.IsErrRepoAlreadyExist(err):
+1 -1
View File
@@ -44,5 +44,5 @@ type swaggerResponseActionWorkflow struct {
// swagger:response ActionWorkflowList
type swaggerResponseActionWorkflowList struct {
// in:body
Body []api.ActionWorkflow `json:"body"`
Body api.ActionWorkflowResponse `json:"body"`
}
+3 -3
View File
@@ -30,7 +30,7 @@ func ListAccessTokens(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user
// description: username of to user whose access tokens are to be listed
// type: string
// required: true
// - name: page
@@ -83,7 +83,7 @@ func CreateAccessToken(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user
// description: username of the user whose token is to be created
// required: true
// type: string
// - name: body
@@ -149,7 +149,7 @@ func DeleteAccessToken(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user
// description: username of the user whose token is to be deleted
// type: string
// required: true
// - name: token
+3 -3
View File
@@ -37,7 +37,7 @@ func CheckUserBlock(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: user to check
// description: username of the user to check
// type: string
// required: true
// responses:
@@ -56,7 +56,7 @@ func BlockUser(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: user to block
// description: username of the user to block
// type: string
// required: true
// - name: note
@@ -81,7 +81,7 @@ func UnblockUser(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: user to unblock
// description: username of the user to unblock
// type: string
// required: true
// responses:
+7 -7
View File
@@ -67,7 +67,7 @@ func ListFollowers(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user
// description: username of the user whose followers are to be listed
// type: string
// required: true
// - name: page
@@ -131,7 +131,7 @@ func ListFollowing(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user
// description: username of the user whose followed users are to be listed
// type: string
// required: true
// - name: page
@@ -167,7 +167,7 @@ func CheckMyFollowing(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of followed user
// description: username of the user to check for authenticated followers
// type: string
// required: true
// responses:
@@ -187,12 +187,12 @@ func CheckFollowing(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of following user
// description: username of the following user
// type: string
// required: true
// - name: target
// in: path
// description: username of followed user
// description: username of the followed user
// type: string
// required: true
// responses:
@@ -216,7 +216,7 @@ func Follow(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user to follow
// description: username of the user to follow
// type: string
// required: true
// responses:
@@ -246,7 +246,7 @@ func Unfollow(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user to unfollow
// description: username of the user to unfollow
// type: string
// required: true
// responses:
+1 -1
View File
@@ -53,7 +53,7 @@ func ListGPGKeys(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user
// description: username of the user whose GPG key list is to be obtained
// type: string
// required: true
// - name: page
+1 -1
View File
@@ -136,7 +136,7 @@ func ListPublicKeys(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user
// description: username of the user whose public keys are to be listed
// type: string
// required: true
// - name: fingerprint
+1 -1
View File
@@ -62,7 +62,7 @@ func ListUserRepos(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user
// description: username of the user whose owned repos are to be listed
// type: string
// required: true
// - name: page
+1 -1
View File
@@ -50,7 +50,7 @@ func GetStarredRepos(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user
// description: username of the user whose starred repos are to be listed
// type: string
// required: true
// - name: page
+3 -3
View File
@@ -110,7 +110,7 @@ func GetInfo(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user to get
// description: username of the user whose data is to be listed
// type: string
// required: true
// responses:
@@ -151,7 +151,7 @@ func GetUserHeatmapData(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user to get
// description: username of the user whose heatmap is to be obtained
// type: string
// required: true
// responses:
@@ -177,7 +177,7 @@ func ListUserActivityFeeds(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
// description: username of user
// description: username of the user whose activity feeds are to be listed
// type: string
// required: true
// - name: only-performed-by
+1 -1
View File
@@ -49,7 +49,7 @@ func GetWatchedRepos(ctx *context.APIContext) {
// - name: username
// type: string
// in: path
// description: username of the user
// description: username of the user whose watched repos are to be listed
// required: true
// - name: page
// in: query