mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-13 06:32:16 +09:00
Backport #39681 by @silverwind 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> Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
@@ -12,6 +12,7 @@ import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -343,7 +344,9 @@ func parseUploadPackage(upload *packageUpload) (*Package, error) {
|
||||
p.DistTags = append(p.DistTags, tag)
|
||||
}
|
||||
|
||||
p.Filename = strings.ToLower(fmt.Sprintf("%s-%s.tgz", name, p.Version))
|
||||
filename := fmt.Sprintf("%s-%s.tgz", name, p.Version)
|
||||
p.Filename = strings.ToLower(filename)
|
||||
p.Metadata.GiteaTarballPath = util.PathEscapeSegments(p.Name) + "/-/" + url.PathEscape(filename) // canonical URL clients derive from name and version, file lookups ignore case
|
||||
|
||||
attachment := upload.Attachments[meta.Name+"-"+meta.Version+".tgz"] // not the sigstore bundle of `npm publish --provenance`
|
||||
if attachment == nil && len(upload.Attachments) == 1 {
|
||||
|
||||
@@ -23,7 +23,7 @@ func TestParsePackage(t *testing.T) {
|
||||
packageScope := "@scope"
|
||||
packageName := "test-package"
|
||||
packageFullName := packageScope + "/" + packageName
|
||||
packageVersion := "1.0.1-pre"
|
||||
packageVersion := "1.0.1-Pre"
|
||||
packageTag := "latest"
|
||||
packageAuthor := "KN4CK3R"
|
||||
packageBin := "gitea"
|
||||
@@ -287,7 +287,8 @@ func TestParsePackage(t *testing.T) {
|
||||
assert.Equal(t, packageFullName, p.Name)
|
||||
assert.Equal(t, packageVersion, p.Version)
|
||||
assert.Equal(t, []string{packageTag}, p.DistTags)
|
||||
assert.Equal(t, fmt.Sprintf("%s-%s.tgz", strings.Split(packageFullName, "/")[1], packageVersion), p.Filename)
|
||||
assert.Equal(t, strings.ToLower(fmt.Sprintf("%s-%s.tgz", packageName, packageVersion)), p.Filename)
|
||||
assert.Equal(t, fmt.Sprintf("%s/-/%s-%s.tgz", packageFullName, packageName, packageVersion), p.Metadata.GiteaTarballPath)
|
||||
b, _ = base64.StdEncoding.DecodeString(data)
|
||||
assert.Equal(t, b, p.Data)
|
||||
assert.Equal(t, packageName, p.Metadata.Name)
|
||||
|
||||
@@ -34,4 +34,6 @@ type Metadata struct {
|
||||
Funding any `json:"funding,omitempty"`
|
||||
AcceptDependencies map[string]string `json:"accept_dependencies,omitempty"`
|
||||
Deprecated string `json:"deprecated,omitempty"`
|
||||
|
||||
GiteaTarballPath string `json:"gitea_tarball_path,omitempty"` // Gitea-internal, fixed at upload because lockfiles pin it
|
||||
}
|
||||
|
||||
@@ -414,7 +414,7 @@ func CommonRoutes() *web.Router {
|
||||
g.UseUnescapedPath()
|
||||
g.MatchPath("DELETE", packageId+"/-/<version>/<filename>/-rev/<revision>", reqPackageAccess(perm.AccessModeWrite), npm.DeletePackageVersion)
|
||||
g.MatchPath("DELETE", packageId+"/-/<filename>/-rev/<revision>", reqPackageAccess(perm.AccessModeWrite), npm.DeletePackageVersion)
|
||||
g.MatchPath("GET", packageId+"/-/<version>/<filename>", npm.DownloadPackageFileByName) // former tarball URL, still in lockfiles
|
||||
g.MatchPath("GET", packageId+"/-/<version>/<filename>", npm.DownloadPackageFileByName) // 1.27 and 28.0 URL, still advertised for older uploads
|
||||
g.MatchPath("GET", packageId+"/-/<filename>", npm.DownloadPackageFileByName)
|
||||
g.MatchPath("DELETE", packageId+"/-rev/<revision>", reqPackageAccess(perm.AccessModeWrite), npm.DeletePackage)
|
||||
g.MatchPath("PUT", packageId+"/-rev/<revision>", reqPackageAccess(perm.AccessModeWrite), npm.DeletePreview)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"slices"
|
||||
"sort"
|
||||
"time"
|
||||
@@ -82,6 +83,11 @@ func createPackageMetadataVersion(registryURL string, pd *packages_model.Package
|
||||
|
||||
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,
|
||||
@@ -113,7 +119,7 @@ func createPackageMetadataVersion(registryURL string, pd *packages_model.Package
|
||||
Dist: npm_module.PackageDistribution{
|
||||
Shasum: pd.Files[0].Blob.HashSHA1,
|
||||
Integrity: "sha512-" + base64.StdEncoding.EncodeToString(hashBytes),
|
||||
Tarball: fmt.Sprintf("%s/%s/-/%s", registryURL, pd.Package.Name, pd.Files[0].File.LowerName), // npmjs shape, which npm parses for allowScripts and yarn keeps registry-relative
|
||||
Tarball: registryURL + "/" + tarballPath,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,13 @@ import (
|
||||
|
||||
func TestCreatePackageMetadataResponse(t *testing.T) {
|
||||
repository := npm_module.Repository{Type: "git", URL: "https://gitea.dev/alice/test.git"}
|
||||
descriptor := func(v string, publishedUnix int64, repo npm_module.Repository) *packages_model.PackageDescriptor {
|
||||
descriptor := func(v string, publishedUnix int64, repo npm_module.Repository, tarballPath string) *packages_model.PackageDescriptor {
|
||||
return &packages_model.PackageDescriptor{
|
||||
Package: &packages_model.Package{Name: "@scope/test"},
|
||||
Owner: &user_model.User{Name: "alice"},
|
||||
Version: &packages_model.PackageVersion{Version: v, CreatedUnix: timeutil.TimeStamp(publishedUnix)},
|
||||
SemVer: version.Must(version.NewVersion(v)),
|
||||
Metadata: &npm_module.Metadata{Readme: v, Keywords: []string{"gitea"}, Repository: repo},
|
||||
Metadata: &npm_module.Metadata{Readme: v, Keywords: []string{"gitea"}, Repository: repo, GiteaTarballPath: tarballPath},
|
||||
Files: []*packages_model.PackageFileDescriptor{{
|
||||
File: &packages_model.PackageFile{LowerName: "test-" + v + ".tgz"},
|
||||
Blob: &packages_model.PackageBlob{},
|
||||
@@ -34,9 +34,9 @@ func TestCreatePackageMetadataResponse(t *testing.T) {
|
||||
}
|
||||
|
||||
result := createPackageMetadataResponse("https://gitea.dev/api/packages/alice/npm", []*packages_model.PackageDescriptor{
|
||||
descriptor("1.1.0", 1000, npm_module.Repository{}),
|
||||
descriptor("2.0.0-rc.1", 1500, repository),
|
||||
descriptor("1.0.0", 2000, repository),
|
||||
descriptor("1.1.0", 1000, npm_module.Repository{}, "@scope/test/-/test-1.1.0.tgz"),
|
||||
descriptor("2.0.0-rc.1", 1500, repository, ""),
|
||||
descriptor("1.0.0", 2000, repository, ""),
|
||||
})
|
||||
|
||||
assert.Equal(t, map[string]time.Time{
|
||||
@@ -54,9 +54,13 @@ func TestCreatePackageMetadataResponse(t *testing.T) {
|
||||
assert.Equal(t, []string{"gitea"}, result.Versions["1.0.0"].Keywords)
|
||||
assert.Equal(t, []npm_module.User{{Name: "alice"}}, result.Versions["1.0.0"].Maintainers)
|
||||
assert.Equal(t,
|
||||
"https://gitea.dev/api/packages/alice/npm/@scope/test/-/test-1.0.0.tgz",
|
||||
"https://gitea.dev/api/packages/alice/npm/%40scope%2Ftest/-/1.0.0/test-1.0.0.tgz",
|
||||
result.Versions["1.0.0"].Dist.Tarball,
|
||||
)
|
||||
assert.Equal(t,
|
||||
"https://gitea.dev/api/packages/alice/npm/@scope/test/-/test-1.1.0.tgz",
|
||||
result.Versions["1.1.0"].Dist.Tarball,
|
||||
)
|
||||
assert.Equal(t, repository, result.Versions["1.0.0"].Repository)
|
||||
|
||||
withoutRepository, err := json.Marshal(result.Versions["1.1.0"])
|
||||
|
||||
Reference in New Issue
Block a user