feat: vendor gitea 1.16.2
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package convert
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
db "code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// buildWorkflowTestRepo creates a temporary git repository for testing GetActionWorkflow.
|
||||
// The default branch "main" has no workflow files; "feature" and "release-v1" each add their own workflow file.
|
||||
func buildWorkflowTestRepo(t *testing.T) string {
|
||||
t.Helper()
|
||||
ctx := t.Context()
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
_, _, err := gitcmd.NewCommand("init").WithDir(tmpDir).RunStdString(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
readme := "readme"
|
||||
featureWF := "on: [push]\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - run: echo test\n"
|
||||
releaseWF := "on: [push]\njobs:\n release:\n runs-on: ubuntu-latest\n steps:\n - run: echo release\n"
|
||||
|
||||
// Build a git fast-import stream:
|
||||
// :4 = initial commit on main (README.md only)
|
||||
// :5 = feature branch commit (adds feature workflow)
|
||||
// :6 = release commit from :4 (adds release workflow, tagged release-v1, not on main)
|
||||
var sb strings.Builder
|
||||
fmt.Fprintf(&sb, "blob\nmark :1\ndata %d\n%s\n", len(readme), readme)
|
||||
fmt.Fprintf(&sb, "blob\nmark :2\ndata %d\n%s\n", len(featureWF), featureWF)
|
||||
fmt.Fprintf(&sb, "blob\nmark :3\ndata %d\n%s\n", len(releaseWF), releaseWF)
|
||||
fmt.Fprintf(&sb, "commit refs/heads/main\nmark :4\nauthor Test <test@gitea.com> 1000000000 +0000\ncommitter Test <test@gitea.com> 1000000000 +0000\ndata 14\ninitial commit\nM 100644 :1 README.md\n\n")
|
||||
fmt.Fprintf(&sb, "commit refs/heads/feature\nmark :5\nauthor Test <test@gitea.com> 1000000001 +0000\ncommitter Test <test@gitea.com> 1000000001 +0000\ndata 12\nadd workflow\nfrom :4\nM 100644 :2 .gitea/workflows/my-workflow.yml\n\n")
|
||||
fmt.Fprintf(&sb, "reset refs/pull/42/merge\nfrom :5\n\n")
|
||||
fmt.Fprintf(&sb, "commit refs/heads/main\nmark :6\nauthor Test <test@gitea.com> 1000000002 +0000\ncommitter Test <test@gitea.com> 1000000002 +0000\ndata 16\nrelease workflow\nfrom :4\nM 100644 :3 .gitea/workflows/my-workflow.yml\n\n")
|
||||
fmt.Fprintf(&sb, "reset refs/tags/release-v1\nfrom :6\n\n")
|
||||
fmt.Fprintf(&sb, "reset refs/heads/main\nfrom :4\n\n")
|
||||
fmt.Fprintf(&sb, "done\n")
|
||||
|
||||
_, _, err = gitcmd.NewCommand("fast-import").WithDir(tmpDir).WithStdinBytes([]byte(sb.String())).RunStdString(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
return tmpDir
|
||||
}
|
||||
|
||||
func TestGetActionWorkflow_FallbackRef(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
repoDir := buildWorkflowTestRepo(t)
|
||||
|
||||
gitRepo, err := git.OpenRepository(ctx, repoDir)
|
||||
require.NoError(t, err)
|
||||
defer gitRepo.Close()
|
||||
|
||||
repo := &repo_model.Repository{
|
||||
DefaultBranch: "main",
|
||||
OwnerName: "test-owner",
|
||||
Name: "test-repo",
|
||||
Units: []*repo_model.RepoUnit{
|
||||
{
|
||||
Type: unit.TypeActions,
|
||||
Config: &repo_model.ActionsConfig{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("returns error when workflow only on non-default branch", func(t *testing.T) {
|
||||
_, err := GetActionWorkflow(ctx, gitRepo, repo, "my-workflow.yml")
|
||||
require.Error(t, err)
|
||||
assert.ErrorIs(t, err, util.ErrNotExist)
|
||||
})
|
||||
|
||||
t.Run("returns workflow when found via ref", func(t *testing.T) {
|
||||
wf, err := GetActionWorkflowByRef(ctx, gitRepo, repo, "my-workflow.yml", git.RefName("refs/heads/feature"))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "my-workflow.yml", wf.ID)
|
||||
})
|
||||
|
||||
t.Run("returns workflow when found via pull ref", func(t *testing.T) {
|
||||
wf, err := GetActionWorkflowByRef(ctx, gitRepo, repo, "my-workflow.yml", git.RefName("refs/pull/42/merge"))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "my-workflow.yml", wf.ID)
|
||||
assert.Contains(t, wf.HTMLURL, "/src/commit/")
|
||||
})
|
||||
|
||||
t.Run("returns workflow with tag link when found via tag ref", func(t *testing.T) {
|
||||
wf, err := GetActionWorkflowByRef(ctx, gitRepo, repo, "my-workflow.yml", git.RefName("refs/tags/release-v1"))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "my-workflow.yml", wf.ID)
|
||||
assert.Contains(t, wf.HTMLURL, "/src/tag/release-v1/")
|
||||
})
|
||||
|
||||
t.Run("returns error when workflow missing from ref", func(t *testing.T) {
|
||||
_, err := GetActionWorkflowByRef(ctx, gitRepo, repo, "nonexistent.yml", git.RefName("refs/heads/feature"))
|
||||
require.Error(t, err)
|
||||
assert.ErrorIs(t, err, util.ErrNotExist)
|
||||
})
|
||||
}
|
||||
|
||||
func TestToActionWorkflowRun_UsesTriggerEvent(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||
run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: 803})
|
||||
|
||||
// Scheduled runs keep Event as the registration event (push) and use TriggerEvent as the real trigger.
|
||||
run.Event = "push"
|
||||
run.TriggerEvent = "schedule"
|
||||
|
||||
apiRun, err := ToActionWorkflowRun(t.Context(), repo, run)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "schedule", apiRun.Event)
|
||||
}
|
||||
|
||||
func TestToActionWorkflowJob_StepStatusIsIndependentOfJobStatus(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
ctx := t.Context()
|
||||
|
||||
run := &actions_model.ActionRun{
|
||||
ID: 9001,
|
||||
RepoID: 2,
|
||||
TriggerUserID: 1,
|
||||
WorkflowID: "test.yaml",
|
||||
Index: 12345,
|
||||
Ref: "refs/heads/main",
|
||||
Status: actions_model.StatusFailure,
|
||||
}
|
||||
require.NoError(t, db.Insert(ctx, run))
|
||||
|
||||
task := &actions_model.ActionTask{
|
||||
ID: 900102,
|
||||
JobID: 9001,
|
||||
RepoID: 2,
|
||||
Status: actions_model.StatusFailure,
|
||||
}
|
||||
require.NoError(t, db.Insert(ctx, task))
|
||||
|
||||
job := &actions_model.ActionRunJob{
|
||||
ID: 90010203,
|
||||
RunID: 9001,
|
||||
TaskID: 900102,
|
||||
RepoID: 2,
|
||||
Name: "test-job-name",
|
||||
Attempt: 1,
|
||||
JobID: "test-job-id",
|
||||
Status: actions_model.StatusFailure,
|
||||
}
|
||||
require.NoError(t, db.Insert(ctx, job))
|
||||
|
||||
require.NoError(t, db.Insert(ctx,
|
||||
&actions_model.ActionTaskStep{TaskID: task.ID, RepoID: 2, Index: 0, Name: "step-success", Status: actions_model.StatusSuccess},
|
||||
&actions_model.ActionTaskStep{TaskID: task.ID, RepoID: 2, Index: 1, Name: "step-failure", Status: actions_model.StatusFailure},
|
||||
))
|
||||
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||
|
||||
apiJob, err := ToActionWorkflowJob(ctx, repo, task, job)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apiJob.Steps, 2)
|
||||
|
||||
assert.Equal(t, "completed", apiJob.Steps[0].Status, "step 0 status")
|
||||
assert.Equal(t, "success", apiJob.Steps[0].Conclusion, "step 0 conclusion (succeeded before the failure)")
|
||||
assert.Equal(t, "completed", apiJob.Steps[1].Status, "step 1 status")
|
||||
assert.Equal(t, "failure", apiJob.Steps[1].Conclusion, "step 1 conclusion")
|
||||
}
|
||||
@@ -15,9 +15,9 @@ import (
|
||||
)
|
||||
|
||||
func ToActivity(ctx context.Context, ac *activities_model.Action, doer *user_model.User) *api.Activity {
|
||||
p, err := access_model.GetUserRepoPermission(ctx, ac.Repo, doer)
|
||||
p, err := access_model.GetDoerRepoPermission(ctx, ac.Repo, doer)
|
||||
if err != nil {
|
||||
log.Error("GetUserRepoPermission[%d]: %v", ac.RepoID, err)
|
||||
log.Error("GetDoerRepoPermission[%d]: %v", ac.RepoID, err)
|
||||
p.AccessMode = perm_model.AccessModeNone
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ func ToBranch(ctx context.Context, repo *repo_model.Repository, branchName strin
|
||||
return nil, err
|
||||
}
|
||||
|
||||
perms, err := access_model.GetUserRepoPermission(ctx, repo, user)
|
||||
perms, err := access_model.GetIndividualUserRepoPermission(ctx, repo, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -105,7 +105,7 @@ func ToBranch(ctx context.Context, repo *repo_model.Repository, branchName strin
|
||||
}
|
||||
|
||||
if user != nil {
|
||||
permission, err := access_model.GetUserRepoPermission(ctx, repo, user)
|
||||
permission, err := access_model.GetIndividualUserRepoPermission(ctx, repo, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -203,8 +203,8 @@ func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch, repo
|
||||
|
||||
// ToTag convert a git.Tag to an api.Tag
|
||||
func ToTag(repo *repo_model.Repository, t *git.Tag) *api.Tag {
|
||||
tarballURL := util.URLJoin(repo.HTMLURL(), "archive", t.Name+".tar.gz")
|
||||
zipballURL := util.URLJoin(repo.HTMLURL(), "archive", t.Name+".zip")
|
||||
tarballURL := repo.HTMLURL() + "/archive/" + url.PathEscape(t.Name+".tar.gz")
|
||||
zipballURL := repo.HTMLURL() + "/archive/" + url.PathEscape(t.Name+".zip")
|
||||
|
||||
// Archive URLs are "" if the download feature is disabled
|
||||
if setting.Repository.DisableDownloadSourceArchives {
|
||||
@@ -214,7 +214,7 @@ func ToTag(repo *repo_model.Repository, t *git.Tag) *api.Tag {
|
||||
|
||||
return &api.Tag{
|
||||
Name: t.Name,
|
||||
Message: strings.TrimSpace(t.Message),
|
||||
Message: strings.ToValidUTF8(strings.TrimSpace(t.Message), "?"), // the trim is not right, 1.27 won't trim
|
||||
ID: t.ID.String(),
|
||||
Commit: ToCommitMeta(repo, t),
|
||||
ZipballURL: zipballURL,
|
||||
@@ -260,7 +260,7 @@ func ToActionWorkflowRun(ctx context.Context, repo *repo_model.Repository, run *
|
||||
RunNumber: run.Index,
|
||||
StartedAt: run.Started.AsLocalTime(),
|
||||
CompletedAt: run.Stopped.AsLocalTime(),
|
||||
Event: string(run.Event),
|
||||
Event: run.TriggerEvent,
|
||||
DisplayTitle: run.Title,
|
||||
HeadBranch: git.RefName(run.Ref).BranchName(),
|
||||
HeadSha: run.CommitSHA,
|
||||
@@ -274,33 +274,31 @@ func ToActionWorkflowRun(ctx context.Context, repo *repo_model.Repository, run *
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ToWorkflowRunAction(status actions_model.Status) string {
|
||||
var action string
|
||||
func ToWorkflowRunAction(status actions_model.Status) (action string) {
|
||||
switch status {
|
||||
case actions_model.StatusWaiting, actions_model.StatusBlocked:
|
||||
action = "requested"
|
||||
case actions_model.StatusRunning:
|
||||
action = "in_progress"
|
||||
}
|
||||
if status.IsDone() {
|
||||
action = "completed"
|
||||
default:
|
||||
if status.IsDone() {
|
||||
action = "completed"
|
||||
} else {
|
||||
setting.PanicInDevOrTesting("unknown action status: %v", status)
|
||||
}
|
||||
}
|
||||
return action
|
||||
}
|
||||
|
||||
func ToActionsStatus(status actions_model.Status) (string, string) {
|
||||
var action string
|
||||
var conclusion string
|
||||
func ToActionsStatus(status actions_model.Status) (action, conclusion string) {
|
||||
switch status {
|
||||
// This is a naming conflict of the webhook between Gitea and GitHub Actions
|
||||
case actions_model.StatusWaiting:
|
||||
action = "queued"
|
||||
action = "queued" // "waiting" is a naming conflict of the webhook between Gitea and GitHub Actions
|
||||
case actions_model.StatusBlocked:
|
||||
action = "waiting"
|
||||
action = "waiting" // naming conflict (as above)
|
||||
case actions_model.StatusRunning:
|
||||
action = "in_progress"
|
||||
}
|
||||
if status.IsDone() {
|
||||
default:
|
||||
action = "completed"
|
||||
switch status {
|
||||
case actions_model.StatusSuccess:
|
||||
@@ -311,6 +309,8 @@ func ToActionsStatus(status actions_model.Status) (string, string) {
|
||||
conclusion = "failure"
|
||||
case actions_model.StatusSkipped:
|
||||
conclusion = "skipped"
|
||||
default:
|
||||
setting.PanicInDevOrTesting("unknown action status: %v", status)
|
||||
}
|
||||
}
|
||||
return action, conclusion
|
||||
@@ -324,18 +324,6 @@ func ToActionWorkflowJob(ctx context.Context, repo *repo_model.Repository, task
|
||||
return nil, err
|
||||
}
|
||||
|
||||
jobIndex := 0
|
||||
jobs, err := actions_model.GetRunJobsByRunID(ctx, job.RunID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i, j := range jobs {
|
||||
if j.ID == job.ID {
|
||||
jobIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
status, conclusion := ToActionsStatus(job.Status)
|
||||
var runnerID int64
|
||||
var runnerName string
|
||||
@@ -349,20 +337,29 @@ func ToActionWorkflowJob(ctx context.Context, repo *repo_model.Repository, task
|
||||
}
|
||||
}
|
||||
|
||||
runnerID = task.RunnerID
|
||||
if runner, ok, _ := db.GetByID[actions_model.ActionRunner](ctx, runnerID); ok {
|
||||
runnerName = runner.Name
|
||||
}
|
||||
for i, step := range task.Steps {
|
||||
stepStatus, stepConclusion := ToActionsStatus(job.Status)
|
||||
steps = append(steps, &api.ActionWorkflowStep{
|
||||
Name: step.Name,
|
||||
Number: int64(i),
|
||||
Status: stepStatus,
|
||||
Conclusion: stepConclusion,
|
||||
StartedAt: step.Started.AsTime().UTC(),
|
||||
CompletedAt: step.Stopped.AsTime().UTC(),
|
||||
})
|
||||
if task != nil {
|
||||
if task.Steps == nil {
|
||||
task.Steps, err = actions_model.GetTaskStepsByTaskID(ctx, task.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
task.Steps = util.SliceNilAsEmpty(task.Steps)
|
||||
}
|
||||
runnerID = task.RunnerID
|
||||
if runner, ok, _ := db.GetByID[actions_model.ActionRunner](ctx, runnerID); ok {
|
||||
runnerName = runner.Name
|
||||
}
|
||||
for i, step := range task.Steps {
|
||||
stepStatus, stepConclusion := ToActionsStatus(step.Status)
|
||||
steps = append(steps, &api.ActionWorkflowStep{
|
||||
Name: step.Name,
|
||||
Number: int64(i),
|
||||
Status: stepStatus,
|
||||
Conclusion: stepConclusion,
|
||||
StartedAt: step.Started.AsTime().UTC(),
|
||||
CompletedAt: step.Stopped.AsTime().UTC(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,7 +367,7 @@ func ToActionWorkflowJob(ctx context.Context, repo *repo_model.Repository, task
|
||||
ID: job.ID,
|
||||
// missing api endpoint for this location
|
||||
URL: fmt.Sprintf("%s/actions/jobs/%d", repo.APIURL(), job.ID),
|
||||
HTMLURL: fmt.Sprintf("%s/jobs/%d", job.Run.HTMLURL(), jobIndex),
|
||||
HTMLURL: fmt.Sprintf("%s/jobs/%d", job.Run.HTMLURL(), job.ID),
|
||||
RunID: job.RunID,
|
||||
// Missing api endpoint for this location, artifacts are available under a nested url
|
||||
RunURL: fmt.Sprintf("%s/actions/runs/%d", repo.APIURL(), job.RunID),
|
||||
@@ -383,21 +380,22 @@ func ToActionWorkflowJob(ctx context.Context, repo *repo_model.Repository, task
|
||||
Conclusion: conclusion,
|
||||
RunnerID: runnerID,
|
||||
RunnerName: runnerName,
|
||||
Steps: steps,
|
||||
Steps: util.SliceNilAsEmpty(steps),
|
||||
CreatedAt: job.Created.AsTime().UTC(),
|
||||
StartedAt: job.Started.AsTime().UTC(),
|
||||
CompletedAt: job.Stopped.AsTime().UTC(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getActionWorkflowEntry(ctx context.Context, repo *repo_model.Repository, commit *git.Commit, folder string, entry *git.TreeEntry) *api.ActionWorkflow {
|
||||
func getActionWorkflowEntry(ctx context.Context, repo *repo_model.Repository, commit *git.Commit, refName git.RefName, folder string, entry *git.TreeEntry) *api.ActionWorkflow {
|
||||
cfgUnit := repo.MustGetUnit(ctx, unit.TypeActions)
|
||||
cfg := cfgUnit.ActionsConfig()
|
||||
|
||||
defaultBranch, _ := commit.GetBranchName()
|
||||
|
||||
workflowURL := fmt.Sprintf("%s/actions/workflows/%s", repo.APIURL(), util.PathEscapeSegments(entry.Name()))
|
||||
workflowRepoURL := fmt.Sprintf("%s/src/branch/%s/%s/%s", repo.HTMLURL(ctx), util.PathEscapeSegments(defaultBranch), util.PathEscapeSegments(folder), util.PathEscapeSegments(entry.Name()))
|
||||
workflowRepoURL := fmt.Sprintf("%s/src/commit/%s/%s/%s", repo.HTMLURL(ctx), commit.ID.String(), util.PathEscapeSegments(folder), util.PathEscapeSegments(entry.Name()))
|
||||
if refWebLinkPath := refName.RefWebLinkPath(); refWebLinkPath != "" {
|
||||
workflowRepoURL = fmt.Sprintf("%s/src/%s/%s/%s", repo.HTMLURL(ctx), refWebLinkPath, util.PathEscapeSegments(folder), util.PathEscapeSegments(entry.Name()))
|
||||
}
|
||||
badgeURL := fmt.Sprintf("%s/actions/workflows/%s/badge.svg?branch=%s", repo.HTMLURL(ctx), util.PathEscapeSegments(entry.Name()), url.QueryEscape(repo.DefaultBranch))
|
||||
|
||||
// See https://docs.github.com/en/rest/actions/workflows?apiVersion=2022-11-28#get-a-workflow
|
||||
@@ -462,21 +460,47 @@ func ListActionWorkflows(ctx context.Context, gitrepo *git.Repository, repo *rep
|
||||
|
||||
workflows := make([]*api.ActionWorkflow, len(entries))
|
||||
for i, entry := range entries {
|
||||
workflows[i] = getActionWorkflowEntry(ctx, repo, defaultBranchCommit, folder, entry)
|
||||
workflows[i] = getActionWorkflowEntry(ctx, repo, defaultBranchCommit, git.RefNameFromBranch(repo.DefaultBranch), folder, entry)
|
||||
}
|
||||
|
||||
return workflows, nil
|
||||
}
|
||||
|
||||
func GetActionWorkflow(ctx context.Context, gitrepo *git.Repository, repo *repo_model.Repository, workflowID string) (*api.ActionWorkflow, error) {
|
||||
entries, err := ListActionWorkflows(ctx, gitrepo, repo)
|
||||
defaultBranchCommit, err := gitrepo.GetBranchCommit(repo.DefaultBranch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return getActionWorkflowFromCommit(ctx, repo, defaultBranchCommit, git.RefNameFromBranch(repo.DefaultBranch), workflowID)
|
||||
}
|
||||
|
||||
func GetActionWorkflowByRef(ctx context.Context, gitrepo *git.Repository, repo *repo_model.Repository, workflowID string, ref git.RefName) (*api.ActionWorkflow, error) {
|
||||
if ref == "" {
|
||||
return nil, util.NewNotExistErrorf("workflow %q not found", workflowID)
|
||||
}
|
||||
|
||||
refCommitID, err := gitrepo.GetRefCommitID(ref.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
refCommit, err := gitrepo.GetCommit(refCommitID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return getActionWorkflowFromCommit(ctx, repo, refCommit, ref, workflowID)
|
||||
}
|
||||
|
||||
func getActionWorkflowFromCommit(ctx context.Context, repo *repo_model.Repository, commit *git.Commit, refName git.RefName, workflowID string) (*api.ActionWorkflow, error) {
|
||||
folder, entries, err := actions.ListWorkflows(commit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.ID == workflowID {
|
||||
return entry, nil
|
||||
if entry.Name() == workflowID {
|
||||
return getActionWorkflowEntry(ctx, repo, commit, refName, folder, entry), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -524,6 +548,7 @@ func ToActionRunner(ctx context.Context, runner *actions_model.ActionRunner) *ap
|
||||
Name: runner.Name,
|
||||
Status: apiStatus,
|
||||
Busy: status == runnerv1.RunnerStatus_RUNNER_STATUS_ACTIVE,
|
||||
Disabled: runner.IsDisabled,
|
||||
Ephemeral: runner.Ephemeral,
|
||||
Labels: labels,
|
||||
}
|
||||
@@ -703,8 +728,8 @@ func ToAnnotatedTag(ctx context.Context, repo *repo_model.Repository, t *git.Tag
|
||||
Tag: t.Name,
|
||||
SHA: t.ID.String(),
|
||||
Object: ToAnnotatedTagObject(repo, c),
|
||||
Message: t.Message,
|
||||
URL: util.URLJoin(repo.APIURL(), "git/tags", t.ID.String()),
|
||||
Message: strings.ToValidUTF8(t.Message, "?"),
|
||||
URL: repo.APIURL() + "/git/tags/" + t.ID.String(),
|
||||
Tagger: ToCommitUser(t.Tagger),
|
||||
Verification: ToVerification(ctx, c),
|
||||
}
|
||||
@@ -715,7 +740,7 @@ func ToAnnotatedTagObject(repo *repo_model.Repository, commit *git.Commit) *api.
|
||||
return &api.AnnotatedTagObject{
|
||||
SHA: commit.ID.String(),
|
||||
Type: string(git.ObjectCommit),
|
||||
URL: util.URLJoin(repo.APIURL(), "git/commits", commit.ID.String()),
|
||||
URL: repo.APIURL() + "/git/commits/" + commit.ID.String(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -772,7 +797,7 @@ func ToOAuth2Application(app *auth.OAuth2Application) *api.OAuth2Application {
|
||||
|
||||
// ToLFSLock convert a LFSLock to api.LFSLock
|
||||
func ToLFSLock(ctx context.Context, l *git_model.LFSLock) *api.LFSLock {
|
||||
u, err := user_model.GetUserByID(ctx, l.OwnerID)
|
||||
_, u, err := user_model.GetPossibleUserByID(ctx, l.OwnerID)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
ctx "code.gitea.io/gitea/services/context"
|
||||
"code.gitea.io/gitea/services/gitdiff"
|
||||
)
|
||||
@@ -33,7 +33,7 @@ func ToCommitUser(sig *git.Signature) *api.CommitUser {
|
||||
func ToCommitMeta(repo *repo_model.Repository, tag *git.Tag) *api.CommitMeta {
|
||||
return &api.CommitMeta{
|
||||
SHA: tag.Object.String(),
|
||||
URL: util.URLJoin(repo.APIURL(), "git/commits", tag.ID.String()),
|
||||
URL: repo.APIURL() + "/git/commits/" + tag.ID.String(),
|
||||
Created: tag.Tagger.When,
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ func ToPayloadCommit(ctx context.Context, repo *repo_model.Repository, c *git.Co
|
||||
return &api.PayloadCommit{
|
||||
ID: c.ID.String(),
|
||||
Message: c.Message(),
|
||||
URL: util.URLJoin(repo.HTMLURL(), "commit", c.ID.String()),
|
||||
URL: repo.HTMLURL() + "/commit/" + c.ID.String(),
|
||||
Author: &api.PayloadUser{
|
||||
Name: c.Author.Name,
|
||||
Email: c.Author.Email,
|
||||
@@ -190,7 +190,7 @@ func ToCommit(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Rep
|
||||
|
||||
// Retrieve files affected by the commit
|
||||
if opts.Files {
|
||||
fileStatus, err := git.GetCommitFileStatus(gitRepo.Ctx, repo.RepoPath(), commit.ID.String())
|
||||
fileStatus, err := gitrepo.GetCommitFileStatus(ctx, repo, commit.ID.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -210,7 +210,7 @@ func ToCommit(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Rep
|
||||
|
||||
// Get diff stats for commit
|
||||
if opts.Stat {
|
||||
diffShortStat, err := gitdiff.GetDiffShortStat(gitRepo, "", commit.ID.String())
|
||||
diffShortStat, err := gitdiff.GetDiffShortStat(ctx, repo, gitRepo, "", commit.ID.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -35,7 +34,7 @@ func TestToCommitMeta(t *testing.T) {
|
||||
assert.NotNil(t, commitMeta)
|
||||
assert.Equal(t, &api.CommitMeta{
|
||||
SHA: sha1.EmptyObjectID().String(),
|
||||
URL: util.URLJoin(headRepo.APIURL(), "git/commits", sha1.EmptyObjectID().String()),
|
||||
URL: headRepo.APIURL() + "/git/commits/" + sha1.EmptyObjectID().String(),
|
||||
Created: time.Unix(0, 0),
|
||||
}, commitMeta)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
"code.gitea.io/gitea/modules/label"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@@ -61,7 +62,8 @@ func toIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Iss
|
||||
Updated: issue.UpdatedUnix.AsTime(),
|
||||
PinOrder: util.Iif(issue.PinOrder == -1, 0, issue.PinOrder), // -1 means loaded with no pin order
|
||||
|
||||
TimeEstimate: issue.TimeEstimate,
|
||||
TimeEstimate: issue.TimeEstimate,
|
||||
ContentVersion: issue.ContentVersion,
|
||||
}
|
||||
|
||||
if issue.Repo != nil {
|
||||
@@ -199,7 +201,7 @@ func ToStopWatches(ctx context.Context, doer *user_model.User, sws []*issues_mod
|
||||
// ADD: Check user permissions
|
||||
perm, ok := permCache[repo.ID]
|
||||
if !ok {
|
||||
perm, err = access_model.GetUserRepoPermission(ctx, repo, doer)
|
||||
perm, err = access_model.GetDoerRepoPermission(ctx, repo, doer)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -226,7 +228,21 @@ func ToStopWatches(ctx context.Context, doer *user_model.User, sws []*issues_mod
|
||||
// ToTrackedTimeList converts TrackedTimeList to API format
|
||||
func ToTrackedTimeList(ctx context.Context, doer *user_model.User, tl issues_model.TrackedTimeList) api.TrackedTimeList {
|
||||
result := make([]*api.TrackedTime, 0, len(tl))
|
||||
permCache := cache.NewEphemeralCache()
|
||||
for _, t := range tl {
|
||||
// If the issue is not loaded, conservatively skip this entry to avoid bypassing permission checks.
|
||||
if t.Issue == nil || t.Issue.Repo == nil {
|
||||
continue
|
||||
}
|
||||
perm, err := cache.GetWithEphemeralCache(ctx, permCache, "repo-perm", t.Issue.RepoID, func(ctx context.Context, repoID int64) (access_model.Permission, error) {
|
||||
return access_model.GetDoerRepoPermission(ctx, t.Issue.Repo, doer)
|
||||
})
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if !perm.CanReadIssuesOrPulls(t.Issue.IsPull) {
|
||||
continue
|
||||
}
|
||||
result = append(result, ToTrackedTime(ctx, doer, t))
|
||||
}
|
||||
return result
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestLabel_ToLabel(t *testing.T) {
|
||||
@@ -83,3 +84,43 @@ func TestToStopWatchesRespectsPermissions(t *testing.T) {
|
||||
assert.Len(t, visibleAdmin, 2)
|
||||
assert.ElementsMatch(t, []string{"repo1", "repo3"}, []string{visibleAdmin[0].RepoName, visibleAdmin[1].RepoName})
|
||||
}
|
||||
|
||||
func TestToTrackedTime(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
ctx := t.Context()
|
||||
publicIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: 1})
|
||||
privateIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: 3})
|
||||
regularUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
|
||||
adminUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
|
||||
publicTrackedTime := &issues_model.TrackedTime{IssueID: publicIssue.ID, UserID: regularUser.ID, Time: 3600}
|
||||
privateTrackedTime := &issues_model.TrackedTime{IssueID: privateIssue.ID, UserID: regularUser.ID, Time: 1800}
|
||||
require.NoError(t, db.Insert(ctx, publicTrackedTime))
|
||||
require.NoError(t, db.Insert(ctx, privateTrackedTime))
|
||||
|
||||
t.Run("NilIssues", func(t *testing.T) {
|
||||
list := ToTrackedTimeList(ctx, regularUser, issues_model.TrackedTimeList{publicTrackedTime, privateTrackedTime})
|
||||
assert.Empty(t, list)
|
||||
})
|
||||
|
||||
t.Run("NilRepo", func(t *testing.T) {
|
||||
badTrackedTime := &issues_model.TrackedTime{Issue: &issues_model.Issue{RepoID: 999999}}
|
||||
visible := ToTrackedTimeList(ctx, regularUser, issues_model.TrackedTimeList{badTrackedTime})
|
||||
assert.Empty(t, visible)
|
||||
})
|
||||
|
||||
trackedTimes := issues_model.TrackedTimeList{publicTrackedTime, privateTrackedTime}
|
||||
require.NoError(t, trackedTimes.LoadAttributes(ctx))
|
||||
|
||||
t.Run("ToRegularUser", func(t *testing.T) {
|
||||
list := ToTrackedTimeList(ctx, regularUser, trackedTimes)
|
||||
require.Len(t, list, 1)
|
||||
assert.Equal(t, "repo1", list[0].Issue.Repo.Name)
|
||||
})
|
||||
t.Run("ToAdminUser", func(t *testing.T) {
|
||||
list := ToTrackedTimeList(ctx, adminUser, trackedTimes)
|
||||
require.Len(t, list, 2)
|
||||
assert.ElementsMatch(t, []string{"repo1", "repo3"}, []string{list[0].Issue.Repo.Name, list[1].Issue.Repo.Name})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ func ToNotificationThread(ctx context.Context, n *activities_model.Notification)
|
||||
|
||||
// since user only get notifications when he has access to use minimal access mode
|
||||
if n.Repository != nil {
|
||||
perm, err := access_model.GetUserRepoPermission(ctx, n.Repository, n.User)
|
||||
perm, err := access_model.GetIndividualUserRepoPermission(ctx, n.Repository, n.User)
|
||||
if err != nil {
|
||||
log.Error("GetUserRepoPermission failed: %v", err)
|
||||
log.Error("GetIndividualUserRepoPermission failed: %v", err)
|
||||
return result
|
||||
}
|
||||
if perm.HasAnyUnitAccessOrPublicAccess() { // if user has been revoked access to repo, do not show repo info
|
||||
@@ -47,7 +47,7 @@ func ToNotificationThread(ctx context.Context, n *activities_model.Notification)
|
||||
result.Subject.Title = n.Issue.Title
|
||||
result.Subject.URL = n.Issue.APIURL(ctx)
|
||||
result.Subject.HTMLURL = n.Issue.HTMLURL(ctx)
|
||||
result.Subject.State = n.Issue.State()
|
||||
result.Subject.State = api.NotifySubjectStateType(n.Issue.State())
|
||||
comment, err := n.Issue.GetLastComment(ctx)
|
||||
if err == nil && comment != nil {
|
||||
result.Subject.LatestCommentURL = comment.APIURL(ctx)
|
||||
@@ -60,7 +60,7 @@ func ToNotificationThread(ctx context.Context, n *activities_model.Notification)
|
||||
result.Subject.Title = n.Issue.Title
|
||||
result.Subject.URL = n.Issue.APIURL(ctx)
|
||||
result.Subject.HTMLURL = n.Issue.HTMLURL(ctx)
|
||||
result.Subject.State = n.Issue.State()
|
||||
result.Subject.State = api.NotifySubjectStateType(n.Issue.State())
|
||||
comment, err := n.Issue.GetLastComment(ctx)
|
||||
if err == nil && comment != nil {
|
||||
result.Subject.LatestCommentURL = comment.APIURL(ctx)
|
||||
@@ -70,7 +70,7 @@ func ToNotificationThread(ctx context.Context, n *activities_model.Notification)
|
||||
if err := n.Issue.LoadPullRequest(ctx); err == nil &&
|
||||
n.Issue.PullRequest != nil &&
|
||||
n.Issue.PullRequest.HasMerged {
|
||||
result.Subject.State = "merged"
|
||||
result.Subject.State = api.NotifySubjectStateMerged
|
||||
}
|
||||
}
|
||||
case activities_model.NotificationSourceCommit:
|
||||
|
||||
@@ -7,12 +7,15 @@ import (
|
||||
"testing"
|
||||
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestToNotificationThreadIncludesRepoForAccessibleUser(t *testing.T) {
|
||||
@@ -36,6 +39,78 @@ func TestToNotificationThreadOmitsRepoWhenAccessRevoked(t *testing.T) {
|
||||
assert.Nil(t, thread.Repository)
|
||||
}
|
||||
|
||||
func TestToNotificationThread(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
t.Run("issue notification", func(t *testing.T) {
|
||||
// Notification 1: source=issue, issue_id=1, status=unread
|
||||
n := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 1})
|
||||
require.NoError(t, n.LoadAttributes(t.Context()))
|
||||
|
||||
thread := ToNotificationThread(t.Context(), n)
|
||||
assert.Equal(t, int64(1), thread.ID)
|
||||
assert.True(t, thread.Unread)
|
||||
assert.False(t, thread.Pinned)
|
||||
require.NotNil(t, thread.Subject)
|
||||
assert.Equal(t, api.NotifySubjectIssue, thread.Subject.Type)
|
||||
assert.Equal(t, api.NotifySubjectStateOpen, thread.Subject.State)
|
||||
})
|
||||
|
||||
t.Run("pinned notification", func(t *testing.T) {
|
||||
// Notification 3: status=pinned
|
||||
n := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 3})
|
||||
require.NoError(t, n.LoadAttributes(t.Context()))
|
||||
|
||||
thread := ToNotificationThread(t.Context(), n)
|
||||
assert.False(t, thread.Unread)
|
||||
assert.True(t, thread.Pinned)
|
||||
})
|
||||
|
||||
t.Run("merged pull request returns merged state", func(t *testing.T) {
|
||||
// Issue 2 is a pull request; pull_request 1 has has_merged=true.
|
||||
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID})
|
||||
|
||||
n := &activities_model.Notification{
|
||||
ID: 999,
|
||||
UserID: 2,
|
||||
RepoID: repo.ID,
|
||||
Status: activities_model.NotificationStatusUnread,
|
||||
Source: activities_model.NotificationSourcePullRequest,
|
||||
IssueID: issue.ID,
|
||||
Issue: issue,
|
||||
Repository: repo,
|
||||
}
|
||||
|
||||
thread := ToNotificationThread(t.Context(), n)
|
||||
require.NotNil(t, thread.Subject)
|
||||
assert.Equal(t, api.NotifySubjectPull, thread.Subject.Type)
|
||||
assert.Equal(t, api.NotifySubjectStateMerged, thread.Subject.State)
|
||||
})
|
||||
|
||||
t.Run("open pull request returns open state", func(t *testing.T) {
|
||||
// Issue 3 is a pull request; pull_request 2 has has_merged=false.
|
||||
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3})
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID})
|
||||
|
||||
n := &activities_model.Notification{
|
||||
ID: 998,
|
||||
UserID: 2,
|
||||
RepoID: repo.ID,
|
||||
Status: activities_model.NotificationStatusUnread,
|
||||
Source: activities_model.NotificationSourcePullRequest,
|
||||
IssueID: issue.ID,
|
||||
Issue: issue,
|
||||
Repository: repo,
|
||||
}
|
||||
|
||||
thread := ToNotificationThread(t.Context(), n)
|
||||
require.NotNil(t, thread.Subject)
|
||||
assert.Equal(t, api.NotifySubjectPull, thread.Subject.Type)
|
||||
assert.Equal(t, api.NotifySubjectStateOpen, thread.Subject.State)
|
||||
})
|
||||
}
|
||||
|
||||
func newRepoNotification(t *testing.T, repoID, userID int64) *activities_model.Notification {
|
||||
t.Helper()
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
func ToPackage(ctx context.Context, pd *packages.PackageDescriptor, doer *user_model.User) (*api.Package, error) {
|
||||
var repo *api.Repository
|
||||
if pd.Repository != nil {
|
||||
permission, err := access_model.GetUserRepoPermission(ctx, pd.Repository, doer)
|
||||
permission, err := access_model.GetDoerRepoPermission(ctx, pd.Repository, doer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -63,11 +63,11 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
|
||||
|
||||
repoUserPerm, err := cache.GetWithContextCache(ctx, cachegroup.RepoUserPermission, fmt.Sprintf("%d-%d", pr.BaseRepoID, doerID),
|
||||
func(ctx context.Context, _ string) (access_model.Permission, error) {
|
||||
return access_model.GetUserRepoPermission(ctx, pr.BaseRepo, doer)
|
||||
return access_model.GetDoerRepoPermission(ctx, pr.BaseRepo, doer)
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Error("GetUserRepoPermission[%d]: %v", pr.BaseRepoID, err)
|
||||
log.Error("GetDoerRepoPermission[%d]: %v", pr.BaseRepoID, err)
|
||||
repoUserPerm.AccessMode = perm.AccessModeNone
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
|
||||
Created: pr.Issue.CreatedUnix.AsTimePtr(),
|
||||
Updated: pr.Issue.UpdatedUnix.AsTimePtr(),
|
||||
PinOrder: util.Iif(apiIssue.PinOrder == -1, 0, apiIssue.PinOrder),
|
||||
ContentVersion: apiIssue.ContentVersion,
|
||||
|
||||
// output "[]" rather than null to align to github outputs
|
||||
RequestedReviewers: []*api.User{},
|
||||
@@ -146,7 +147,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
|
||||
|
||||
gitRepo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo)
|
||||
if err != nil {
|
||||
log.Error("OpenRepository[%s]: %v", pr.BaseRepo.RepoPath(), err)
|
||||
log.Error("OpenRepository[%s]: %v", pr.BaseRepo.RelativePath(), err)
|
||||
return nil
|
||||
}
|
||||
defer gitRepo.Close()
|
||||
@@ -181,9 +182,9 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
|
||||
}
|
||||
|
||||
if pr.HeadRepo != nil && pr.Flow == issues_model.PullRequestFlowGithub {
|
||||
p, err := access_model.GetUserRepoPermission(ctx, pr.HeadRepo, doer)
|
||||
p, err := access_model.GetDoerRepoPermission(ctx, pr.HeadRepo, doer)
|
||||
if err != nil {
|
||||
log.Error("GetUserRepoPermission[%d]: %v", pr.HeadRepoID, err)
|
||||
log.Error("GetDoerRepoPermission[%d]: %v", pr.HeadRepoID, err)
|
||||
p.AccessMode = perm.AccessModeNone
|
||||
}
|
||||
|
||||
@@ -192,7 +193,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
|
||||
|
||||
headGitRepo, err := gitrepo.OpenRepository(ctx, pr.HeadRepo)
|
||||
if err != nil {
|
||||
log.Error("OpenRepository[%s]: %v", pr.HeadRepo.RepoPath(), err)
|
||||
log.Error("OpenRepository[%s]: %v", pr.HeadRepo.RelativePath(), err)
|
||||
return nil
|
||||
}
|
||||
defer headGitRepo.Close()
|
||||
@@ -235,7 +236,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
|
||||
// Calculate diff
|
||||
startCommitID = pr.MergeBase
|
||||
|
||||
diffShortStats, err := gitdiff.GetDiffShortStat(gitRepo, startCommitID, endCommitID)
|
||||
diffShortStats, err := gitdiff.GetDiffShortStat(ctx, pr.BaseRepo, gitRepo, startCommitID, endCommitID)
|
||||
if err != nil {
|
||||
log.Error("GetDiffShortStat: %v", err)
|
||||
} else {
|
||||
@@ -248,7 +249,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
|
||||
if len(apiPullRequest.Head.Sha) == 0 && len(apiPullRequest.Head.Ref) != 0 {
|
||||
baseGitRepo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo)
|
||||
if err != nil {
|
||||
log.Error("OpenRepository[%s]: %v", pr.BaseRepo.RepoPath(), err)
|
||||
log.Error("OpenRepository[%s]: %v", pr.BaseRepo.RelativePath(), err)
|
||||
return nil
|
||||
}
|
||||
defer baseGitRepo.Close()
|
||||
@@ -334,9 +335,9 @@ func ToAPIPullRequests(ctx context.Context, baseRepo *repo_model.Repository, prs
|
||||
}
|
||||
defer gitRepo.Close()
|
||||
|
||||
baseRepoPerm, err := access_model.GetUserRepoPermission(ctx, baseRepo, doer)
|
||||
baseRepoPerm, err := access_model.GetDoerRepoPermission(ctx, baseRepo, doer)
|
||||
if err != nil {
|
||||
log.Error("GetUserRepoPermission[%d]: %v", baseRepo.ID, err)
|
||||
log.Error("GetDoerRepoPermission[%d]: %v", baseRepo.ID, err)
|
||||
baseRepoPerm.AccessMode = perm.AccessModeNone
|
||||
}
|
||||
|
||||
@@ -372,6 +373,7 @@ func ToAPIPullRequests(ctx context.Context, baseRepo *repo_model.Repository, prs
|
||||
Created: pr.Issue.CreatedUnix.AsTimePtr(),
|
||||
Updated: pr.Issue.UpdatedUnix.AsTimePtr(),
|
||||
PinOrder: util.Iif(apiIssue.PinOrder == -1, 0, apiIssue.PinOrder),
|
||||
ContentVersion: apiIssue.ContentVersion,
|
||||
|
||||
AllowMaintainerEdit: pr.AllowMaintainerEdit,
|
||||
|
||||
@@ -435,9 +437,9 @@ func ToAPIPullRequests(ctx context.Context, baseRepo *repo_model.Repository, prs
|
||||
apiPullRequest.Head.Ref = pr.HeadBranch
|
||||
}
|
||||
if pr.HeadRepoID != pr.BaseRepoID {
|
||||
p, err := access_model.GetUserRepoPermission(ctx, pr.HeadRepo, doer)
|
||||
p, err := access_model.GetDoerRepoPermission(ctx, pr.HeadRepo, doer)
|
||||
if err != nil {
|
||||
log.Error("GetUserRepoPermission[%d]: %v", pr.HeadRepoID, err)
|
||||
log.Error("GetDoerRepoPermission[%d]: %v", pr.HeadRepoID, err)
|
||||
p.AccessMode = perm.AccessModeNone
|
||||
}
|
||||
apiPullRequest.Head.Repository = ToRepo(ctx, pr.HeadRepo, p)
|
||||
|
||||
@@ -92,34 +92,40 @@ func ToPullReviewCommentList(ctx context.Context, review *issues_model.Review, d
|
||||
for _, lines := range review.CodeComments {
|
||||
for _, comments := range lines {
|
||||
for _, comment := range comments {
|
||||
apiComment := &api.PullReviewComment{
|
||||
ID: comment.ID,
|
||||
Body: comment.Content,
|
||||
Poster: ToUser(ctx, comment.Poster, doer),
|
||||
Resolver: ToUser(ctx, comment.ResolveDoer, doer),
|
||||
ReviewID: review.ID,
|
||||
Created: comment.CreatedUnix.AsTime(),
|
||||
Updated: comment.UpdatedUnix.AsTime(),
|
||||
Path: comment.TreePath,
|
||||
CommitID: comment.CommitSHA,
|
||||
OrigCommitID: comment.OldRef,
|
||||
DiffHunk: patch2diff(comment.Patch),
|
||||
HTMLURL: comment.HTMLURL(ctx),
|
||||
HTMLPullURL: review.Issue.HTMLURL(ctx),
|
||||
}
|
||||
|
||||
if comment.Line < 0 {
|
||||
apiComment.OldLineNum = comment.UnsignedLine()
|
||||
} else {
|
||||
apiComment.LineNum = comment.UnsignedLine()
|
||||
}
|
||||
apiComments = append(apiComments, apiComment)
|
||||
apiComments = append(apiComments, ToPullReviewComment(ctx, comment, doer))
|
||||
}
|
||||
}
|
||||
}
|
||||
return apiComments, nil
|
||||
}
|
||||
|
||||
// ToPullReviewComment convert a single code review comment to api format
|
||||
func ToPullReviewComment(ctx context.Context, comment *issues_model.Comment, doer *user_model.User) *api.PullReviewComment {
|
||||
apiComment := &api.PullReviewComment{
|
||||
ID: comment.ID,
|
||||
Body: comment.Content,
|
||||
Poster: ToUser(ctx, comment.Poster, doer),
|
||||
Resolver: ToUser(ctx, comment.ResolveDoer, doer),
|
||||
ReviewID: comment.ReviewID,
|
||||
Created: comment.CreatedUnix.AsTime(),
|
||||
Updated: comment.UpdatedUnix.AsTime(),
|
||||
Path: comment.TreePath,
|
||||
CommitID: comment.CommitSHA,
|
||||
OrigCommitID: comment.OldRef,
|
||||
DiffHunk: patch2diff(comment.Patch),
|
||||
HTMLURL: comment.HTMLURL(ctx),
|
||||
HTMLPullURL: comment.Issue.HTMLURL(ctx),
|
||||
}
|
||||
|
||||
if comment.Line < 0 {
|
||||
apiComment.OldLineNum = comment.UnsignedLine()
|
||||
} else {
|
||||
apiComment.LineNum = comment.UnsignedLine()
|
||||
}
|
||||
|
||||
return apiComment
|
||||
}
|
||||
|
||||
func patch2diff(patch string) string {
|
||||
split := strings.Split(patch, "\n@@")
|
||||
if len(split) == 2 {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
@@ -34,7 +35,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
|
||||
permissionInRepo.SetUnitsWithDefaultAccessMode(repo.Units, permissionInRepo.AccessMode)
|
||||
}
|
||||
|
||||
// TODO: ideally we should pass "doer" into "ToRepo" to to make CloneLink could generate user-related links
|
||||
// TODO: ideally we should pass "doer" into "ToRepo" to make CloneLink could generate user-related links
|
||||
// And passing "doer" in will also fix other FIXMEs in this file.
|
||||
cloneLink := repo.CloneLinkGeneral(ctx) // no doer at the moment
|
||||
permission := &api.Permission{
|
||||
@@ -103,6 +104,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
|
||||
defaultDeleteBranchAfterMerge := false
|
||||
defaultMergeStyle := repo_model.MergeStyleMerge
|
||||
defaultAllowMaintainerEdit := false
|
||||
defaultTargetBranch := ""
|
||||
if unit, err := repo.GetUnit(ctx, unit_model.TypePullRequests); err == nil {
|
||||
config := unit.PullRequestsConfig()
|
||||
hasPullRequests = true
|
||||
@@ -116,8 +118,9 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
|
||||
allowManualMerge = config.AllowManualMerge
|
||||
autodetectManualMerge = config.AutodetectManualMerge
|
||||
defaultDeleteBranchAfterMerge = config.DefaultDeleteBranchAfterMerge
|
||||
defaultMergeStyle = config.GetDefaultMergeStyle()
|
||||
defaultMergeStyle = config.DefaultMergeStyle
|
||||
defaultAllowMaintainerEdit = config.DefaultAllowMaintainerEdit
|
||||
defaultTargetBranch = config.DefaultTargetBranch
|
||||
}
|
||||
hasProjects := false
|
||||
projectsMode := repo_model.ProjectsModeAll
|
||||
@@ -142,6 +145,11 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
|
||||
RepoID: repo.ID,
|
||||
})
|
||||
|
||||
branchCount, err := git_model.CountBranches(ctx, repo.ID, false)
|
||||
if err != nil {
|
||||
log.Error("CountBranches [%d]: %v", repo.ID, err)
|
||||
}
|
||||
|
||||
mirrorInterval := ""
|
||||
var mirrorUpdated time.Time
|
||||
if repo.IsMirror {
|
||||
@@ -203,6 +211,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
|
||||
Stars: repo.NumStars,
|
||||
Forks: repo.NumForks,
|
||||
Watchers: repo.NumWatches,
|
||||
BranchCount: int(branchCount),
|
||||
OpenIssues: repo.NumOpenIssues,
|
||||
OpenPulls: repo.NumOpenPulls,
|
||||
Releases: int(numReleases),
|
||||
@@ -235,6 +244,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
|
||||
DefaultDeleteBranchAfterMerge: defaultDeleteBranchAfterMerge,
|
||||
DefaultMergeStyle: string(defaultMergeStyle),
|
||||
DefaultAllowMaintainerEdit: defaultAllowMaintainerEdit,
|
||||
DefaultTargetBranch: defaultTargetBranch,
|
||||
AvatarURL: repo.AvatarLink(ctx),
|
||||
Internal: !repo.IsPrivate && repo.Owner.Visibility == api.VisibleTypePrivate,
|
||||
MirrorInterval: mirrorInterval,
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/commitstatus"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
|
||||
@@ -55,6 +56,8 @@ func ToCombinedStatus(ctx context.Context, commitID string, statuses []*git_mode
|
||||
if combinedStatus != nil {
|
||||
status.Statuses = ToCommitStatuses(ctx, statuses)
|
||||
status.State = combinedStatus.State
|
||||
} else {
|
||||
status.State = commitstatus.CommitStatusPending
|
||||
}
|
||||
return &status
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func ToWikiCommit(commit *git.Commit) *api.WikiCommit {
|
||||
},
|
||||
Date: commit.Committer.When.UTC().Format(time.RFC3339),
|
||||
},
|
||||
Message: commit.CommitMessage,
|
||||
Message: commit.Message(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user