Set repository link based on the url in package.json for npm packages ()

automatically set repository link for package based on the repository
url present inside package.json

closes 
This commit is contained in:
Mai-Lapyst 2023-03-28 19:55:03 +02:00 committed by GitHub
parent 3cab9c6b0c
commit 5cd1d6c93b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 135 additions and 0 deletions
models/repo
routers/api/packages/npm

View file

@ -658,6 +658,49 @@ func GetRepositoryByName(ownerID int64, name string) (*Repository, error) {
return repo, err return repo, err
} }
// getRepositoryURLPathSegments returns segments (owner, reponame) extracted from a url
func getRepositoryURLPathSegments(repoURL string) []string {
if strings.HasPrefix(repoURL, setting.AppURL) {
return strings.Split(strings.TrimPrefix(repoURL, setting.AppURL), "/")
}
sshURLVariants := [4]string{
setting.SSH.Domain + ":",
setting.SSH.User + "@" + setting.SSH.Domain + ":",
"git+ssh://" + setting.SSH.Domain + "/",
"git+ssh://" + setting.SSH.User + "@" + setting.SSH.Domain + "/",
}
for _, sshURL := range sshURLVariants {
if strings.HasPrefix(repoURL, sshURL) {
return strings.Split(strings.TrimPrefix(repoURL, sshURL), "/")
}
}
return nil
}
// GetRepositoryByURL returns the repository by given url
func GetRepositoryByURL(ctx context.Context, repoURL string) (*Repository, error) {
// possible urls for git:
// https://my.domain/sub-path/<owner>/<repo>.git
// https://my.domain/sub-path/<owner>/<repo>
// git+ssh://user@my.domain/<owner>/<repo>.git
// git+ssh://user@my.domain/<owner>/<repo>
// user@my.domain:<owner>/<repo>.git
// user@my.domain:<owner>/<repo>
pathSegments := getRepositoryURLPathSegments(repoURL)
if len(pathSegments) != 2 {
return nil, fmt.Errorf("unknown or malformed repository URL")
}
ownerName := pathSegments[0]
repoName := strings.TrimSuffix(pathSegments[1], ".git")
return GetRepositoryByOwnerAndName(ctx, ownerName, repoName)
}
// GetRepositoryByID returns the repository by given id if exists. // GetRepositoryByID returns the repository by given id if exists.
func GetRepositoryByID(ctx context.Context, id int64) (*Repository, error) { func GetRepositoryByID(ctx context.Context, id int64) (*Repository, error) {
repo := new(Repository) repo := new(Repository)

View file

@ -124,3 +124,65 @@ func TestMetas(t *testing.T) {
assert.Equal(t, "user3", metas["org"]) assert.Equal(t, "user3", metas["org"])
assert.Equal(t, ",owners,team1,", metas["teams"]) assert.Equal(t, ",owners,team1,", metas["teams"])
} }
func TestGetRepositoryByURL(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
t.Run("InvalidPath", func(t *testing.T) {
repo, err := repo_model.GetRepositoryByURL(db.DefaultContext, "something")
assert.Nil(t, repo)
assert.Error(t, err)
})
t.Run("ValidHttpURL", func(t *testing.T) {
test := func(t *testing.T, url string) {
repo, err := repo_model.GetRepositoryByURL(db.DefaultContext, url)
assert.NotNil(t, repo)
assert.NoError(t, err)
assert.Equal(t, repo.ID, int64(2))
assert.Equal(t, repo.OwnerID, int64(2))
}
test(t, "https://try.gitea.io/user2/repo2")
test(t, "https://try.gitea.io/user2/repo2.git")
})
t.Run("ValidGitSshURL", func(t *testing.T) {
test := func(t *testing.T, url string) {
repo, err := repo_model.GetRepositoryByURL(db.DefaultContext, url)
assert.NotNil(t, repo)
assert.NoError(t, err)
assert.Equal(t, repo.ID, int64(2))
assert.Equal(t, repo.OwnerID, int64(2))
}
test(t, "git+ssh://sshuser@try.gitea.io/user2/repo2")
test(t, "git+ssh://sshuser@try.gitea.io/user2/repo2.git")
test(t, "git+ssh://try.gitea.io/user2/repo2")
test(t, "git+ssh://try.gitea.io/user2/repo2.git")
})
t.Run("ValidImplicitSshURL", func(t *testing.T) {
test := func(t *testing.T, url string) {
repo, err := repo_model.GetRepositoryByURL(db.DefaultContext, url)
assert.NotNil(t, repo)
assert.NoError(t, err)
assert.Equal(t, repo.ID, int64(2))
assert.Equal(t, repo.OwnerID, int64(2))
}
test(t, "sshuser@try.gitea.io:user2/repo2")
test(t, "sshuser@try.gitea.io:user2/repo2.git")
test(t, "try.gitea.io:user2/repo2")
test(t, "try.gitea.io:user2/repo2.git")
})
}

View file

@ -13,6 +13,9 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages" packages_model "code.gitea.io/gitea/models/packages"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
packages_module "code.gitea.io/gitea/modules/packages" packages_module "code.gitea.io/gitea/modules/packages"
npm_module "code.gitea.io/gitea/modules/packages/npm" npm_module "code.gitea.io/gitea/modules/packages/npm"
@ -166,6 +169,26 @@ func UploadPackage(ctx *context.Context) {
return return
} }
repo, err := repo_model.GetRepositoryByURL(ctx, npmPackage.Metadata.Repository.URL)
if err == nil {
canWrite := repo.OwnerID == ctx.Doer.ID
if !canWrite {
perms, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
canWrite = perms.CanWrite(unit.TypePackages)
}
if !canWrite {
apiError(ctx, http.StatusForbidden, "no permission to upload this package")
return
}
}
buf, err := packages_module.CreateHashedBufferFromReader(bytes.NewReader(npmPackage.Data), 32*1024*1024) buf, err := packages_module.CreateHashedBufferFromReader(bytes.NewReader(npmPackage.Data), 32*1024*1024)
if err != nil { if err != nil {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
@ -217,6 +240,13 @@ func UploadPackage(ctx *context.Context) {
} }
} }
if repo != nil {
if err := packages_model.SetRepositoryLink(ctx, pv.PackageID, repo.ID); err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
}
ctx.Status(http.StatusCreated) ctx.Status(http.StatusCreated)
} }