mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2026-05-13 22:40:24 +00:00
Fixes #9629. New pull mirrors have credentials stored encrypted in the database, the same as push mirrors, rather than in the repository's `config` file. `git fetch` on the pull mirror is updated to use the credential store. Pull mirrors will have their credentials migrated to the encrypted storage in the database as they're synced or otherwise accessed via the web UI. ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. All work and communication must conform to Forgejo's [AI Agreement](https://codeberg.org/forgejo/governance/src/branch/main/AIAgreement.md). There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests for Go changes - I added test coverage for Go changes... - [ ] in their respective `*_test.go` for unit tests. - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I ran... - [x] `make pr-go` before pushing ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [x] This change will be noticed by a Forgejo user or admin (feature, bug fix, performance, etc.). I suggest to include a release note for this change. - [ ] This change is not visible to a Forgejo user or admin (refactor, dependency upgrade, etc.). I think there is no need to add a release note for this change. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11909 Reviewed-by: Gusted <gusted@noreply.codeberg.org> Reviewed-by: Andreas Ahlenstorf <aahlenst@noreply.codeberg.org> Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net> Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>
208 lines
6.6 KiB
Go
208 lines
6.6 KiB
Go
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package templates
|
|
|
|
import (
|
|
"context"
|
|
"html/template"
|
|
"mime"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
activities_model "forgejo.org/models/activities"
|
|
asymkey_model "forgejo.org/models/asymkey"
|
|
repo_model "forgejo.org/models/repo"
|
|
user_model "forgejo.org/models/user"
|
|
"forgejo.org/modules/json"
|
|
"forgejo.org/modules/log"
|
|
"forgejo.org/modules/repository"
|
|
"forgejo.org/modules/svg"
|
|
mirror_service "forgejo.org/services/mirror"
|
|
|
|
"github.com/editorconfig/editorconfig-core-go/v2"
|
|
)
|
|
|
|
func SortArrow(normSort, revSort, urlSort string, isDefault bool) template.HTML {
|
|
// if needed
|
|
if len(normSort) == 0 || len(urlSort) == 0 {
|
|
return ""
|
|
}
|
|
|
|
if len(urlSort) == 0 && isDefault {
|
|
// if sort is sorted as default add arrow tho this table header
|
|
if isDefault {
|
|
return svg.RenderHTML("octicon-triangle-down", 16)
|
|
}
|
|
} else {
|
|
// if sort arg is in url test if it correlates with column header sort arguments
|
|
// the direction of the arrow should indicate the "current sort order", up means ASC(normal), down means DESC(rev)
|
|
switch urlSort {
|
|
case normSort:
|
|
// the table is sorted with this header normal
|
|
return svg.RenderHTML("octicon-triangle-up", 16)
|
|
case revSort:
|
|
// the table is sorted with this header reverse
|
|
return svg.RenderHTML("octicon-triangle-down", 16)
|
|
}
|
|
}
|
|
// the table is NOT sorted with this header
|
|
return ""
|
|
}
|
|
|
|
// IsMultilineCommitMessage checks to see if a commit message contains multiple lines.
|
|
func IsMultilineCommitMessage(msg string) bool {
|
|
return strings.Count(strings.TrimSpace(msg), "\n") >= 1
|
|
}
|
|
|
|
// Actioner describes an action
|
|
type Actioner interface {
|
|
GetOpType() activities_model.ActionType
|
|
GetActUserName(ctx context.Context) string
|
|
GetRepo(ctx context.Context) *repo_model.Repository
|
|
GetRepoUserName(ctx context.Context) string
|
|
GetRepoName(ctx context.Context) string
|
|
GetRepoPath(ctx context.Context) string
|
|
GetRepoLink(ctx context.Context) string
|
|
GetBranch() string
|
|
GetContent() string
|
|
GetCreate() time.Time
|
|
GetIssueInfos() []string
|
|
}
|
|
|
|
// ActionIcon accepts an action operation type and returns an icon class name.
|
|
func ActionIcon(opType activities_model.ActionType) string {
|
|
switch opType {
|
|
case activities_model.ActionCreateRepo, activities_model.ActionTransferRepo, activities_model.ActionRenameRepo:
|
|
return "repo"
|
|
case activities_model.ActionCommitRepo:
|
|
return "git-commit"
|
|
case activities_model.ActionDeleteBranch:
|
|
return "git-branch"
|
|
case activities_model.ActionMergePullRequest, activities_model.ActionAutoMergePullRequest:
|
|
return "git-merge"
|
|
case activities_model.ActionCreatePullRequest:
|
|
return "git-pull-request"
|
|
case activities_model.ActionClosePullRequest:
|
|
return "git-pull-request-closed"
|
|
case activities_model.ActionCreateIssue:
|
|
return "issue-opened"
|
|
case activities_model.ActionCloseIssue:
|
|
return "issue-closed"
|
|
case activities_model.ActionReopenIssue, activities_model.ActionReopenPullRequest:
|
|
return "issue-reopened"
|
|
case activities_model.ActionCommentIssue, activities_model.ActionCommentPull:
|
|
return "comment-discussion"
|
|
case activities_model.ActionMirrorSyncPush, activities_model.ActionMirrorSyncCreate, activities_model.ActionMirrorSyncDelete:
|
|
return "mirror"
|
|
case activities_model.ActionApprovePullRequest:
|
|
return "check"
|
|
case activities_model.ActionRejectPullRequest:
|
|
return "file-diff"
|
|
case activities_model.ActionPublishRelease, activities_model.ActionPushTag, activities_model.ActionDeleteTag:
|
|
return "tag"
|
|
case activities_model.ActionPullReviewDismissed:
|
|
return "x"
|
|
default:
|
|
return "question"
|
|
}
|
|
}
|
|
|
|
// ActionContent2Commits converts action content to push commits
|
|
func ActionContent2Commits(ctx context.Context, act Actioner) *repository.PushCommits {
|
|
push := repository.NewPushCommits()
|
|
|
|
if act == nil || act.GetContent() == "" {
|
|
return push
|
|
}
|
|
|
|
if err := json.Unmarshal([]byte(act.GetContent()), push); err != nil {
|
|
log.Error("json.Unmarshal:\n%s\nERROR: %v", act.GetContent(), err)
|
|
}
|
|
|
|
if push.Len == 0 {
|
|
push.Len = len(push.Commits)
|
|
}
|
|
repo := act.GetRepo(ctx)
|
|
for _, commit := range push.Commits {
|
|
gitCommit, err := repository.PushCommitToCommit(commit)
|
|
if err != nil {
|
|
// Only happens if the commit has an invalid sha
|
|
commit.Verification = &asymkey_model.ObjectVerification{
|
|
Verified: false,
|
|
Reason: "git.error.invalid_commit_id",
|
|
}
|
|
continue
|
|
}
|
|
verification := asymkey_model.ParseCommitWithSignature(ctx, gitCommit)
|
|
_ = asymkey_model.CalculateTrustStatus(verification, repo.GetTrustModel(), func(user *user_model.User) (bool, error) {
|
|
return repo_model.IsOwnerMemberCollaborator(ctx, repo, user.ID)
|
|
}, nil)
|
|
commit.Verification = verification
|
|
}
|
|
|
|
return push
|
|
}
|
|
|
|
// MigrationIcon returns a SVG name matching the service an issue/comment was migrated from
|
|
func MigrationIcon(hostname string) string {
|
|
switch hostname {
|
|
case "github.com":
|
|
return "octicon-mark-github"
|
|
default:
|
|
return "gitea-git"
|
|
}
|
|
}
|
|
|
|
type remoteAddress struct {
|
|
Address string
|
|
Username string
|
|
Password string
|
|
}
|
|
|
|
func mirrorRemoteAddress(ctx context.Context, mirror *repo_model.Mirror) remoteAddress {
|
|
ret := remoteAddress{}
|
|
u, err := mirror_service.DecryptOrRecoverRemoteAddress(ctx, mirror)
|
|
if err != nil {
|
|
log.Error("DecryptOrRecoverRemoteAddress %v", err)
|
|
return ret
|
|
}
|
|
|
|
if u.Scheme != "ssh" && u.Scheme != "file" {
|
|
if u.User != nil {
|
|
ret.Username = u.User.Username()
|
|
ret.Password, _ = u.User.Password()
|
|
}
|
|
}
|
|
|
|
// The URL stored in the git repo could contain authentication,
|
|
// erase it, or it will be shown in the UI.
|
|
u.User = nil
|
|
ret.Address = u.String()
|
|
// Why not use m.OriginalURL to set ret.Address?
|
|
// It should be OK to use it, since m.OriginalURL should be the same as the authentication-erased URL from the Git repository.
|
|
// However, the old code has already stored authentication in m.OriginalURL when updating mirror settings.
|
|
// That means we need to use "giturl.Parse" for m.OriginalURL again to ensure authentication is erased.
|
|
// Instead of doing this, why not directly use the authentication-erased URL from the Git repository?
|
|
// It should be the same as long as there are no bugs.
|
|
|
|
return ret
|
|
}
|
|
|
|
func FilenameIsImage(filename string) bool {
|
|
mimeType := mime.TypeByExtension(filepath.Ext(filename))
|
|
return strings.HasPrefix(mimeType, "image/")
|
|
}
|
|
|
|
func TabSizeClass(ec *editorconfig.Editorconfig, filename string) string {
|
|
if ec != nil {
|
|
def, err := ec.GetDefinitionForFilename(filename)
|
|
if err == nil && def.TabWidth >= 1 && def.TabWidth <= 16 {
|
|
return "tab-size-" + strconv.Itoa(def.TabWidth)
|
|
}
|
|
}
|
|
return "tab-size-4"
|
|
}
|