mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	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
This commit is contained in:
		| @@ -658,6 +658,49 @@ func GetRepositoryByName(ownerID int64, name string) (*Repository, error) { | ||||
| 	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. | ||||
| func GetRepositoryByID(ctx context.Context, id int64) (*Repository, error) { | ||||
| 	repo := new(Repository) | ||||
|   | ||||
| @@ -124,3 +124,65 @@ func TestMetas(t *testing.T) { | ||||
| 	assert.Equal(t, "user3", metas["org"]) | ||||
| 	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") | ||||
| 	}) | ||||
| } | ||||
|   | ||||
| @@ -13,6 +13,9 @@ import ( | ||||
|  | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	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" | ||||
| 	packages_module "code.gitea.io/gitea/modules/packages" | ||||
| 	npm_module "code.gitea.io/gitea/modules/packages/npm" | ||||
| @@ -166,6 +169,26 @@ func UploadPackage(ctx *context.Context) { | ||||
| 		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) | ||||
| 	if err != nil { | ||||
| 		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) | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user