Skip to content

Commit 5cd1d6c

Browse files
authored
Set repository link based on the url in package.json for npm packages (#20379)
automatically set repository link for package based on the repository url present inside package.json closes #20146
1 parent 3cab9c6 commit 5cd1d6c

File tree

3 files changed

+135
-0
lines changed

3 files changed

+135
-0
lines changed

models/repo/repo.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,49 @@ func GetRepositoryByName(ownerID int64, name string) (*Repository, error) {
658658
return repo, err
659659
}
660660

661+
// getRepositoryURLPathSegments returns segments (owner, reponame) extracted from a url
662+
func getRepositoryURLPathSegments(repoURL string) []string {
663+
if strings.HasPrefix(repoURL, setting.AppURL) {
664+
return strings.Split(strings.TrimPrefix(repoURL, setting.AppURL), "/")
665+
}
666+
667+
sshURLVariants := [4]string{
668+
setting.SSH.Domain + ":",
669+
setting.SSH.User + "@" + setting.SSH.Domain + ":",
670+
"git+ssh://" + setting.SSH.Domain + "/",
671+
"git+ssh://" + setting.SSH.User + "@" + setting.SSH.Domain + "/",
672+
}
673+
674+
for _, sshURL := range sshURLVariants {
675+
if strings.HasPrefix(repoURL, sshURL) {
676+
return strings.Split(strings.TrimPrefix(repoURL, sshURL), "/")
677+
}
678+
}
679+
680+
return nil
681+
}
682+
683+
// GetRepositoryByURL returns the repository by given url
684+
func GetRepositoryByURL(ctx context.Context, repoURL string) (*Repository, error) {
685+
// possible urls for git:
686+
// https://my.domain/sub-path/<owner>/<repo>.git
687+
// https://my.domain/sub-path/<owner>/<repo>
688+
// git+ssh://[email protected]/<owner>/<repo>.git
689+
// git+ssh://[email protected]/<owner>/<repo>
690+
// [email protected]:<owner>/<repo>.git
691+
// [email protected]:<owner>/<repo>
692+
693+
pathSegments := getRepositoryURLPathSegments(repoURL)
694+
695+
if len(pathSegments) != 2 {
696+
return nil, fmt.Errorf("unknown or malformed repository URL")
697+
}
698+
699+
ownerName := pathSegments[0]
700+
repoName := strings.TrimSuffix(pathSegments[1], ".git")
701+
return GetRepositoryByOwnerAndName(ctx, ownerName, repoName)
702+
}
703+
661704
// GetRepositoryByID returns the repository by given id if exists.
662705
func GetRepositoryByID(ctx context.Context, id int64) (*Repository, error) {
663706
repo := new(Repository)

models/repo/repo_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,65 @@ func TestMetas(t *testing.T) {
124124
assert.Equal(t, "user3", metas["org"])
125125
assert.Equal(t, ",owners,team1,", metas["teams"])
126126
}
127+
128+
func TestGetRepositoryByURL(t *testing.T) {
129+
assert.NoError(t, unittest.PrepareTestDatabase())
130+
131+
t.Run("InvalidPath", func(t *testing.T) {
132+
repo, err := repo_model.GetRepositoryByURL(db.DefaultContext, "something")
133+
134+
assert.Nil(t, repo)
135+
assert.Error(t, err)
136+
})
137+
138+
t.Run("ValidHttpURL", func(t *testing.T) {
139+
test := func(t *testing.T, url string) {
140+
repo, err := repo_model.GetRepositoryByURL(db.DefaultContext, url)
141+
142+
assert.NotNil(t, repo)
143+
assert.NoError(t, err)
144+
145+
assert.Equal(t, repo.ID, int64(2))
146+
assert.Equal(t, repo.OwnerID, int64(2))
147+
}
148+
149+
test(t, "https://try.gitea.io/user2/repo2")
150+
test(t, "https://try.gitea.io/user2/repo2.git")
151+
})
152+
153+
t.Run("ValidGitSshURL", func(t *testing.T) {
154+
test := func(t *testing.T, url string) {
155+
repo, err := repo_model.GetRepositoryByURL(db.DefaultContext, url)
156+
157+
assert.NotNil(t, repo)
158+
assert.NoError(t, err)
159+
160+
assert.Equal(t, repo.ID, int64(2))
161+
assert.Equal(t, repo.OwnerID, int64(2))
162+
}
163+
164+
test(t, "git+ssh://[email protected]/user2/repo2")
165+
test(t, "git+ssh://[email protected]/user2/repo2.git")
166+
167+
test(t, "git+ssh://try.gitea.io/user2/repo2")
168+
test(t, "git+ssh://try.gitea.io/user2/repo2.git")
169+
})
170+
171+
t.Run("ValidImplicitSshURL", func(t *testing.T) {
172+
test := func(t *testing.T, url string) {
173+
repo, err := repo_model.GetRepositoryByURL(db.DefaultContext, url)
174+
175+
assert.NotNil(t, repo)
176+
assert.NoError(t, err)
177+
178+
assert.Equal(t, repo.ID, int64(2))
179+
assert.Equal(t, repo.OwnerID, int64(2))
180+
}
181+
182+
test(t, "[email protected]:user2/repo2")
183+
test(t, "[email protected]:user2/repo2.git")
184+
185+
test(t, "try.gitea.io:user2/repo2")
186+
test(t, "try.gitea.io:user2/repo2.git")
187+
})
188+
}

routers/api/packages/npm/npm.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ import (
1313

1414
"code.gitea.io/gitea/models/db"
1515
packages_model "code.gitea.io/gitea/models/packages"
16+
access_model "code.gitea.io/gitea/models/perm/access"
17+
repo_model "code.gitea.io/gitea/models/repo"
18+
"code.gitea.io/gitea/models/unit"
1619
"code.gitea.io/gitea/modules/context"
1720
packages_module "code.gitea.io/gitea/modules/packages"
1821
npm_module "code.gitea.io/gitea/modules/packages/npm"
@@ -166,6 +169,26 @@ func UploadPackage(ctx *context.Context) {
166169
return
167170
}
168171

172+
repo, err := repo_model.GetRepositoryByURL(ctx, npmPackage.Metadata.Repository.URL)
173+
if err == nil {
174+
canWrite := repo.OwnerID == ctx.Doer.ID
175+
176+
if !canWrite {
177+
perms, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
178+
if err != nil {
179+
apiError(ctx, http.StatusInternalServerError, err)
180+
return
181+
}
182+
183+
canWrite = perms.CanWrite(unit.TypePackages)
184+
}
185+
186+
if !canWrite {
187+
apiError(ctx, http.StatusForbidden, "no permission to upload this package")
188+
return
189+
}
190+
}
191+
169192
buf, err := packages_module.CreateHashedBufferFromReader(bytes.NewReader(npmPackage.Data), 32*1024*1024)
170193
if err != nil {
171194
apiError(ctx, http.StatusInternalServerError, err)
@@ -217,6 +240,13 @@ func UploadPackage(ctx *context.Context) {
217240
}
218241
}
219242

243+
if repo != nil {
244+
if err := packages_model.SetRepositoryLink(ctx, pv.PackageID, repo.ID); err != nil {
245+
apiError(ctx, http.StatusInternalServerError, err)
246+
return
247+
}
248+
}
249+
220250
ctx.Status(http.StatusCreated)
221251
}
222252

0 commit comments

Comments
 (0)