Merge branch 'main' into xormigrate

This commit is contained in:
qwerty287 2024-07-30 09:02:05 +02:00 committed by GitHub
commit 75d10e9d96
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 395 additions and 104 deletions

View File

@ -858,18 +858,19 @@ deps-backend:
.PHONY: deps-tools
deps-tools:
$(GO) install $(AIR_PACKAGE)
$(GO) install $(EDITORCONFIG_CHECKER_PACKAGE)
$(GO) install $(GOFUMPT_PACKAGE)
$(GO) install $(GOLANGCI_LINT_PACKAGE)
$(GO) install $(GXZ_PACKAGE)
$(GO) install $(MISSPELL_PACKAGE)
$(GO) install $(SWAGGER_PACKAGE)
$(GO) install $(XGO_PACKAGE)
$(GO) install $(GO_LICENSES_PACKAGE)
$(GO) install $(GOVULNCHECK_PACKAGE)
$(GO) install $(ACTIONLINT_PACKAGE)
$(GO) install $(GOPLS_PACKAGE)
$(GO) install $(AIR_PACKAGE) & \
$(GO) install $(EDITORCONFIG_CHECKER_PACKAGE) & \
$(GO) install $(GOFUMPT_PACKAGE) & \
$(GO) install $(GOLANGCI_LINT_PACKAGE) & \
$(GO) install $(GXZ_PACKAGE) & \
$(GO) install $(MISSPELL_PACKAGE) & \
$(GO) install $(SWAGGER_PACKAGE) & \
$(GO) install $(XGO_PACKAGE) & \
$(GO) install $(GO_LICENSES_PACKAGE) & \
$(GO) install $(GOVULNCHECK_PACKAGE) & \
$(GO) install $(ACTIONLINT_PACKAGE) & \
$(GO) install $(GOPLS_PACKAGE) & \
wait
node_modules: package-lock.json
npm install --no-save

View File

@ -171,13 +171,17 @@ func GetNextCommitStatusIndex(ctx context.Context, repoID int64, sha string) (in
return newIdx, nil
}
func (status *CommitStatus) loadAttributes(ctx context.Context) (err error) {
func (status *CommitStatus) loadRepository(ctx context.Context) (err error) {
if status.Repo == nil {
status.Repo, err = repo_model.GetRepositoryByID(ctx, status.RepoID)
if err != nil {
return fmt.Errorf("getRepositoryByID [%d]: %w", status.RepoID, err)
}
}
return nil
}
func (status *CommitStatus) loadCreator(ctx context.Context) (err error) {
if status.Creator == nil && status.CreatorID > 0 {
status.Creator, err = user_model.GetUserByID(ctx, status.CreatorID)
if err != nil {
@ -187,6 +191,13 @@ func (status *CommitStatus) loadAttributes(ctx context.Context) (err error) {
return nil
}
func (status *CommitStatus) loadAttributes(ctx context.Context) (err error) {
if err := status.loadRepository(ctx); err != nil {
return err
}
return status.loadCreator(ctx)
}
// APIURL returns the absolute APIURL to this commit-status.
func (status *CommitStatus) APIURL(ctx context.Context) string {
_ = status.loadAttributes(ctx)
@ -198,6 +209,25 @@ func (status *CommitStatus) LocaleString(lang translation.Locale) string {
return lang.TrString("repo.commitstatus." + status.State.String())
}
// HideActionsURL set `TargetURL` to an empty string if the status comes from Gitea Actions
func (status *CommitStatus) HideActionsURL(ctx context.Context) {
if status.RepoID == 0 {
return
}
if status.Repo == nil {
if err := status.loadRepository(ctx); err != nil {
log.Error("loadRepository: %v", err)
return
}
}
prefix := fmt.Sprintf("%s/actions", status.Repo.Link())
if strings.HasPrefix(status.TargetURL, prefix) {
status.TargetURL = ""
}
}
// CalcCommitStatus returns commit status state via some status, the commit statues should order by id desc
func CalcCommitStatus(statuses []*CommitStatus) *CommitStatus {
var lastStatus *CommitStatus
@ -506,3 +536,19 @@ func ConvertFromGitCommit(ctx context.Context, commits []*git.Commit, repo *repo
repo,
)
}
// CommitStatusesHideActionsURL hide Gitea Actions urls
func CommitStatusesHideActionsURL(ctx context.Context, statuses []*CommitStatus) {
idToRepos := make(map[int64]*repo_model.Repository)
for _, status := range statuses {
if status == nil {
continue
}
if status.Repo == nil {
status.Repo = idToRepos[status.RepoID]
}
status.HideActionsURL(ctx)
idToRepos[status.RepoID] = status.Repo
}
}

View File

@ -4,9 +4,11 @@
package git_test
import (
"fmt"
"testing"
"time"
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
@ -231,3 +233,26 @@ func TestFindRepoRecentCommitStatusContexts(t *testing.T) {
assert.Equal(t, "compliance/lint-backend", contexts[0])
}
}
func TestCommitStatusesHideActionsURL(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: 791, RepoID: repo.ID})
assert.NoError(t, run.LoadAttributes(db.DefaultContext))
statuses := []*git_model.CommitStatus{
{
RepoID: repo.ID,
TargetURL: fmt.Sprintf("%s/jobs/%d", run.Link(), run.Index),
},
{
RepoID: repo.ID,
TargetURL: "https://mycicd.org/1",
},
}
git_model.CommitStatusesHideActionsURL(db.DefaultContext, statuses)
assert.Empty(t, statuses[0].TargetURL)
assert.Equal(t, "https://mycicd.org/1", statuses[1].TargetURL)
}

View File

@ -27,6 +27,8 @@ import (
"xorm.io/builder"
)
var ErrMustCollaborator = util.NewPermissionDeniedErrorf("user must be a collaborator")
// ErrPullRequestNotExist represents a "PullRequestNotExist" kind of error.
type ErrPullRequestNotExist struct {
ID int64
@ -572,6 +574,12 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *Iss
return nil
}
// ErrUserMustCollaborator represents an error that the user must be a collaborator to a given repo.
type ErrUserMustCollaborator struct {
UserID int64
RepoName string
}
// GetUnmergedPullRequest returns a pull request that is open and has not been merged
// by given head/base and repo/branch.
func GetUnmergedPullRequest(ctx context.Context, headRepoID, baseRepoID int64, headBranch, baseBranch string, flow PullRequestFlow) (*PullRequest, error) {

View File

@ -103,6 +103,13 @@ type Project struct {
ClosedDateUnix timeutil.TimeStamp
}
// Ghost Project is a project which has been deleted
const GhostProjectID = -1
func (p *Project) IsGhost() bool {
return p.ID == GhostProjectID
}
func (p *Project) LoadOwner(ctx context.Context) (err error) {
if p.Owner != nil {
return nil

View File

@ -745,17 +745,18 @@ func GetRepositoryByOwnerAndName(ctx context.Context, ownerName, repoName string
// GetRepositoryByName returns the repository by given name under user if exists.
func GetRepositoryByName(ctx context.Context, ownerID int64, name string) (*Repository, error) {
repo := &Repository{
OwnerID: ownerID,
LowerName: strings.ToLower(name),
}
has, err := db.GetEngine(ctx).Get(repo)
var repo Repository
has, err := db.GetEngine(ctx).
Where("`owner_id`=?", ownerID).
And("`lower_name`=?", strings.ToLower(name)).
NoAutoCondition().
Get(&repo)
if err != nil {
return nil, err
} else if !has {
return nil, ErrRepoNotExist{0, ownerID, "", name}
}
return repo, err
return &repo, err
}
// getRepositoryURLPathSegments returns segments (owner, reponame) extracted from a url

2
options/license/HIDAPI Normal file
View File

@ -0,0 +1,2 @@
This software may be used by anyone for any reason so long
as the copyright notice in the source files remains intact.

10
options/license/Ruby-pty Normal file
View File

@ -0,0 +1,10 @@
(c) Copyright 1998 by Akinori Ito.
This software may be redistributed freely for this purpose, in full
or in part, provided that this entire copyright notice is included
on any copies of this software and applications and derivations thereof.
This software is provided on an "as is" basis, without warranty of any
kind, either expressed or implied, as to any matter including, but not
limited to warranty of fitness of purpose, or merchantability, or
results obtained from use of this software.

View File

@ -0,0 +1,23 @@
Copyright (c) 2008-2010 Derick Eddington. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
Except as contained in this notice, the name(s) of the above copyright
holders shall not be used in advertising or otherwise to promote the sale,
use or other dealings in this Software without prior written authorization.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,6 @@
Additional permission under the GNU Affero GPL version 3 section 7:
If you modify this Program, or any covered work, by linking or
combining it with other code, such other code is not for that reason
alone subject to any of the requirements of the GNU Affero GPL
version 3.

View File

@ -1765,6 +1765,7 @@ compare.compare_head = compare
pulls.desc = Enable pull requests and code reviews.
pulls.new = New Pull Request
pulls.new.blocked_user = Cannot create pull request because you are blocked by the repository owner.
pulls.new.must_collaborator = You must be a collaborator to create pull request.
pulls.edit.already_changed = Unable to save changes to the pull request. It appears the content has already been changed by another user. Please refresh the page and try editing again to avoid overwriting their changes
pulls.view = View Pull Request
pulls.compare_changes = New Pull Request
@ -3704,6 +3705,7 @@ variables.update.failed = Failed to edit variable.
variables.update.success = The variable has been edited.
[projects]
deleted.display_name = Deleted Project
type-1.display_name = Individual Project
type-2.display_name = Repository Project
type-3.display_name = Organization Project

View File

@ -117,12 +117,11 @@ func (Action) CreateOrUpdateSecret(ctx *context.APIContext) {
// "404":
// "$ref": "#/responses/notFound"
owner := ctx.Repo.Owner
repo := ctx.Repo.Repository
opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption)
_, created, err := secret_service.CreateOrUpdateSecret(ctx, owner.ID, repo.ID, ctx.PathParam("secretname"), opt.Data)
_, created, err := secret_service.CreateOrUpdateSecret(ctx, 0, repo.ID, ctx.PathParam("secretname"), opt.Data)
if err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err)
@ -174,10 +173,9 @@ func (Action) DeleteSecret(ctx *context.APIContext) {
// "404":
// "$ref": "#/responses/notFound"
owner := ctx.Repo.Owner
repo := ctx.Repo.Repository
err := secret_service.DeleteSecretByName(ctx, owner.ID, repo.ID, ctx.PathParam("secretname"))
err := secret_service.DeleteSecretByName(ctx, 0, repo.ID, ctx.PathParam("secretname"))
if err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
ctx.Error(http.StatusBadRequest, "DeleteSecret", err)
@ -486,7 +484,7 @@ func (Action) ListVariables(ctx *context.APIContext) {
// GetRegistrationToken returns the token to register repo runners
func (Action) GetRegistrationToken(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/runners/registration-token repository repoGetRunnerRegistrationToken
// swagger:operation GET /repos/{owner}/{repo}/actions/runners/registration-token repository repoGetRunnerRegistrationToken
// ---
// summary: Get a repository's actions runner registration token
// produces:
@ -506,7 +504,7 @@ func (Action) GetRegistrationToken(ctx *context.APIContext) {
// "200":
// "$ref": "#/responses/RegistrationToken"
shared.GetRegistrationToken(ctx, ctx.Repo.Repository.OwnerID, ctx.Repo.Repository.ID)
shared.GetRegistrationToken(ctx, 0, ctx.Repo.Repository.ID)
}
var _ actions_service.API = new(Action)

View File

@ -535,6 +535,8 @@ func CreatePullRequest(ctx *context.APIContext) {
ctx.Error(http.StatusBadRequest, "UserDoesNotHaveAccessToRepo", err)
} else if errors.Is(err, user_model.ErrBlockedUser) {
ctx.Error(http.StatusForbidden, "BlockedUser", err)
} else if errors.Is(err, issues_model.ErrMustCollaborator) {
ctx.Error(http.StatusForbidden, "MustCollaborator", err)
} else {
ctx.Error(http.StatusInternalServerError, "NewPullRequest", err)
}

View File

@ -70,6 +70,11 @@ func Branches(ctx *context.Context) {
ctx.ServerError("LoadBranches", err)
return
}
if !ctx.Repo.CanRead(unit.TypeActions) {
for key := range commitStatuses {
git_model.CommitStatusesHideActionsURL(ctx, commitStatuses[key])
}
}
commitStatus := make(map[string]*git_model.CommitStatus)
for commitID, cs := range commitStatuses {

View File

@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
@ -81,7 +82,7 @@ func Commits(ctx *context.Context) {
ctx.ServerError("CommitsByRange", err)
return
}
ctx.Data["Commits"] = git_model.ConvertFromGitCommit(ctx, commits, ctx.Repo.Repository)
ctx.Data["Commits"] = processGitCommits(ctx, commits)
ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
@ -199,7 +200,7 @@ func SearchCommits(ctx *context.Context) {
return
}
ctx.Data["CommitCount"] = len(commits)
ctx.Data["Commits"] = git_model.ConvertFromGitCommit(ctx, commits, ctx.Repo.Repository)
ctx.Data["Commits"] = processGitCommits(ctx, commits)
ctx.Data["Keyword"] = query
if all {
@ -242,7 +243,7 @@ func FileHistory(ctx *context.Context) {
ctx.ServerError("CommitsByFileAndRange", err)
return
}
ctx.Data["Commits"] = git_model.ConvertFromGitCommit(ctx, commits, ctx.Repo.Repository)
ctx.Data["Commits"] = processGitCommits(ctx, commits)
ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
@ -353,6 +354,9 @@ func Diff(ctx *context.Context) {
if err != nil {
log.Error("GetLatestCommitStatus: %v", err)
}
if !ctx.Repo.CanRead(unit_model.TypeActions) {
git_model.CommitStatusesHideActionsURL(ctx, statuses)
}
ctx.Data["CommitStatus"] = git_model.CalcCommitStatus(statuses)
ctx.Data["CommitStatuses"] = statuses
@ -433,3 +437,14 @@ func RawDiff(ctx *context.Context) {
return
}
}
func processGitCommits(ctx *context.Context, gitCommits []*git.Commit) []*git_model.SignCommitWithStatuses {
commits := git_model.ConvertFromGitCommit(ctx, gitCommits, ctx.Repo.Repository)
if !ctx.Repo.CanRead(unit_model.TypeActions) {
for _, commit := range commits {
commit.Status.HideActionsURL(ctx)
git_model.CommitStatusesHideActionsURL(ctx, commit.Statuses)
}
}
return commits
}

View File

@ -643,7 +643,7 @@ func PrepareCompareDiff(
return false
}
commits := git_model.ConvertFromGitCommit(ctx, ci.CompareInfo.Commits, ci.HeadRepo)
commits := processGitCommits(ctx, ci.CompareInfo.Commits)
ctx.Data["Commits"] = commits
ctx.Data["CommitCount"] = len(commits)

View File

@ -339,6 +339,11 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
ctx.ServerError("GetIssuesAllCommitStatus", err)
return
}
if !ctx.Repo.CanRead(unit.TypeActions) {
for key := range commitStatuses {
git_model.CommitStatusesHideActionsURL(ctx, commitStatuses[key])
}
}
if err := issues.LoadAttributes(ctx); err != nil {
ctx.ServerError("issues.LoadAttributes", err)
@ -1671,7 +1676,7 @@ func ViewIssue(ctx *context.Context) {
}
ghostProject := &project_model.Project{
ID: -1,
ID: project_model.GhostProjectID,
Title: ctx.Locale.TrString("repo.issues.deleted_project"),
}
@ -1757,6 +1762,12 @@ func ViewIssue(ctx *context.Context) {
ctx.ServerError("LoadPushCommits", err)
return
}
if !ctx.Repo.CanRead(unit.TypeActions) {
for _, commit := range comment.Commits {
commit.Status.HideActionsURL(ctx)
git_model.CommitStatusesHideActionsURL(ctx, commit.Statuses)
}
}
} else if comment.Type == issues_model.CommentTypeAddTimeManual ||
comment.Type == issues_model.CommentTypeStopTracking ||
comment.Type == issues_model.CommentTypeDeleteTimeManual {

View File

@ -283,6 +283,10 @@ func PrepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue)
ctx.ServerError("GetLatestCommitStatus", err)
return nil
}
if !ctx.Repo.CanRead(unit.TypeActions) {
git_model.CommitStatusesHideActionsURL(ctx, commitStatuses)
}
if len(commitStatuses) != 0 {
ctx.Data["LatestCommitStatuses"] = commitStatuses
ctx.Data["LatestCommitStatus"] = git_model.CalcCommitStatus(commitStatuses)
@ -345,6 +349,10 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
ctx.ServerError("GetLatestCommitStatus", err)
return nil
}
if !ctx.Repo.CanRead(unit.TypeActions) {
git_model.CommitStatusesHideActionsURL(ctx, commitStatuses)
}
if len(commitStatuses) > 0 {
ctx.Data["LatestCommitStatuses"] = commitStatuses
ctx.Data["LatestCommitStatus"] = git_model.CalcCommitStatus(commitStatuses)
@ -437,6 +445,10 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
ctx.ServerError("GetLatestCommitStatus", err)
return nil
}
if !ctx.Repo.CanRead(unit.TypeActions) {
git_model.CommitStatusesHideActionsURL(ctx, commitStatuses)
}
if len(commitStatuses) > 0 {
ctx.Data["LatestCommitStatuses"] = commitStatuses
ctx.Data["LatestCommitStatus"] = git_model.CalcCommitStatus(commitStatuses)
@ -603,7 +615,7 @@ func ViewPullCommits(ctx *context.Context) {
ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
commits := git_model.ConvertFromGitCommit(ctx, prInfo.Commits, ctx.Repo.Repository)
commits := processGitCommits(ctx, prInfo.Commits)
ctx.Data["Commits"] = commits
ctx.Data["CommitCount"] = len(commits)
@ -1325,6 +1337,16 @@ func CompareAndPullRequestPost(ctx *context.Context) {
return
}
ctx.JSONError(flashError)
} else if errors.Is(err, issues_model.ErrMustCollaborator) {
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{
"Message": ctx.Tr("repo.pulls.push_rejected"),
"Summary": ctx.Tr("repo.pulls.new.must_collaborator"),
})
if err != nil {
ctx.ServerError("CompareAndPullRequest.HTMLString", err)
return
}
ctx.JSONError(flashError)
}
return
}

View File

@ -656,6 +656,9 @@ func SearchRepo(ctx *context.Context) {
ctx.JSON(http.StatusInternalServerError, nil)
return
}
if !ctx.Repo.CanRead(unit.TypeActions) {
git_model.CommitStatusesHideActionsURL(ctx, latestCommitStatuses)
}
results := make([]*repo_service.WebSearchRepository, len(repos))
for i, repo := range repos {

View File

@ -363,6 +363,9 @@ func loadLatestCommitData(ctx *context.Context, latestCommit *git.Commit) bool {
if err != nil {
log.Error("GetLatestCommitStatus: %v", err)
}
if !ctx.Repo.CanRead(unit_model.TypeActions) {
git_model.CommitStatusesHideActionsURL(ctx, statuses)
}
ctx.Data["LatestCommitStatus"] = git_model.CalcCommitStatus(statuses)
ctx.Data["LatestCommitStatuses"] = statuses

View File

@ -17,6 +17,7 @@ import (
activities_model "code.gitea.io/gitea/models/activities"
asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
@ -568,6 +569,11 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
ctx.ServerError("GetIssuesLastCommitStatus", err)
return
}
if !ctx.Repo.CanRead(unit.TypeActions) {
for key := range commitStatuses {
git_model.CommitStatusesHideActionsURL(ctx, commitStatuses[key])
}
}
// -------------------------------
// Fill stats to post to ctx.Data.

View File

@ -13,8 +13,10 @@ import (
activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
@ -303,6 +305,11 @@ func NotificationSubscriptions(ctx *context.Context) {
ctx.ServerError("GetIssuesAllCommitStatus", err)
return
}
if !ctx.Repo.CanRead(unit.TypeActions) {
for key := range commitStatuses {
git_model.CommitStatusesHideActionsURL(ctx, commitStatuses[key])
}
}
ctx.Data["CommitLastStatus"] = lastStatus
ctx.Data["CommitStatuses"] = commitStatuses
ctx.Data["Issues"] = issues

View File

@ -5,7 +5,6 @@ package actions
import (
"context"
"time"
"code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/modules/log"
@ -13,7 +12,7 @@ import (
)
// Cleanup removes expired actions logs, data and artifacts
func Cleanup(taskCtx context.Context, olderThan time.Duration) error {
func Cleanup(taskCtx context.Context) error {
// TODO: clean up expired actions logs
// clean up expired artifacts

View File

@ -19,6 +19,7 @@ func initActionsTasks() {
registerStopEndlessTasks()
registerCancelAbandonedJobs()
registerScheduleTasks()
registerActionsCleanup()
}
func registerStopZombieTasks() {
@ -63,3 +64,13 @@ func registerScheduleTasks() {
return actions_service.StartScheduleTasks(ctx)
})
}
func registerActionsCleanup() {
RegisterTaskFatal("cleanup_actions", &BaseConfig{
Enabled: true,
RunAtStart: true,
Schedule: "@midnight",
}, func(ctx context.Context, _ *user_model.User, _ Config) error {
return actions_service.Cleanup(ctx)
})
}

View File

@ -13,7 +13,6 @@ import (
"code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/services/actions"
"code.gitea.io/gitea/services/auth"
"code.gitea.io/gitea/services/migrations"
mirror_service "code.gitea.io/gitea/services/mirror"
@ -157,20 +156,6 @@ func registerCleanupPackages() {
})
}
func registerActionsCleanup() {
RegisterTaskFatal("cleanup_actions", &OlderThanConfig{
BaseConfig: BaseConfig{
Enabled: true,
RunAtStart: true,
Schedule: "@midnight",
},
OlderThan: 24 * time.Hour,
}, func(ctx context.Context, _ *user_model.User, config Config) error {
realConfig := config.(*OlderThanConfig)
return actions.Cleanup(ctx, realConfig.OlderThan)
})
}
func initBasicTasks() {
if setting.Mirror.Enabled {
registerUpdateMirrorTask()
@ -187,7 +172,4 @@ func initBasicTasks() {
if setting.Packages.Enabled {
registerCleanupPackages()
}
if setting.Actions.Enabled {
registerActionsCleanup()
}
}

View File

@ -17,7 +17,9 @@ import (
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/container"
@ -48,6 +50,28 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *iss
return user_model.ErrBlockedUser
}
// user should be a collaborator or a member of the organization for base repo
if !issue.Poster.IsAdmin {
canCreate, err := repo_model.IsOwnerMemberCollaborator(ctx, repo, issue.Poster.ID)
if err != nil {
return err
}
if !canCreate {
// or user should have write permission in the head repo
if err := pr.LoadHeadRepo(ctx); err != nil {
return err
}
perm, err := access_model.GetUserRepoPermission(ctx, pr.HeadRepo, issue.Poster)
if err != nil {
return err
}
if !perm.CanWrite(unit.TypeCode) {
return issues_model.ErrMustCollaborator
}
}
}
prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr)
if err != nil {
if !git_model.IsErrBranchNotExist(err) {

View File

@ -581,13 +581,19 @@
{{template "shared/user/authorlink" .Poster}}
{{$oldProjectDisplayHtml := "Unknown Project"}}
{{if .OldProject}}
{{$trKey := printf "projects.type-%d.display_name" .OldProject.Type}}
{{$oldProjectDisplayHtml = HTMLFormat `<span data-tooltip-content="%s">%s</span>` (ctx.Locale.Tr $trKey) .OldProject.Title}}
{{$tooltip := ctx.Locale.Tr "projects.deleted.display_name"}}
{{if not .OldProject.IsGhost}}
{{$tooltip = ctx.Locale.Tr (printf "projects.type-%d.display_name" .OldProject.Type)}}
{{end}}
{{$oldProjectDisplayHtml = HTMLFormat `<span data-tooltip-content="%s">%s</span>` $tooltip .OldProject.Title}}
{{end}}
{{$newProjectDisplayHtml := "Unknown Project"}}
{{if .Project}}
{{$trKey := printf "projects.type-%d.display_name" .Project.Type}}
{{$newProjectDisplayHtml = HTMLFormat `<span data-tooltip-content="%s">%s</span>` (ctx.Locale.Tr $trKey) .Project.Title}}
{{$tooltip := ctx.Locale.Tr "projects.deleted.display_name"}}
{{if not .Project.IsGhost}}
{{$tooltip = ctx.Locale.Tr (printf "projects.type-%d.display_name" .Project.Type)}}
{{end}}
{{$newProjectDisplayHtml = HTMLFormat `<span data-tooltip-content="%s">%s</span>` $tooltip .Project.Title}}
{{end}}
{{if and (gt .OldProjectID 0) (gt .ProjectID 0)}}
{{ctx.Locale.Tr "repo.issues.change_project_at" $oldProjectDisplayHtml $newProjectDisplayHtml $createdStr}}

View File

@ -3843,6 +3843,39 @@
}
}
},
"/repos/{owner}/{repo}/actions/runners/registration-token": {
"get": {
"produces": [
"application/json"
],
"tags": [
"repository"
],
"summary": "Get a repository's actions runner registration token",
"operationId": "repoGetRunnerRegistrationToken",
"parameters": [
{
"type": "string",
"description": "owner of the repo",
"name": "owner",
"in": "path",
"required": true
},
{
"type": "string",
"description": "name of the repo",
"name": "repo",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"$ref": "#/responses/RegistrationToken"
}
}
}
},
"/repos/{owner}/{repo}/actions/secrets": {
"get": {
"produces": [
@ -13408,39 +13441,6 @@
}
}
},
"/repos/{owner}/{repo}/runners/registration-token": {
"get": {
"produces": [
"application/json"
],
"tags": [
"repository"
],
"summary": "Get a repository's actions runner registration token",
"operationId": "repoGetRunnerRegistrationToken",
"parameters": [
{
"type": "string",
"description": "owner of the repo",
"name": "owner",
"in": "path",
"required": true
},
{
"type": "string",
"description": "name of the repo",
"name": "repo",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"$ref": "#/responses/RegistrationToken"
}
}
}
},
"/repos/{owner}/{repo}/signing-key.gpg": {
"get": {
"produces": [

View File

@ -11,9 +11,11 @@ import (
"time"
actions_model "code.gitea.io/gitea/models/actions"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unittest"
@ -34,7 +36,7 @@ import (
func TestPullRequestTargetEvent(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // owner of the base repo
org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) // owner of the forked repo
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) // owner of the forked repo
// create the base repo
baseRepo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{
@ -57,8 +59,12 @@ func TestPullRequestTargetEvent(t *testing.T) {
}}, nil)
assert.NoError(t, err)
// add user4 as the collaborator
ctx := NewAPITestContext(t, baseRepo.OwnerName, baseRepo.Name, auth_model.AccessTokenScopeWriteRepository)
t.Run("AddUser4AsCollaboratorWithReadAccess", doAPIAddCollaborator(ctx, "user4", perm.AccessModeRead))
// create the forked repo
forkedRepo, err := repo_service.ForkRepository(git.DefaultContext, user2, org3, repo_service.ForkRepoOptions{
forkedRepo, err := repo_service.ForkRepository(git.DefaultContext, user2, user4, repo_service.ForkRepoOptions{
BaseRepo: baseRepo,
Name: "forked-repo-pull-request-target",
Description: "test pull-request-target event",
@ -95,7 +101,7 @@ func TestPullRequestTargetEvent(t *testing.T) {
assert.NotEmpty(t, addWorkflowToBaseResp)
// add a new file to the forked repo
addFileToForkedResp, err := files_service.ChangeRepoFiles(git.DefaultContext, forkedRepo, org3, &files_service.ChangeRepoFilesOptions{
addFileToForkedResp, err := files_service.ChangeRepoFiles(git.DefaultContext, forkedRepo, user4, &files_service.ChangeRepoFilesOptions{
Files: []*files_service.ChangeRepoFile{
{
Operation: "create",
@ -107,12 +113,12 @@ func TestPullRequestTargetEvent(t *testing.T) {
OldBranch: "main",
NewBranch: "fork-branch-1",
Author: &files_service.IdentityOptions{
Name: org3.Name,
Email: org3.Email,
Name: user4.Name,
Email: user4.Email,
},
Committer: &files_service.IdentityOptions{
Name: org3.Name,
Email: org3.Email,
Name: user4.Name,
Email: user4.Email,
},
Dates: &files_service.CommitDateOptions{
Author: time.Now(),
@ -126,8 +132,8 @@ func TestPullRequestTargetEvent(t *testing.T) {
pullIssue := &issues_model.Issue{
RepoID: baseRepo.ID,
Title: "Test pull-request-target-event",
PosterID: org3.ID,
Poster: org3,
PosterID: user4.ID,
Poster: user4,
IsPull: true,
}
pullRequest := &issues_model.PullRequest{
@ -149,7 +155,7 @@ func TestPullRequestTargetEvent(t *testing.T) {
assert.Equal(t, actions_module.GithubEventPullRequestTarget, actionRun.TriggerEvent)
// add another file whose name cannot match the specified path
addFileToForkedResp, err = files_service.ChangeRepoFiles(git.DefaultContext, forkedRepo, org3, &files_service.ChangeRepoFilesOptions{
addFileToForkedResp, err = files_service.ChangeRepoFiles(git.DefaultContext, forkedRepo, user4, &files_service.ChangeRepoFilesOptions{
Files: []*files_service.ChangeRepoFile{
{
Operation: "create",
@ -161,12 +167,12 @@ func TestPullRequestTargetEvent(t *testing.T) {
OldBranch: "main",
NewBranch: "fork-branch-2",
Author: &files_service.IdentityOptions{
Name: org3.Name,
Email: org3.Email,
Name: user4.Name,
Email: user4.Email,
},
Committer: &files_service.IdentityOptions{
Name: org3.Name,
Email: org3.Email,
Name: user4.Name,
Email: user4.Email,
},
Dates: &files_service.CommitDateOptions{
Author: time.Now(),
@ -180,8 +186,8 @@ func TestPullRequestTargetEvent(t *testing.T) {
pullIssue = &issues_model.Issue{
RepoID: baseRepo.ID,
Title: "A mismatched path cannot trigger pull-request-target-event",
PosterID: org3.ID,
Poster: org3,
PosterID: user4.ID,
Poster: user4,
IsPull: true,
}
pullRequest = &issues_model.PullRequest{

View File

@ -12,6 +12,7 @@ import (
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@ -126,6 +127,65 @@ func TestAPICreatePullSuccess(t *testing.T) {
MakeRequest(t, req, http.StatusUnprocessableEntity) // second request should fail
}
func TestAPICreatePullBasePermission(t *testing.T) {
defer tests.PrepareTestEnv(t)()
repo10 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
// repo10 have code, pulls units.
repo11 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 11})
// repo11 only have code unit but should still create pulls
owner10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo10.OwnerID})
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
session := loginUser(t, user4.Name)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
opts := &api.CreatePullRequestOption{
Head: fmt.Sprintf("%s:master", repo11.OwnerName),
Base: "master",
Title: "create a failure pr",
}
req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), &opts).AddTokenAuth(token)
MakeRequest(t, req, http.StatusForbidden)
// add user4 to be a collaborator to base repo
ctx := NewAPITestContext(t, repo10.OwnerName, repo10.Name, auth_model.AccessTokenScopeWriteRepository)
t.Run("AddUser4AsCollaborator", doAPIAddCollaborator(ctx, user4.Name, perm.AccessModeRead))
// create again
req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), &opts).AddTokenAuth(token)
MakeRequest(t, req, http.StatusCreated)
}
func TestAPICreatePullHeadPermission(t *testing.T) {
defer tests.PrepareTestEnv(t)()
repo10 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
// repo10 have code, pulls units.
repo11 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 11})
// repo11 only have code unit but should still create pulls
owner10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo10.OwnerID})
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
session := loginUser(t, user4.Name)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
opts := &api.CreatePullRequestOption{
Head: fmt.Sprintf("%s:master", repo11.OwnerName),
Base: "master",
Title: "create a failure pr",
}
req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), &opts).AddTokenAuth(token)
MakeRequest(t, req, http.StatusForbidden)
// add user4 to be a collaborator to head repo with read permission
ctx := NewAPITestContext(t, repo11.OwnerName, repo11.Name, auth_model.AccessTokenScopeWriteRepository)
t.Run("AddUser4AsCollaboratorWithRead", doAPIAddCollaborator(ctx, user4.Name, perm.AccessModeRead))
req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), &opts).AddTokenAuth(token)
MakeRequest(t, req, http.StatusForbidden)
// add user4 to be a collaborator to head repo with write permission
t.Run("AddUser4AsCollaboratorWithWrite", doAPIAddCollaborator(ctx, user4.Name, perm.AccessModeWrite))
req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), &opts).AddTokenAuth(token)
MakeRequest(t, req, http.StatusCreated)
}
func TestAPICreatePullSameRepoSuccess(t *testing.T) {
defer tests.PrepareTestEnv(t)()
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})