Merge pull request '[v1.22/gitea] week 16 cherry pick to v7.0' (#3235) from earl-warren/forgejo:wip-v7.0-gitea-cherry-pick into v7.0/forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/3235
Reviewed-by: Gergely Nagy <algernon@noreply.codeberg.org>
This commit is contained in:
Earl Warren 2024-04-16 09:31:46 +00:00
commit 335abbbc9d
60 changed files with 341 additions and 311 deletions

View file

@ -114,7 +114,7 @@ func showWebStartupMessage(msg string) {
log.Info("* WorkPath: %s", setting.AppWorkPath)
log.Info("* CustomPath: %s", setting.CustomPath)
log.Info("* ConfigFile: %s", setting.CustomConf)
log.Info("%s", msg)
log.Info("%s", msg) // show startup message
}
func serveInstall(ctx *cli.Context) error {

View file

@ -44,6 +44,9 @@ func (schedules ScheduleList) LoadTriggerUser(ctx context.Context) error {
schedule.TriggerUser = user_model.NewActionsUser()
} else {
schedule.TriggerUser = users[schedule.TriggerUserID]
if schedule.TriggerUser == nil {
schedule.TriggerUser = user_model.NewGhostUser()
}
}
}
return nil

View file

@ -11,6 +11,7 @@ import (
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@ -227,7 +228,9 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask
if runner.RepoID != 0 {
jobCond = builder.Eq{"repo_id": runner.RepoID}
} else if runner.OwnerID != 0 {
jobCond = builder.In("repo_id", builder.Select("id").From("repository").Where(builder.Eq{"owner_id": runner.OwnerID}))
jobCond = builder.In("repo_id", builder.Select("`repository`.id").From("repository").
Join("INNER", "repo_unit", "`repository`.id = `repo_unit`.repo_id").
Where(builder.Eq{"`repository`.owner_id": runner.OwnerID, "`repo_unit`.type": unit.TypeActions}))
}
if jobCond.IsValid() {
jobCond = builder.In("run_id", builder.Select("id").From("action_run").Where(jobCond))

View file

@ -76,23 +76,14 @@ func calcFingerprintNative(publicKeyContent string) (string, error) {
// CalcFingerprint calculate public key's fingerprint
func CalcFingerprint(publicKeyContent string) (string, error) {
// Call the method based on configuration
var (
fnName, fp string
err error
)
if len(setting.SSH.KeygenPath) == 0 {
fnName = "calcFingerprintNative"
fp, err = calcFingerprintNative(publicKeyContent)
} else {
fnName = "calcFingerprintSSHKeygen"
fp, err = calcFingerprintSSHKeygen(publicKeyContent)
}
useNative := setting.SSH.KeygenPath == ""
calcFn := util.Iif(useNative, calcFingerprintNative, calcFingerprintSSHKeygen)
fp, err := calcFn(publicKeyContent)
if err != nil {
if IsErrKeyUnableVerify(err) {
log.Info("%s", publicKeyContent)
return "", err
}
return "", fmt.Errorf("%s: %w", fnName, err)
return "", fmt.Errorf("CalcFingerprint(%s): %w", util.Iif(useNative, "native", "ssh-keygen"), err)
}
return fp, nil
}

View file

@ -53,7 +53,7 @@ func (repo *Repository) IsDependenciesEnabled(ctx context.Context) bool {
var u *RepoUnit
var err error
if u, err = repo.GetUnit(ctx, unit.TypeIssues); err != nil {
log.Trace("%s", err)
log.Trace("IsDependenciesEnabled: %v", err)
return setting.Service.DefaultEnableDependencies
}
return u.IssuesConfig().EnableDependencies

View file

@ -142,35 +142,39 @@ type remoteAddress struct {
Password string
}
func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string, ignoreOriginalURL bool) remoteAddress {
a := remoteAddress{}
remoteURL := m.OriginalURL
if ignoreOriginalURL || remoteURL == "" {
var err error
remoteURL, err = git.GetRemoteAddress(ctx, m.RepoPath(), remoteName)
if err != nil {
log.Error("GetRemoteURL %v", err)
return a
}
func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string) remoteAddress {
ret := remoteAddress{}
remoteURL, err := git.GetRemoteAddress(ctx, m.RepoPath(), remoteName)
if err != nil {
log.Error("GetRemoteURL %v", err)
return ret
}
u, err := giturl.Parse(remoteURL)
if err != nil {
log.Error("giturl.Parse %v", err)
return a
return ret
}
if u.Scheme != "ssh" && u.Scheme != "file" {
if u.User != nil {
a.Username = u.User.Username()
a.Password, _ = u.User.Password()
ret.Username = u.User.Username()
ret.Password, _ = u.User.Password()
}
u.User = nil
}
a.Address = u.String()
return a
// 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 {

View file

@ -212,3 +212,11 @@ func ToFloat64(number any) (float64, error) {
func ToPointer[T any](val T) *T {
return &val
}
// Iif is an "inline-if", it returns "trueVal" if "condition" is true, otherwise "falseVal"
func Iif[T any](condition bool, trueVal, falseVal T) T {
if condition {
return trueVal
}
return falseVal
}

View file

@ -311,7 +311,7 @@ func SearchIssues(ctx *context.APIContext) {
ctx.SetLinkHeader(int(total), limit)
ctx.SetTotalCountHeader(total)
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues))
}
// ListIssues list the issues of a repository
@ -548,7 +548,7 @@ func ListIssues(ctx *context.APIContext) {
ctx.SetLinkHeader(int(total), listOptions.PageSize)
ctx.SetTotalCountHeader(total)
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues))
}
func getUserIDForFilter(ctx *context.APIContext, queryName string) int64 {
@ -614,7 +614,7 @@ func GetIssue(ctx *context.APIContext) {
ctx.NotFound()
return
}
ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, issue))
ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, ctx.Doer, issue))
}
// CreateIssue create an issue of a repository
@ -737,7 +737,7 @@ func CreateIssue(ctx *context.APIContext) {
ctx.Error(http.StatusInternalServerError, "GetIssueByID", err)
return
}
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, issue))
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, issue))
}
// EditIssue modify an issue of a repository
@ -913,7 +913,7 @@ func EditIssue(ctx *context.APIContext) {
ctx.InternalServerError(err)
return
}
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, issue))
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, issue))
}
func DeleteIssue(ctx *context.APIContext) {

View file

@ -108,7 +108,7 @@ func ListIssueAttachments(ctx *context.APIContext) {
return
}
ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, issue).Attachments)
ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, ctx.Doer, issue).Attachments)
}
// CreateIssueAttachment creates an attachment and saves the given file

View file

@ -153,7 +153,7 @@ func GetIssueDependencies(ctx *context.APIContext) {
blockerIssues = append(blockerIssues, &blocker.Issue)
}
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, blockerIssues))
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, blockerIssues))
}
// CreateIssueDependency create a new issue dependencies
@ -214,7 +214,7 @@ func CreateIssueDependency(ctx *context.APIContext) {
return
}
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, target))
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, target))
}
// RemoveIssueDependency remove an issue dependency
@ -275,7 +275,7 @@ func RemoveIssueDependency(ctx *context.APIContext) {
return
}
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, target))
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, target))
}
// GetIssueBlocks list issues that are blocked by this issue
@ -381,7 +381,7 @@ func GetIssueBlocks(ctx *context.APIContext) {
issues = append(issues, &depMeta.Issue)
}
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues))
}
// CreateIssueBlocking block the issue given in the body by the issue in path
@ -438,7 +438,7 @@ func CreateIssueBlocking(ctx *context.APIContext) {
return
}
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, dependency))
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, dependency))
}
// RemoveIssueBlocking unblock the issue given in the body by the issue in path
@ -495,7 +495,7 @@ func RemoveIssueBlocking(ctx *context.APIContext) {
return
}
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, dependency))
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, dependency))
}
func getParamsIssue(ctx *context.APIContext) *issues_model.Issue {

View file

@ -207,7 +207,7 @@ func ListPinnedIssues(ctx *context.APIContext) {
return
}
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues))
}
// ListPinnedPullRequests returns a list of all pinned PRs

View file

@ -138,7 +138,7 @@ func ListTrackedTimes(ctx *context.APIContext) {
}
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, trackedTimes))
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes))
}
// AddTime add time manual to the given issue
@ -225,7 +225,7 @@ func AddTime(ctx *context.APIContext) {
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
return
}
ctx.JSON(http.StatusOK, convert.ToTrackedTime(ctx, trackedTime))
ctx.JSON(http.StatusOK, convert.ToTrackedTime(ctx, user, trackedTime))
}
// ResetIssueTime reset time manual to the given issue
@ -455,7 +455,7 @@ func ListTrackedTimesByUser(ctx *context.APIContext) {
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
return
}
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, trackedTimes))
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes))
}
// ListTrackedTimesByRepository lists all tracked times of the repository
@ -567,7 +567,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
}
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, trackedTimes))
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes))
}
// ListMyTrackedTimes lists all tracked times of the current user
@ -629,5 +629,5 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
}
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, trackedTimes))
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes))
}

View file

@ -27,7 +27,7 @@ func GenerateActionsRunnerToken(ctx *context.PrivateContext) {
defer rd.Close()
if err := json.NewDecoder(rd).Decode(&genRequest); err != nil {
log.Error("%v", err)
log.Error("JSON Decode failed: %v", err)
ctx.JSON(http.StatusInternalServerError, private.Response{
Err: err.Error(),
})
@ -36,7 +36,7 @@ func GenerateActionsRunnerToken(ctx *context.PrivateContext) {
owner, repo, err := parseScope(ctx, genRequest.Scope)
if err != nil {
log.Error("%v", err)
log.Error("parseScope failed: %v", err)
ctx.JSON(http.StatusInternalServerError, private.Response{
Err: err.Error(),
})
@ -46,18 +46,18 @@ func GenerateActionsRunnerToken(ctx *context.PrivateContext) {
if errors.Is(err, util.ErrNotExist) || (token != nil && !token.IsActive) {
token, err = actions_model.NewRunnerToken(ctx, owner, repo)
if err != nil {
err := fmt.Sprintf("error while creating runner token: %v", err)
log.Error("%v", err)
errMsg := fmt.Sprintf("error while creating runner token: %v", err)
log.Error("NewRunnerToken failed: %v", errMsg)
ctx.JSON(http.StatusInternalServerError, private.Response{
Err: err,
Err: errMsg,
})
return
}
} else if err != nil {
err := fmt.Sprintf("could not get unactivated runner token: %v", err)
log.Error("%v", err)
errMsg := fmt.Sprintf("could not get unactivated runner token: %v", err)
log.Error("GetLatestRunnerToken failed: %v", errMsg)
ctx.JSON(http.StatusInternalServerError, private.Response{
Err: err,
Err: errMsg,
})
return
}

View file

@ -47,7 +47,7 @@ func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env []
_ = stdoutWriter.Close()
err := readAndVerifyCommitsFromShaReader(stdoutReader, repo, env)
if err != nil {
log.Error("%v", err)
log.Error("readAndVerifyCommitsFromShaReader failed: %v", err)
cancel()
}
_ = stdoutReader.Close()
@ -66,7 +66,6 @@ func readAndVerifyCommitsFromShaReader(input io.ReadCloser, repo *git.Repository
line := scanner.Text()
err := readAndVerifyCommit(line, repo, env)
if err != nil {
log.Error("%v", err)
return err
}
}

View file

@ -35,7 +35,7 @@ func SendEmail(ctx *context.PrivateContext) {
defer rd.Close()
if err := json.NewDecoder(rd).Decode(&mail); err != nil {
log.Error("%v", err)
log.Error("JSON Decode failed: %v", err)
ctx.JSON(http.StatusInternalServerError, private.Response{
Err: err.Error(),
})

View file

@ -403,7 +403,6 @@ func EditUserPost(ctx *context.Context) {
ctx.Data["Err_Password"] = true
ctx.RenderWithErr(ctx.Tr("auth.password_pwned"), tplUserEdit, &form)
case password.IsErrIsPwnedRequest(err):
log.Error("%s", err.Error())
ctx.Data["Err_Password"] = true
ctx.RenderWithErr(ctx.Tr("auth.password_pwned_err"), tplUserEdit, &form)
default:

View file

@ -215,7 +215,6 @@ func ResetPasswdPost(ctx *context.Context) {
case errors.Is(err, password.ErrIsPwned):
ctx.RenderWithErr(ctx.Tr("auth.password_pwned"), tplResetPassword, nil)
case password.IsErrIsPwnedRequest(err):
log.Error("%s", err.Error())
ctx.RenderWithErr(ctx.Tr("auth.password_pwned_err"), tplResetPassword, nil)
default:
ctx.ServerError("UpdateAuth", err)
@ -299,7 +298,6 @@ func MustChangePasswordPost(ctx *context.Context) {
ctx.Data["Err_Password"] = true
ctx.RenderWithErr(ctx.Tr("auth.password_pwned"), tplMustChangePassword, &form)
case password.IsErrIsPwnedRequest(err):
log.Error("%s", err.Error())
ctx.Data["Err_Password"] = true
ctx.RenderWithErr(ctx.Tr("auth.password_pwned_err"), tplMustChangePassword, &form)
default:

View file

@ -7,7 +7,6 @@ import (
"errors"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
@ -195,14 +194,15 @@ func NewProjectPost(ctx *context.Context) {
// ChangeProjectStatus updates the status of a project between "open" and "close"
func ChangeProjectStatus(ctx *context.Context) {
toClose := false
var toClose bool
switch ctx.Params(":action") {
case "open":
toClose = false
case "close":
toClose = true
default:
ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects")
ctx.JSONRedirect(ctx.ContextUser.HomeLink() + "/-/projects")
return
}
id := ctx.ParamsInt64(":id")
@ -210,7 +210,7 @@ func ChangeProjectStatus(ctx *context.Context) {
ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err)
return
}
ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects?state=" + url.QueryEscape(ctx.Params(":action")))
ctx.JSONRedirect(fmt.Sprintf("%s/-/projects/%d", ctx.ContextUser.HomeLink(), id))
}
// DeleteProject delete a project

View file

@ -2179,7 +2179,7 @@ func GetIssueInfo(ctx *context.Context) {
}
}
ctx.JSON(http.StatusOK, convert.ToIssue(ctx, issue))
ctx.JSON(http.StatusOK, convert.ToIssue(ctx, ctx.Doer, issue))
}
// UpdateIssueTitle change issue's title
@ -2713,7 +2713,7 @@ func SearchIssues(ctx *context.Context) {
}
ctx.SetTotalCountHeader(total)
ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, issues))
ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, ctx.Doer, issues))
}
func getUserIDForFilter(ctx *context.Context, queryName string) int64 {
@ -2883,7 +2883,7 @@ func ListIssues(ctx *context.Context) {
}
ctx.SetTotalCountHeader(total)
ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, issues))
ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, ctx.Doer, issues))
}
func BatchDeleteIssues(ctx *context.Context) {

View file

@ -7,7 +7,6 @@ import (
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"code.gitea.io/gitea/models/db"
@ -180,14 +179,10 @@ func ChangeProjectStatus(ctx *context.Context) {
id := ctx.ParamsInt64(":id")
if err := project_model.ChangeProjectStatusByRepoIDAndID(ctx, ctx.Repo.Repository.ID, id, toClose); err != nil {
if project_model.IsErrProjectNotExist(err) {
ctx.NotFound("", err)
} else {
ctx.ServerError("ChangeProjectStatusByIDAndRepoID", err)
}
ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err)
return
}
ctx.JSONRedirect(ctx.Repo.RepoLink + "/projects?state=" + url.QueryEscape(ctx.Params(":action")))
ctx.JSONRedirect(fmt.Sprintf("%s/projects/%d", ctx.Repo.RepoLink, id))
}
// DeleteProject delete a project

View file

@ -74,7 +74,6 @@ func AccountPost(ctx *context.Context) {
case errors.Is(err, password.ErrIsPwned):
ctx.Flash.Error(ctx.Tr("auth.password_pwned"))
case password.IsErrIsPwnedRequest(err):
log.Error("%s", err.Error())
ctx.Flash.Error(ctx.Tr("auth.password_pwned_err"))
default:
ctx.ServerError("UpdateAuth", err)

View file

@ -49,7 +49,7 @@ func (n *actionsNotifier) NewIssue(ctx context.Context, issue *issues_model.Issu
newNotifyInputFromIssue(issue, webhook_module.HookEventIssues).WithPayload(&api.IssuePayload{
Action: api.HookIssueOpened,
Index: issue.Index,
Issue: convert.ToAPIIssue(ctx, issue),
Issue: convert.ToAPIIssue(ctx, issue.Poster, issue),
Repository: convert.ToRepo(ctx, issue.Repo, permission),
Sender: convert.ToUser(ctx, issue.Poster, nil),
}).Notify(withMethod(ctx, "NewIssue"))
@ -89,7 +89,7 @@ func (n *actionsNotifier) IssueChangeContent(ctx context.Context, doer *user_mod
WithPayload(&api.IssuePayload{
Action: api.HookIssueEdited,
Index: issue.Index,
Issue: convert.ToAPIIssue(ctx, issue),
Issue: convert.ToAPIIssue(ctx, doer, issue),
Repository: convert.ToRepo(ctx, issue.Repo, permission),
Sender: convert.ToUser(ctx, doer, nil),
}).
@ -127,7 +127,7 @@ func (n *actionsNotifier) IssueChangeStatus(ctx context.Context, doer *user_mode
}
apiIssue := &api.IssuePayload{
Index: issue.Index,
Issue: convert.ToAPIIssue(ctx, issue),
Issue: convert.ToAPIIssue(ctx, doer, issue),
Repository: convert.ToRepo(ctx, issue.Repo, permission),
Sender: convert.ToUser(ctx, doer, nil),
}
@ -229,7 +229,7 @@ func notifyIssueChange(ctx context.Context, doer *user_model.User, issue *issues
WithPayload(&api.IssuePayload{
Action: action,
Index: issue.Index,
Issue: convert.ToAPIIssue(ctx, issue),
Issue: convert.ToAPIIssue(ctx, doer, issue),
Repository: convert.ToRepo(ctx, issue.Repo, permission),
Sender: convert.ToUser(ctx, doer, nil),
}).
@ -293,7 +293,7 @@ func notifyIssueCommentChange(ctx context.Context, doer *user_model.User, commen
payload := &api.IssueCommentPayload{
Action: action,
Issue: convert.ToAPIIssue(ctx, comment.Issue),
Issue: convert.ToAPIIssue(ctx, doer, comment.Issue),
Comment: convert.ToAPIComment(ctx, comment.Issue.Repo, comment),
Repository: convert.ToRepo(ctx, comment.Issue.Repo, permission),
Sender: convert.ToUser(ctx, doer, nil),

View file

@ -560,12 +560,9 @@ func DetectAndHandleSchedules(ctx context.Context, repo *repo_model.Repository)
}
// We need a notifyInput to call handleSchedules
// Here we use the commit author as the Doer of the notifyInput
commitUser, err := user_model.GetUserByEmail(ctx, commit.Author.Email)
if err != nil {
return fmt.Errorf("get user by email: %w", err)
}
notifyInput := newNotifyInput(repo, commitUser, webhook_module.HookEventSchedule)
// if repo is a mirror, commit author maybe an external user,
// so we use action user as the Doer of the notifyInput
notifyInput := newNotifyInput(repo, user_model.NewActionsUser(), webhook_module.HookEventSchedule)
return handleSchedules(ctx, scheduleWorkflows, commit, notifyInput, repo.DefaultBranch)
}

View file

@ -79,11 +79,11 @@ func VerifyCaptcha(ctx *Context, tpl base.TplName, form any) {
case setting.CfTurnstile:
valid, err = turnstile.Verify(ctx, ctx.Req.Form.Get(cfTurnstileResponseField))
default:
ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType))
ctx.ServerError("Unknown Captcha Type", fmt.Errorf("unknown Captcha Type: %s", setting.Service.CaptchaType))
return
}
if err != nil {
log.Debug("%v", err)
log.Debug("Captcha Verify failed: %v", err)
}
if !valid {

View file

@ -18,19 +18,19 @@ import (
api "code.gitea.io/gitea/modules/structs"
)
func ToIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue {
return toIssue(ctx, issue, WebAssetDownloadURL)
func ToIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue) *api.Issue {
return toIssue(ctx, doer, issue, WebAssetDownloadURL)
}
// ToAPIIssue converts an Issue to API format
// it assumes some fields assigned with values:
// Required - Poster, Labels,
// Optional - Milestone, Assignee, PullRequest
func ToAPIIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue {
return toIssue(ctx, issue, APIAssetDownloadURL)
func ToAPIIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue) *api.Issue {
return toIssue(ctx, doer, issue, APIAssetDownloadURL)
}
func toIssue(ctx context.Context, issue *issues_model.Issue, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Issue {
func toIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Issue {
if err := issue.LoadLabels(ctx); err != nil {
return &api.Issue{}
}
@ -44,7 +44,7 @@ func toIssue(ctx context.Context, issue *issues_model.Issue, getDownloadURL func
apiIssue := &api.Issue{
ID: issue.ID,
Index: issue.Index,
Poster: ToUser(ctx, issue.Poster, nil),
Poster: ToUser(ctx, issue.Poster, doer),
Title: issue.Title,
Body: issue.Content,
Attachments: toAttachments(issue.Repo, issue.Attachments, getDownloadURL),
@ -114,25 +114,25 @@ func toIssue(ctx context.Context, issue *issues_model.Issue, getDownloadURL func
}
// ToIssueList converts an IssueList to API format
func ToIssueList(ctx context.Context, il issues_model.IssueList) []*api.Issue {
func ToIssueList(ctx context.Context, doer *user_model.User, il issues_model.IssueList) []*api.Issue {
result := make([]*api.Issue, len(il))
for i := range il {
result[i] = ToIssue(ctx, il[i])
result[i] = ToIssue(ctx, doer, il[i])
}
return result
}
// ToAPIIssueList converts an IssueList to API format
func ToAPIIssueList(ctx context.Context, il issues_model.IssueList) []*api.Issue {
func ToAPIIssueList(ctx context.Context, doer *user_model.User, il issues_model.IssueList) []*api.Issue {
result := make([]*api.Issue, len(il))
for i := range il {
result[i] = ToAPIIssue(ctx, il[i])
result[i] = ToAPIIssue(ctx, doer, il[i])
}
return result
}
// ToTrackedTime converts TrackedTime to API format
func ToTrackedTime(ctx context.Context, t *issues_model.TrackedTime) (apiT *api.TrackedTime) {
func ToTrackedTime(ctx context.Context, doer *user_model.User, t *issues_model.TrackedTime) (apiT *api.TrackedTime) {
apiT = &api.TrackedTime{
ID: t.ID,
IssueID: t.IssueID,
@ -141,7 +141,7 @@ func ToTrackedTime(ctx context.Context, t *issues_model.TrackedTime) (apiT *api.
Created: t.Created,
}
if t.Issue != nil {
apiT.Issue = ToAPIIssue(ctx, t.Issue)
apiT.Issue = ToAPIIssue(ctx, doer, t.Issue)
}
if t.User != nil {
apiT.UserName = t.User.Name
@ -192,10 +192,10 @@ func ToStopWatches(ctx context.Context, sws []*issues_model.Stopwatch) (api.Stop
}
// ToTrackedTimeList converts TrackedTimeList to API format
func ToTrackedTimeList(ctx context.Context, tl issues_model.TrackedTimeList) api.TrackedTimeList {
func ToTrackedTimeList(ctx context.Context, doer *user_model.User, tl issues_model.TrackedTimeList) api.TrackedTimeList {
result := make([]*api.TrackedTime, 0, len(tl))
for _, t := range tl {
result = append(result, ToTrackedTime(ctx, t))
result = append(result, ToTrackedTime(ctx, doer, t))
}
return result
}

View file

@ -120,7 +120,7 @@ func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issu
return nil
}
comment.TrackedTime = ToTrackedTime(ctx, c.Time)
comment.TrackedTime = ToTrackedTime(ctx, doer, c.Time)
}
if c.RefIssueID != 0 {
@ -129,7 +129,7 @@ func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issu
log.Error("GetIssueByID(%d): %v", c.RefIssueID, err)
return nil
}
comment.RefIssue = ToAPIIssue(ctx, issue)
comment.RefIssue = ToAPIIssue(ctx, doer, issue)
}
if c.RefCommentID != 0 {
@ -180,7 +180,7 @@ func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issu
}
if c.DependentIssue != nil {
comment.DependentIssue = ToAPIIssue(ctx, c.DependentIssue)
comment.DependentIssue = ToAPIIssue(ctx, doer, c.DependentIssue)
}
return comment

View file

@ -33,7 +33,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
return nil
}
apiIssue := ToAPIIssue(ctx, pr.Issue)
apiIssue := ToAPIIssue(ctx, doer, pr.Issue)
if err := pr.LoadBaseRepo(ctx); err != nil {
log.Error("GetRepositoryById[%d]: %v", pr.ID, err)
return nil

View file

@ -72,6 +72,11 @@ func (g *GitBucketDownloader) LogString() string {
// NewGitBucketDownloader creates a GitBucket downloader
func NewGitBucketDownloader(ctx context.Context, baseURL, userName, password, token, repoOwner, repoName string) *GitBucketDownloader {
githubDownloader := NewGithubDownloaderV3(ctx, baseURL, userName, password, token, repoOwner, repoName)
// Gitbucket 4.40 uses different internal hard-coded perPage values.
// Issues, PRs, and other major parts use 25. Release page uses 10.
// Some API doesn't support paging yet. Sounds difficult, but using
// minimum number among them worked out very well.
githubDownloader.maxPerPage = 10
githubDownloader.SkipReactions = true
githubDownloader.SkipReviews = true
return &GitBucketDownloader{

View file

@ -13,6 +13,7 @@ import (
system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git"
giturl "code.gitea.io/gitea/modules/git/url"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
@ -30,10 +31,15 @@ const gitShortEmptySha = "0000000"
// UpdateAddress writes new address to Git repository and database
func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error {
u, err := giturl.Parse(addr)
if err != nil {
return fmt.Errorf("invalid addr: %v", err)
}
remoteName := m.GetRemoteName()
repoPath := m.GetRepository(ctx).RepoPath()
// Remove old remote
_, _, err := git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: repoPath})
_, _, err = git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: repoPath})
if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
return err
}
@ -70,7 +76,9 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error
}
}
m.Repo.OriginalURL = addr
// erase authentication before storing in database
u.User = nil
m.Repo.OriginalURL = u.String()
return repo_model.UpdateRepositoryCols(ctx, m.Repo, "original_url")
}
@ -449,19 +457,17 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
return false
}
var gitRepo *git.Repository
if len(results) == 0 {
log.Trace("SyncMirrors [repo: %-v]: no branches updated", m.Repo)
} else {
log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results))
gitRepo, err = gitrepo.OpenRepository(ctx, m.Repo)
if err != nil {
log.Error("SyncMirrors [repo: %-v]: unable to OpenRepository: %v", m.Repo, err)
return false
}
defer gitRepo.Close()
gitRepo, err := gitrepo.OpenRepository(ctx, m.Repo)
if err != nil {
log.Error("SyncMirrors [repo: %-v]: unable to OpenRepository: %v", m.Repo, err)
return false
}
defer gitRepo.Close()
log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results))
if len(results) > 0 {
if ok := checkAndUpdateEmptyRepository(ctx, m, gitRepo, results); !ok {
log.Error("SyncMirrors [repo: %-v]: checkAndUpdateEmptyRepository: %v", m.Repo, err)
return false
}
}
@ -534,16 +540,24 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
}
log.Trace("SyncMirrors [repo: %-v]: done notifying updated branches/tags - now updating last commit time", m.Repo)
// Get latest commit date and update to current repository updated time
commitDate, err := git.GetLatestCommitTime(ctx, m.Repo.RepoPath())
isEmpty, err := gitRepo.IsEmpty()
if err != nil {
log.Error("SyncMirrors [repo: %-v]: unable to GetLatestCommitDate: %v", m.Repo, err)
log.Error("SyncMirrors [repo: %-v]: unable to check empty git repo: %v", m.Repo, err)
return false
}
if !isEmpty {
// Get latest commit date and update to current repository updated time
commitDate, err := git.GetLatestCommitTime(ctx, m.Repo.RepoPath())
if err != nil {
log.Error("SyncMirrors [repo: %-v]: unable to GetLatestCommitDate: %v", m.Repo, err)
return false
}
if err = repo_model.UpdateRepositoryUpdatedTime(ctx, m.RepoID, commitDate); err != nil {
log.Error("SyncMirrors [repo: %-v]: unable to update repository 'updated_unix': %v", m.Repo, err)
return false
}
if err = repo_model.UpdateRepositoryUpdatedTime(ctx, m.RepoID, commitDate); err != nil {
log.Error("SyncMirrors [repo: %-v]: unable to update repository 'updated_unix': %v", m.Repo, err)
return false
}
log.Trace("SyncMirrors [repo: %-v]: Successfully updated", m.Repo)

View file

@ -91,7 +91,7 @@ func AutoMergePullRequest(ctx context.Context, doer *user_model.User, pr *issues
// NewPullRequest notifies new pull request to notifiers
func NewPullRequest(ctx context.Context, pr *issues_model.PullRequest, mentions []*user_model.User) {
if err := pr.LoadIssue(ctx); err != nil {
log.Error("%v", err)
log.Error("LoadIssue failed: %v", err)
return
}
if err := pr.Issue.LoadPoster(ctx); err != nil {
@ -112,7 +112,7 @@ func PullRequestSynchronized(ctx context.Context, doer *user_model.User, pr *iss
// PullRequestReview notifies new pull request review
func PullRequestReview(ctx context.Context, pr *issues_model.PullRequest, review *issues_model.Review, comment *issues_model.Comment, mentions []*user_model.User) {
if err := review.LoadReviewer(ctx); err != nil {
log.Error("%v", err)
log.Error("LoadReviewer failed: %v", err)
return
}
for _, notifier := range notifiers {

View file

@ -28,7 +28,7 @@ func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_mod
t, err := NewTemporaryUploadRepository(ctx, repo)
if err != nil {
log.Error("%v", err)
log.Error("NewTemporaryUploadRepository failed: %v", err)
}
defer t.Close()
if err := t.Clone(opts.OldBranch, false); err != nil {

View file

@ -111,7 +111,7 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user
t, err := NewTemporaryUploadRepository(ctx, repo)
if err != nil {
log.Error("%v", err)
log.Error("NewTemporaryUploadRepository failed: %v", err)
}
defer t.Close()
if err := t.Clone(opts.OldBranch, true); err != nil {

View file

@ -143,7 +143,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
t, err := NewTemporaryUploadRepository(ctx, repo)
if err != nil {
log.Error("%v", err)
log.Error("NewTemporaryUploadRepository failed: %v", err)
}
defer t.Close()
hasOldBranch := true

View file

@ -67,7 +67,7 @@ func (m *webhookNotifier) IssueClearLabels(ctx context.Context, doer *user_model
err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssueLabel, &api.IssuePayload{
Action: api.HookIssueLabelCleared,
Index: issue.Index,
Issue: convert.ToAPIIssue(ctx, issue),
Issue: convert.ToAPIIssue(ctx, doer, issue),
Repository: convert.ToRepo(ctx, issue.Repo, permission),
Sender: convert.ToUser(ctx, doer, nil),
})
@ -168,7 +168,7 @@ func (m *webhookNotifier) IssueChangeAssignee(ctx context.Context, doer *user_mo
permission, _ := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
apiIssue := &api.IssuePayload{
Index: issue.Index,
Issue: convert.ToAPIIssue(ctx, issue),
Issue: convert.ToAPIIssue(ctx, doer, issue),
Repository: convert.ToRepo(ctx, issue.Repo, permission),
Sender: convert.ToUser(ctx, doer, nil),
}
@ -214,7 +214,7 @@ func (m *webhookNotifier) IssueChangeTitle(ctx context.Context, doer *user_model
From: oldTitle,
},
},
Issue: convert.ToAPIIssue(ctx, issue),
Issue: convert.ToAPIIssue(ctx, doer, issue),
Repository: convert.ToRepo(ctx, issue.Repo, permission),
Sender: convert.ToUser(ctx, doer, nil),
})
@ -250,7 +250,7 @@ func (m *webhookNotifier) IssueChangeStatus(ctx context.Context, doer *user_mode
} else {
apiIssue := &api.IssuePayload{
Index: issue.Index,
Issue: convert.ToAPIIssue(ctx, issue),
Issue: convert.ToAPIIssue(ctx, doer, issue),
Repository: convert.ToRepo(ctx, issue.Repo, permission),
Sender: convert.ToUser(ctx, doer, nil),
CommitID: commitID,
@ -281,7 +281,7 @@ func (m *webhookNotifier) NewIssue(ctx context.Context, issue *issues_model.Issu
if err := PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssues, &api.IssuePayload{
Action: api.HookIssueOpened,
Index: issue.Index,
Issue: convert.ToAPIIssue(ctx, issue),
Issue: convert.ToAPIIssue(ctx, issue.Poster, issue),
Repository: convert.ToRepo(ctx, issue.Repo, permission),
Sender: convert.ToUser(ctx, issue.Poster, nil),
}); err != nil {
@ -349,7 +349,7 @@ func (m *webhookNotifier) IssueChangeContent(ctx context.Context, doer *user_mod
From: oldContent,
},
},
Issue: convert.ToAPIIssue(ctx, issue),
Issue: convert.ToAPIIssue(ctx, doer, issue),
Repository: convert.ToRepo(ctx, issue.Repo, permission),
Sender: convert.ToUser(ctx, doer, nil),
})
@ -384,7 +384,7 @@ func (m *webhookNotifier) UpdateComment(ctx context.Context, doer *user_model.Us
permission, _ := access_model.GetUserRepoPermission(ctx, c.Issue.Repo, doer)
if err := PrepareWebhooks(ctx, EventSource{Repository: c.Issue.Repo}, eventType, &api.IssueCommentPayload{
Action: api.HookIssueCommentEdited,
Issue: convert.ToAPIIssue(ctx, c.Issue),
Issue: convert.ToAPIIssue(ctx, doer, c.Issue),
Comment: convert.ToAPIComment(ctx, c.Issue.Repo, c),
Changes: &api.ChangesPayload{
Body: &api.ChangesFromPayload{
@ -412,7 +412,7 @@ func (m *webhookNotifier) CreateIssueComment(ctx context.Context, doer *user_mod
permission, _ := access_model.GetUserRepoPermission(ctx, repo, doer)
if err := PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, eventType, &api.IssueCommentPayload{
Action: api.HookIssueCommentCreated,
Issue: convert.ToAPIIssue(ctx, issue),
Issue: convert.ToAPIIssue(ctx, doer, issue),
Comment: convert.ToAPIComment(ctx, repo, comment),
Repository: convert.ToRepo(ctx, repo, permission),
Sender: convert.ToUser(ctx, doer, nil),
@ -449,7 +449,7 @@ func (m *webhookNotifier) DeleteComment(ctx context.Context, doer *user_model.Us
permission, _ := access_model.GetUserRepoPermission(ctx, comment.Issue.Repo, doer)
if err := PrepareWebhooks(ctx, EventSource{Repository: comment.Issue.Repo}, eventType, &api.IssueCommentPayload{
Action: api.HookIssueCommentDeleted,
Issue: convert.ToAPIIssue(ctx, comment.Issue),
Issue: convert.ToAPIIssue(ctx, doer, comment.Issue),
Comment: convert.ToAPIComment(ctx, comment.Issue.Repo, comment),
Repository: convert.ToRepo(ctx, comment.Issue.Repo, permission),
Sender: convert.ToUser(ctx, doer, nil),
@ -533,7 +533,7 @@ func (m *webhookNotifier) IssueChangeLabels(ctx context.Context, doer *user_mode
err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssueLabel, &api.IssuePayload{
Action: api.HookIssueLabelUpdated,
Index: issue.Index,
Issue: convert.ToAPIIssue(ctx, issue),
Issue: convert.ToAPIIssue(ctx, doer, issue),
Repository: convert.ToRepo(ctx, issue.Repo, permission),
Sender: convert.ToUser(ctx, doer, nil),
})
@ -575,7 +575,7 @@ func (m *webhookNotifier) IssueChangeMilestone(ctx context.Context, doer *user_m
err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssueMilestone, &api.IssuePayload{
Action: hookAction,
Index: issue.Index,
Issue: convert.ToAPIIssue(ctx, issue),
Issue: convert.ToAPIIssue(ctx, doer, issue),
Repository: convert.ToRepo(ctx, issue.Repo, permission),
Sender: convert.ToUser(ctx, doer, nil),
})

View file

@ -209,7 +209,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
if isOldWikiExist {
err := gitRepo.RemoveFilesFromIndex(oldWikiPath)
if err != nil {
log.Error("%v", err)
log.Error("RemoveFilesFromIndex failed: %v", err)
return err
}
}
@ -219,18 +219,18 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
objectHash, err := gitRepo.HashObject(strings.NewReader(content))
if err != nil {
log.Error("%v", err)
log.Error("HashObject failed: %v", err)
return err
}
if err := gitRepo.AddObjectToIndex("100644", objectHash, newWikiPath); err != nil {
log.Error("%v", err)
log.Error("AddObjectToIndex failed: %v", err)
return err
}
tree, err := gitRepo.WriteTree()
if err != nil {
log.Error("%v", err)
log.Error("WriteTree failed: %v", err)
return err
}
@ -255,7 +255,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
commitHash, err := gitRepo.CommitTree(doer.NewGitSig(), committer, tree, commitTreeOpts)
if err != nil {
log.Error("%v", err)
log.Error("CommitTree failed: %v", err)
return err
}
@ -270,11 +270,11 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
0,
),
}); err != nil {
log.Error("%v", err)
log.Error("Push failed: %v", err)
if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) {
return err
}
return fmt.Errorf("Push: %w", err)
return fmt.Errorf("failed to push: %w", err)
}
return nil

View file

@ -62,10 +62,7 @@
<div class="ui modal admin" id="detail-modal">
<div class="header">{{ctx.Locale.Tr "admin.notices.view_detail_header"}}</div>
<div class="content">
<div class="sub header"></div>
<pre></pre>
</div>
<div class="content"><pre></pre></div>
</div>
{{template "admin/layout_footer" .}}

View file

@ -13,11 +13,9 @@
<div class="ui four wide column">
{{template "shared/user/profile_big_avatar" .}}
</div>
<div class="ui twelve wide column">
<div class="tw-mb-4">
<div class="ui twelve wide column tw-mb-4">
{{template "user/overview/header" .}}
</div>
{{template "projects/list" .}}
{{template "projects/list" .}}
</div>
</div>
</div>

View file

@ -1,7 +1,7 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository projects view-project">
{{template "shared/user/org_profile_avatar" .}}
<div class="ui container">
<div class="ui container tw-mb-4">
{{template "user/overview/header" .}}
</div>
<div class="ui container fluid padded">

View file

@ -18,10 +18,10 @@
{{end}}
{{end}}
<div class="ui top attached header clearing segment tw-relative commit-header {{$class}}">
<div class="tw-flex tw-mb-4 tw-flex-wrap">
<div class="tw-flex tw-mb-4 tw-gap-1">
<h3 class="tw-mb-0 tw-flex-1"><span class="commit-summary" title="{{.Commit.Summary}}">{{RenderCommitMessage $.Context .Commit.Message ($.Repository.ComposeMetas ctx)}}</span>{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses}}</h3>
{{if not $.PageIsWiki}}
<div>
<div class="commit-header-buttons">
<a class="ui primary tiny button" href="{{.SourcePath}}">
{{ctx.Locale.Tr "repo.diff.browse_source"}}
</a>

View file

@ -111,7 +111,7 @@
{{$isReviewFile := and $.IsSigned $.PageIsPullFiles (not $.IsArchived) $.IsShowingAllCommits}}
<div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}} tw-mt-0" id="diff-{{$file.NameHash}}" data-old-filename="{{$file.OldName}}" data-new-filename="{{$file.Name}}" {{if or ($file.ShouldBeHidden) (not $isExpandable)}}data-folded="true"{{end}}>
<h4 class="diff-file-header sticky-2nd-row ui top attached header tw-font-normal tw-flex tw-items-center tw-justify-between tw-flex-wrap">
<div class="diff-file-name tw-flex tw-items-center gt-gap-2 tw-flex-wrap">
<div class="diff-file-name tw-flex tw-flex-1 tw-items-center tw-gap-1 tw-flex-wrap">
<button class="fold-file btn interact-bg tw-p-1{{if not $isExpandable}} tw-invisible{{end}}">
{{if $file.ShouldBeHidden}}
{{svg "octicon-chevron-right" 18}}
@ -128,21 +128,23 @@
{{template "repo/diff/stats" dict "file" . "root" $}}
{{end}}
</div>
<span class="file tw-font-mono"><a class="muted file-link" title="{{if $file.IsRenamed}}{{$file.OldName}}{{end}}{{$file.Name}}" href="#diff-{{$file.NameHash}}">{{if $file.IsRenamed}}{{$file.OldName}}{{end}}{{$file.Name}}</a>{{if .IsLFSFile}} ({{ctx.Locale.Tr "repo.stored_lfs"}}){{end}}</span>
<button class="btn interact-fg tw-p-2" data-clipboard-text="{{$file.Name}}">{{svg "octicon-copy" 14}}</button>
{{if $file.IsGenerated}}
<span class="ui label">{{ctx.Locale.Tr "repo.diff.generated"}}</span>
{{end}}
{{if $file.IsVendored}}
<span class="ui label">{{ctx.Locale.Tr "repo.diff.vendored"}}</span>
{{end}}
{{if and $file.Mode $file.OldMode}}
{{$old := ctx.Locale.Tr ($file.ModeTranslationKey $file.OldMode)}}
{{$new := ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}}
<span class="tw-ml-4 tw-font-mono">{{ctx.Locale.Tr "git.filemode.changed_filemode" $old $new}}</span>
{{else if $file.Mode}}
<span class="tw-ml-4 tw-font-mono">{{ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}}</span>
{{end}}
<span class="file tw-flex tw-items-center tw-font-mono tw-flex-1"><a class="muted file-link" title="{{if $file.IsRenamed}}{{$file.OldName}}{{end}}{{$file.Name}}" href="#diff-{{$file.NameHash}}">{{if $file.IsRenamed}}{{$file.OldName}}{{end}}{{$file.Name}}</a>
{{if .IsLFSFile}} ({{ctx.Locale.Tr "repo.stored_lfs"}}){{end}}
<button class="btn interact-fg tw-p-2" data-clipboard-text="{{$file.Name}}">{{svg "octicon-copy" 14}}</button>
{{if $file.IsGenerated}}
<span class="ui label">{{ctx.Locale.Tr "repo.diff.generated"}}</span>
{{end}}
{{if $file.IsVendored}}
<span class="ui label">{{ctx.Locale.Tr "repo.diff.vendored"}}</span>
{{end}}
{{if and $file.Mode $file.OldMode}}
{{$old := ctx.Locale.Tr ($file.ModeTranslationKey $file.OldMode)}}
{{$new := ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}}
<span class="tw-mx-2 tw-font-mono tw-whitespace-nowrap">{{ctx.Locale.Tr "git.filemode.changed_filemode" $old $new}}</span>
{{else if $file.Mode}}
<span class="tw-mx-2 tw-font-mono tw-whitespace-nowrap">{{ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}}</span>
{{end}}
</span>
</div>
<div class="diff-file-header-actions tw-flex tw-items-center tw-gap-1 tw-flex-wrap">
{{if $showFileViewToggle}}

View file

@ -59,8 +59,11 @@
</div>
{{end}}
{{template "repo/sub_menu" .}}
{{$n := len .TreeNames}}
{{$l := Eval $n "-" 1}}
{{$isHomepage := (eq $n 0)}}
<div class="repo-button-row">
<div class="tw-flex tw-items-center tw-flex-wrap tw-gap-y-2">
<div class="tw-flex tw-items-center tw-gap-y-2">
{{template "repo/branch_dropdown" dict "root" . "ContainerClasses" "tw-mr-1"}}
{{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}}
{{$cmpBranch := ""}}
@ -75,9 +78,7 @@
</a>
{{end}}
<!-- Show go to file and breadcrumbs if not on home page -->
{{$n := len .TreeNames}}
{{$l := Eval $n "-" 1}}
{{if eq $n 0}}
{{if $isHomepage}}
<a href="{{.Repository.Link}}/find/{{.BranchNameSubURL}}" class="ui compact basic button">{{ctx.Locale.Tr "repo.find_file.go_to_file"}}</a>
{{end}}
@ -101,20 +102,20 @@
</button>
{{end}}
{{if and (eq $n 0) (.Repository.IsTemplate)}}
{{if and $isHomepage (.Repository.IsTemplate)}}
<a role="button" class="ui primary compact button" href="{{AppSubUrl}}/repo/create?template_id={{.Repository.ID}}">
{{ctx.Locale.Tr "repo.use_template"}}
</a>
{{end}}
{{if ne $n 0}}
{{if (not $isHomepage)}}
<span class="breadcrumb repo-path tw-ml-1">
<a class="section" href="{{.RepoLink}}/src/{{.BranchNameSubURL}}" title="{{.Repository.Name}}">{{StringUtils.EllipsisString .Repository.Name 30}}</a>
{{- range $i, $v := .TreeNames -}}
<span class="breadcrumb-divider">/</span>
{{- if eq $i $l -}}
<span class="active section" title="{{$v}}">{{StringUtils.EllipsisString $v 30}}</span>
<span class="active section" title="{{$v}}">{{$v}}</span>
{{- else -}}
{{$p := index $.Paths $i}}<span class="section"><a href="{{$.BranchLink}}/{{PathEscapeSegments $p}}" title="{{$v}}">{{StringUtils.EllipsisString $v 30}}</a></span>
{{$p := index $.Paths $i}}<span class="section"><a href="{{$.BranchLink}}/{{PathEscapeSegments $p}}" title="{{$v}}">{{$v}}</a></span>
{{- end -}}
{{- end -}}
</span>
@ -122,7 +123,7 @@
</div>
<div class="tw-flex tw-items-center">
<!-- Only show clone panel in repository home page -->
{{if eq $n 0}}
{{if $isHomepage}}
<div class="clone-panel ui action tiny input">
{{template "repo/clone_buttons" .}}
<button class="ui small jump dropdown icon button" data-tooltip-content="{{ctx.Locale.Tr "repo.more_operations"}}">
@ -145,7 +146,7 @@
</div>
{{template "repo/cite/cite_modal" .}}
{{end}}
{{if and (ne $n 0) (not .IsViewFile) (not .IsBlame)}}
{{if and (not $isHomepage) (not .IsViewFile) (not .IsBlame)}}
<a class="ui button" href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}">
{{svg "octicon-history" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.file_history"}}
</a>

View file

@ -156,7 +156,7 @@
<label for="interval">{{ctx.Locale.Tr "repo.mirror_interval" .MinimumMirrorInterval}}</label>
<input id="interval" name="interval" value="{{.PullMirror.Interval}}">
</div>
{{$address := MirrorRemoteAddress $.Context .Repository .PullMirror.GetRemoteName false}}
{{$address := MirrorRemoteAddress $.Context .Repository .PullMirror.GetRemoteName}}
<div class="field {{if .Err_MirrorAddress}}error{{end}}">
<label for="mirror_address">{{ctx.Locale.Tr "repo.mirror_address"}}</label>
<input id="mirror_address" name="mirror_address" value="{{$address.Address}}" required>

View file

@ -11,13 +11,13 @@
{{end}}
{{if not .ReadmeInList}}
<div id="repo-file-commit-box" class="ui top attached header list-header tw-mb-4">
<div>
<div id="repo-file-commit-box" class="ui top attached header list-header tw-mb-4 tw-flex tw-justify-between">
<div class="latest-commit">
{{template "repo/latest_commit" .}}
</div>
{{if .LatestCommit}}
{{if .LatestCommit.Committer}}
<div class="ui text grey right age">
<div class="text grey age">
{{TimeSince .LatestCommit.Committer.When ctx.Locale}}
</div>
{{end}}

View file

@ -1,8 +1,12 @@
<table id="repo-files-table" class="ui single line table tw-mt-0" {{if .HasFilesWithoutLatestCommit}}hx-indicator="tr.notready td.message span" hx-trigger="load" hx-swap="morph" hx-post="{{.LastCommitLoaderURL}}"{{end}}>
<thead>
<tr class="commit-list">
<th colspan="2">
{{template "repo/latest_commit" .}}
<th class="tw-overflow-hidden" colspan="2">
<div class="tw-flex">
<div class="latest-commit">
{{template "repo/latest_commit" .}}
</div>
</div>
</th>
<th class="text grey right age">{{if .LatestCommit}}{{if .LatestCommit.Committer}}{{TimeSince .LatestCommit.Committer.When ctx.Locale}}{{end}}{{end}}</th>
</tr>

View file

@ -1,7 +1,7 @@
<div role="main" aria-label="{{.Title}}" class="page-content user notification" id="notification_div" data-sequence-number="{{.SequenceNumber}}">
<div class="ui container">
{{$notificationUnreadCount := call .NotificationUnreadCount}}
<div class="tw-flex tw-items-center tw-justify-between tw-mb-4">
<div class="tw-flex tw-items-center tw-justify-between tw-mb-[--page-spacing]">
<div class="small-menu-items ui compact tiny menu">
<a class="{{if eq .Status 1}}active {{end}}item" href="{{AppSubUrl}}/notifications?q=unread">
{{ctx.Locale.Tr "notification.unread"}}

View file

@ -13,11 +13,9 @@
<div class="ui four wide column">
{{template "shared/user/profile_big_avatar" .}}
</div>
<div class="ui twelve wide column">
<div class="tw-mb-4">
<div class="ui twelve wide column tw-mb-4">
{{template "user/overview/header" .}}
</div>
{{template "package/shared/versionlist" .}}
{{template "package/shared/versionlist" .}}
</div>
</div>
</div>

View file

@ -13,11 +13,9 @@
<div class="ui four wide column">
{{template "shared/user/profile_big_avatar" .}}
</div>
<div class="ui twelve wide column">
<div class="tw-mb-4">
<div class="ui twelve wide column tw-mb-4">
{{template "user/overview/header" .}}
</div>
{{template "package/shared/list" .}}
{{template "package/shared/list" .}}
</div>
</div>
</div>

View file

@ -6,11 +6,8 @@
<div class="ui four wide column">
{{template "shared/user/profile_big_avatar" .}}
</div>
<div class="ui twelve wide column">
<div class="tw-mb-4">
{{template "user/overview/header" .}}
</div>
<div class="ui twelve wide column tw-mb-4">
{{template "user/overview/header" .}}
{{if eq .TabName "activity"}}
{{if .ContextUser.KeepActivityPrivate}}
<div class="ui info message">

View file

@ -44,7 +44,7 @@
}
.run-list-item-right {
flex: 0 0 15%;
flex: 0 0 min(20%, 130px);
display: flex;
flex-direction: column;
gap: 3px;

View file

@ -24,7 +24,21 @@
--repo-header-issue-min-height: 41px;
--min-height-textarea: 132px; /* padding + 6 lines + border = calc(1.57142em + 6lh + 2px), but lh is not fully supported */
--tab-size: 4;
--checkbox-size: 16px; /* height and width of checkbox and radio inputs */
--checkbox-size: 15px; /* height and width of checkbox and radio inputs */
--page-spacing: 16px; /* space between page elements */
--page-margin-x: 32px; /* minimum space on left and right side of page */
}
@media (min-width: 768px) and (max-width: 1200px) {
:root {
--page-margin-x: 16px;
}
}
@media (max-width: 767.98px) {
:root {
--page-margin-x: 8px;
}
}
:root * {
@ -668,11 +682,14 @@ img.ui.avatar,
margin-bottom: 14px;
}
/* add padding to all content when there is no .secondary.nav. this uses padding instead of
margin because with the negative margin on .ui.grid we would have to set margin-top: 0,
but that does not work universally for all pages */
/* add margin to all pages when there is no .secondary.nav */
.page-content > :first-child:not(.secondary-nav) {
padding-top: 14px;
margin-top: var(--page-spacing);
}
/* if .ui.grid is the first child the first grid-column has 'padding-top: 1rem' which we need
to compensate here */
.page-content > :first-child.ui.grid {
margin-top: calc(var(--page-spacing) - 1rem);
}
.ui.pagination.menu .active.item {
@ -1330,6 +1347,7 @@ overflow-menu .ui.label {
white-space: pre-wrap;
word-break: break-all;
overflow-wrap: anywhere;
line-height: inherit; /* needed for inline code preview in markup */
}
.blame .code-inner {

View file

@ -249,21 +249,6 @@ textarea:focus,
.user.signup form .optional .title {
margin-left: 250px !important;
}
.user.activate form .inline.field > input,
.user.forgot.password form .inline.field > input,
.user.reset.password form .inline.field > input,
.user.link-account form .inline.field > input,
.user.signin form .inline.field > input,
.user.signup form .inline.field > input,
.user.activate form .inline.field > textarea,
.user.forgot.password form .inline.field > textarea,
.user.reset.password form .inline.field > textarea,
.user.link-account form .inline.field > textarea,
.user.signin form .inline.field > textarea,
.user.signup form .inline.field > textarea,
.oauth-login-link {
width: 50%;
}
}
@media (max-width: 767.98px) {
@ -310,14 +295,7 @@ textarea:focus,
.user.reset.password form .inline.field > label,
.user.link-account form .inline.field > label,
.user.signin form .inline.field > label,
.user.signup form .inline.field > label,
.user.activate form input,
.user.forgot.password form input,
.user.reset.password form input,
.user.link-account form input,
.user.signin form input,
.user.signup form input,
.oauth-login-link {
.user.signup form .inline.field > label {
width: 100% !important;
}
}
@ -435,9 +413,9 @@ textarea:focus,
.repository.new.repo form label,
.repository.new.migrate form label,
.repository.new.fork form label,
.repository.new.repo form input,
.repository.new.migrate form input,
.repository.new.fork form input,
.repository.new.repo form .inline.field > input,
.repository.new.migrate form .inline.field > input,
.repository.new.fork form .inline.field > input,
.repository.new.fork form .field a,
.repository.new.repo form .selection.dropdown,
.repository.new.migrate form .selection.dropdown,

View file

@ -20,7 +20,7 @@ input[type="radio"] {
.ui.checkbox input[type="checkbox"],
.ui.checkbox input[type="radio"] {
position: absolute;
top: 0;
top: 1px;
left: 0;
width: var(--checkbox-size);
height: var(--checkbox-size);

View file

@ -49,30 +49,11 @@
/* overwrite width of containers inside the main page content div (div with class "page-content") */
.page-content .ui.ui.ui.container:not(.fluid) {
width: 1280px;
max-width: calc(100% - 64px);
max-width: calc(100% - calc(2 * var(--page-margin-x)));
margin-left: auto;
margin-right: auto;
}
.ui.container.fluid.padded {
padding: 0 32px;
}
/* enable fluid page widths for medium size viewports */
@media (min-width: 768px) and (max-width: 1200px) {
.page-content .ui.ui.ui.container:not(.fluid) {
max-width: calc(100% - 32px);
}
.ui.container.fluid.padded {
padding: 0 16px;
}
}
@media (max-width: 767.98px) {
.page-content .ui.ui.ui.container:not(.fluid) {
max-width: calc(100% - 16px);
}
.ui.container.fluid.padded {
padding: 0 8px;
}
padding: 0 var(--page-margin-x);
}

View file

@ -2,7 +2,8 @@
.flex-container {
display: flex !important;
gap: 16px;
gap: var(--page-spacing);
margin-top: var(--page-spacing);
}
.flex-container-nav {

View file

@ -177,12 +177,44 @@
}
}
.repository.file.list .repo-path {
word-break: break-word;
.commit-summary {
flex: 1;
overflow-wrap: anywhere;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.repository.file.list #repo-files-table {
table-layout: fixed;
.commit-header .commit-summary,
td .commit-summary {
white-space: normal;
}
.latest-commit {
display: flex;
flex: 1;
align-items: center;
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 767.98px) {
.latest-commit .sha {
display: none;
}
.latest-commit .commit-summary {
margin-left: 8px;
}
}
.repo-path {
display: flex;
overflow-wrap: anywhere;
}
/* this is what limits the commit table width to a value that works on all viewport sizes */
#repo-files-table th:first-of-type {
max-width: calc(calc(min(100vw, 1280px)) - 145px - calc(2 * var(--page-margin-x)));
}
.repository.file.list #repo-files-table thead th {
@ -262,7 +294,6 @@
}
.repository.file.list #repo-files-table td.age {
width: 120px;
color: var(--color-text-light-1);
}
@ -1223,10 +1254,6 @@
margin: 0;
}
.repository #commits-table td.message {
text-overflow: unset;
}
.repository #commits-table.ui.basic.striped.table tbody tr:nth-child(2n) {
background-color: var(--color-light) !important;
}
@ -2153,6 +2180,20 @@
display: inline-block !important;
}
.commit-header-buttons {
display: flex;
gap: 4px;
align-items: flex-start;
white-space: nowrap;
}
@media (max-width: 767.98px) {
.commit-header-buttons {
flex-direction: column;
align-items: stretch;
}
}
.settings.webhooks .list > .item:not(:first-child),
.settings.githooks .list > .item:not(:first-child),
.settings.actions .list > .item:not(:first-child) {
@ -2469,7 +2510,7 @@ tbody.commit-list {
.author-wrapper {
overflow: hidden;
text-overflow: ellipsis;
max-width: calc(100% - 50px);
max-width: 100%;
display: inline-block;
vertical-align: middle;
}
@ -2494,10 +2535,6 @@ tbody.commit-list {
tr.commit-list {
width: 100%;
}
th .message-wrapper {
display: block;
max-width: calc(100vw - 70px);
}
.author-wrapper {
max-width: 80px;
}
@ -2507,27 +2544,18 @@ tbody.commit-list {
tr.commit-list {
width: 723px;
}
th .message-wrapper {
max-width: 120px;
}
}
@media (min-width: 992px) and (max-width: 1200px) {
tr.commit-list {
width: 933px;
}
th .message-wrapper {
max-width: 350px;
}
}
@media (min-width: 1201px) {
tr.commit-list {
width: 1127px;
}
th .message-wrapper {
max-width: 525px;
}
}
.commit-list .commit-status-link {
@ -2854,7 +2882,7 @@ tbody.commit-list {
.repository.file.list #repo-files-table .entry td.message,
.repository.file.list #repo-files-table .commit-list td.message,
.repository.file.list #repo-files-table .entry span.commit-summary,
.repository.file.list #repo-files-table .commit-list span.commit-summary {
.repository.file.list #repo-files-table .commit-list tr span.commit-summary {
display: none !important;
}
.repository.view.issue .comment-list .timeline,

View file

@ -65,7 +65,7 @@
--color-console-fg-subtle: #bec4c8;
--color-console-bg: #171b1e;
--color-console-border: #2e353b;
--color-console-hover-bg: #e8e8ff16;
--color-console-hover-bg: #292d31;
--color-console-active-bg: #2e353b;
--color-console-menu-bg: #252b30;
--color-console-menu-border: #424b51;

View file

@ -63,12 +63,12 @@
/* console colors - used for actions console and console files */
--color-console-fg: #f8f8f9;
--color-console-fg-subtle: #bec4c8;
--color-console-bg: #181b1d;
--color-console-border: #313538;
--color-console-hover-bg: #ffffff16;
--color-console-active-bg: #313538;
--color-console-menu-bg: #272b2e;
--color-console-menu-border: #464a4d;
--color-console-bg: #171b1e;
--color-console-border: #2e353b;
--color-console-hover-bg: #292d31;
--color-console-active-bg: #2e353b;
--color-console-menu-bg: #252b30;
--color-console-menu-border: #424b51;
/* named colors */
--color-red: #db2828;
--color-orange: #f2711c;

View file

@ -526,8 +526,16 @@ export function initRepositoryActionView() {
.action-summary {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin: 0 0 0 28px;
margin-left: 28px;
}
@media (max-width: 767.98px) {
.action-commit-summary {
margin-left: 0;
margin-top: 8px;
}
}
/* ================ */
@ -540,6 +548,14 @@ export function initRepositoryActionView() {
top: 12px;
max-height: 100vh;
overflow-y: auto;
background: var(--color-body);
z-index: 2; /* above .job-info-header */
}
@media (max-width: 767.98px) {
.action-view-left {
position: static; /* can not sticky because multiple jobs would overlap into right view */
}
}
.job-artifacts-title {
@ -701,7 +717,9 @@ export function initRepositoryActionView() {
position: sticky;
top: 0;
height: 60px;
z-index: 1;
z-index: 1; /* above .job-step-container */
background: var(--color-console-bg);
border-radius: 3px;
}
.job-info-header:has(+ .job-step-container) {
@ -739,7 +757,7 @@ export function initRepositoryActionView() {
.job-step-container .job-step-summary.step-expandable:hover {
color: var(--color-console-fg);
background-color: var(--color-console-hover-bg);
background: var(--color-console-hover-bg);
}
.job-step-container .job-step-summary .step-summary-msg {
@ -757,17 +775,15 @@ export function initRepositoryActionView() {
top: 60px;
}
@media (max-width: 768px) {
@media (max-width: 767.98px) {
.action-view-body {
flex-direction: column;
}
.action-view-left, .action-view-right {
width: 100%;
}
.action-view-left {
max-width: none;
overflow-y: hidden;
}
}
</style>

View file

@ -207,13 +207,13 @@ export function initAdminCommon() {
// Notice
if (document.querySelector('.admin.notice')) {
const $detailModal = document.getElementById('detail-modal');
const detailModal = document.getElementById('detail-modal');
// Attach view detail modals
$('.view-detail').on('click', function () {
$detailModal.find('.content pre').text($(this).parents('tr').find('.notice-description').text());
$detailModal.find('.sub.header').text(this.closest('tr')?.querySelector('relative-time')?.getAttribute('title'));
$detailModal.modal('show');
const description = this.closest('tr').querySelector('.notice-description').textContent;
detailModal.querySelector('.content pre').textContent = description;
$(detailModal).modal('show');
return false;
});

View file

@ -435,12 +435,10 @@ export function initRepoPullRequestReview() {
offset += $('.diff-detail-box').outerHeight() + $(diffHeader).outerHeight();
}
document.getElementById(`show-outdated-${id}`).classList.add('tw-hidden');
document.getElementById(`code-comments-${id}`).classList.remove('tw-hidden');
document.getElementById(`code-preview-${id}`).classList.remove('tw-hidden');
document.getElementById(`hide-outdated-${id}`).classList.remove('tw-hidden');
hideElem(`#show-outdated-${id}`);
showElem(`#code-comments-${id}, #code-preview-${id}, #hide-outdated-${id}`);
// if the comment box is folded, expand it
if (ancestorDiffBox.getAttribute('data-folded') === 'true') {
if (ancestorDiffBox?.getAttribute('data-folded') === 'true') {
setFileFolding(ancestorDiffBox, ancestorDiffBox.querySelector('.fold-file'), false);
}