mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-14 18:33:42 +02:00
Gitea changed the `dist.tarball` it advertises for already published versions twice: | Gitea | `dist.tarball` path of `@scope/name@1.0.0` | |---|---| | 1.27 | `%40scope%2Fname/-/1.0.0/name-1.0.0.tgz` | | 28.0, https://github.com/go-gitea/gitea/pull/39061 | `@scope%2Fname/-/1.0.0/name-1.0.0.tgz` | | 28.1, https://github.com/go-gitea/gitea/pull/39434 | `@scope/name/-/name-1.0.0.tgz` | pnpm 11.5.2+ verifies the tarball URLs pinned in its lockfile against the registry metadata, so frozen installs of lockfiles written against 1.27 fail with `ERR_PNPM_TARBALL_URL_MISMATCH`. - Versions uploaded before this PR get their 1.27 URL back. - New versions store their canonical tarball path at upload, so later URL changes can't reach published versions. The host still follows the request. Lockfiles written against 28.0 for scoped packages, or by pnpm 12 with `@scope:registry` against 28.1, need those pinned `tarball:` entries removed, as 1.27 lockfiles of the same versions need the other URL. Signed-off-by: silverwind <me@silverwind.io>
161 lines
5.5 KiB
Go
161 lines
5.5 KiB
Go
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package npm
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"net/url"
|
|
"slices"
|
|
"sort"
|
|
"time"
|
|
|
|
packages_model "gitea.dev/models/packages"
|
|
npm_module "gitea.dev/modules/packages/npm"
|
|
)
|
|
|
|
func createPackageMetadataResponse(registryURL string, pds []*packages_model.PackageDescriptor) *npm_module.PackageMetadata {
|
|
sort.Slice(pds, func(i, j int) bool {
|
|
return pds[i].SemVer.LessThan(pds[j].SemVer)
|
|
})
|
|
|
|
versions := make(map[string]*npm_module.PackageMetadataVersion)
|
|
distTags := make(map[string]string)
|
|
times := make(map[string]time.Time)
|
|
firstPublished, lastPublished := pds[0].Version.CreatedUnix, pds[0].Version.CreatedUnix
|
|
var latest *packages_model.PackageDescriptor
|
|
for _, pd := range pds {
|
|
semVer := pd.SemVer.String()
|
|
versions[semVer] = createPackageMetadataVersion(registryURL, pd)
|
|
times[semVer] = pd.Version.CreatedUnix.AsTimeInLocation(time.UTC)
|
|
firstPublished = min(firstPublished, pd.Version.CreatedUnix)
|
|
lastPublished = max(lastPublished, pd.Version.CreatedUnix)
|
|
|
|
for _, pvp := range pd.VersionProperties {
|
|
if pvp.Name == npm_module.TagProperty {
|
|
distTags[pvp.Value] = pd.Version.Version
|
|
if pvp.Value == "latest" {
|
|
latest = pd
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// npm derives both from the versions currently served, so a deletion moves them
|
|
times["created"] = firstPublished.AsTimeInLocation(time.UTC)
|
|
times["modified"] = lastPublished.AsTimeInLocation(time.UTC)
|
|
|
|
if latest == nil { // yarn and pnpm fail without it, e.g. after its version got deleted
|
|
latest = pds[len(pds)-1]
|
|
for _, pd := range slices.Backward(pds) {
|
|
if pd.SemVer.Prerelease() == "" {
|
|
latest = pd
|
|
break
|
|
}
|
|
}
|
|
distTags["latest"] = latest.Version.Version
|
|
}
|
|
|
|
metadata := packages_model.DescriptorMetadata[*npm_module.Metadata](latest)
|
|
|
|
return &npm_module.PackageMetadata{
|
|
ID: latest.Package.Name,
|
|
Name: latest.Package.Name,
|
|
DistTags: distTags,
|
|
Description: metadata.Description,
|
|
Readme: metadata.Readme,
|
|
Maintainers: []npm_module.User{{Name: latest.Owner.Name}},
|
|
Time: times,
|
|
Homepage: metadata.ProjectURL,
|
|
Keywords: metadata.Keywords,
|
|
Author: npm_module.User{Name: metadata.Author},
|
|
License: metadata.License,
|
|
Versions: versions,
|
|
Repository: metadata.Repository,
|
|
}
|
|
}
|
|
|
|
func createPackageMetadataVersion(registryURL string, pd *packages_model.PackageDescriptor) *npm_module.PackageMetadataVersion {
|
|
hashBytes, _ := hex.DecodeString(pd.Files[0].Blob.HashSHA512)
|
|
|
|
metadata := packages_model.DescriptorMetadata[*npm_module.Metadata](pd)
|
|
|
|
tarballPath := metadata.GiteaTarballPath
|
|
if tarballPath == "" {
|
|
tarballPath = fmt.Sprintf("%s/-/%s/%s", url.QueryEscape(pd.Package.Name), url.PathEscape(pd.Version.Version), url.PathEscape(pd.Files[0].File.LowerName)) // 1.27 URL, pinned by older lockfiles
|
|
}
|
|
|
|
return &npm_module.PackageMetadataVersion{
|
|
ID: fmt.Sprintf("%s@%s", pd.Package.Name, pd.Version.Version),
|
|
Name: pd.Package.Name,
|
|
Version: pd.Version.Version,
|
|
Description: metadata.Description,
|
|
Author: npm_module.User{Name: metadata.Author},
|
|
Maintainers: []npm_module.User{{Name: pd.Owner.Name}},
|
|
Homepage: metadata.ProjectURL,
|
|
License: metadata.License,
|
|
Repository: metadata.Repository,
|
|
Keywords: metadata.Keywords,
|
|
Dependencies: metadata.Dependencies,
|
|
BundleDependencies: metadata.BundleDependencies,
|
|
DevDependencies: metadata.DevelopmentDependencies,
|
|
PeerDependencies: metadata.PeerDependencies,
|
|
PeerDependenciesMeta: metadata.PeerDependenciesMeta,
|
|
OptionalDependencies: metadata.OptionalDependencies,
|
|
Bin: metadata.Bin,
|
|
HasInstallScript: metadata.HasInstallScript,
|
|
HasShrinkwrap: metadata.HasShrinkwrap,
|
|
Engines: metadata.Engines,
|
|
CPU: metadata.CPU,
|
|
OS: metadata.OS,
|
|
Libc: metadata.Libc,
|
|
Directories: metadata.Directories,
|
|
Funding: metadata.Funding,
|
|
AcceptDependencies: metadata.AcceptDependencies,
|
|
Deprecated: metadata.Deprecated,
|
|
Dist: npm_module.PackageDistribution{
|
|
Shasum: pd.Files[0].Blob.HashSHA1,
|
|
Integrity: "sha512-" + base64.StdEncoding.EncodeToString(hashBytes),
|
|
Tarball: registryURL + "/" + tarballPath,
|
|
},
|
|
}
|
|
}
|
|
|
|
func createPackageSearchResponse(ctx context.Context, pds []*packages_model.PackageDescriptor, total int64) *npm_module.PackageSearch {
|
|
objects := make([]*npm_module.PackageSearchObject, 0, len(pds))
|
|
for _, pd := range pds {
|
|
metadata := packages_model.DescriptorMetadata[*npm_module.Metadata](pd)
|
|
|
|
scope := metadata.Scope
|
|
if scope == "" {
|
|
scope = "unscoped"
|
|
}
|
|
|
|
objects = append(objects, &npm_module.PackageSearchObject{
|
|
Package: &npm_module.PackageSearchPackage{
|
|
Scope: scope,
|
|
Name: metadata.Name,
|
|
Version: pd.Version.Version,
|
|
Date: pd.Version.CreatedUnix.AsLocalTime(),
|
|
Description: metadata.Description,
|
|
Author: npm_module.User{Name: metadata.Author},
|
|
Publisher: npm_module.User{Name: pd.Owner.Name},
|
|
Maintainers: []npm_module.User{}, // npm cli needs this field
|
|
Keywords: metadata.Keywords,
|
|
Links: &npm_module.PackageSearchPackageLinks{
|
|
Registry: buildNpmRegistryURL(ctx, pd.Owner),
|
|
Homepage: metadata.ProjectURL,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
return &npm_module.PackageSearch{
|
|
Objects: objects,
|
|
Total: total,
|
|
}
|
|
}
|