Merge branch 'main' into add-issue-planned-time

This commit is contained in:
スツゼル 2023-03-04 23:04:29 +02:00 committed by GitHub
commit 879d96f077
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
146 changed files with 2429 additions and 869 deletions

View File

@ -1,14 +1,50 @@
#!/bin/sh
# this script runs in alpine image which only has `sh` shell
set +e
if sed --version 2>/dev/null | grep -q GNU; then
SED_INPLACE="sed -i"
else
SED_INPLACE="sed -i ''"
fi
set -e
if [ ! -f ./options/locale/locale_en-US.ini ]; then
echo "please run this script in the root directory of the project"
exit 1
fi
mv ./options/locale/locale_en-US.ini ./options/
# Make sure to only change lines that have the translation enclosed between quotes
sed -i -r -e '/^[a-zA-Z0-9_.-]+[ ]*=[ ]*".*"$/ {
s/^([a-zA-Z0-9_.-]+)[ ]*="/\1=/
s/\\"/"/g
# the "ini" library for locale has many quirks
# * `a="xx"` gets `xx` (no quote)
# * `a=x\"y` gets `x\"y` (no unescaping)
# * `a="x\"y"` gets `"x\"y"` (no unescaping, the quotes are still there)
# * `a='x\"y'` gets `x\"y` (no unescaping, no quote)
# * `a="foo` gets `"foo` (although the quote is not closed)
# * 'a=`foo`' works like single-quote
# crowdin needs the strings to be quoted correctly and doesn't like incomplete quotes
# crowdin always outputs quoted strings if there are quotes in the strings.
# this script helps to unquote the crowdin outputs for the quirky ini library
# * find all `key="...\"..."` lines
# * remove the leading quote
# * remove the trailing quote
# * unescape the quotes
# * eg: key="...\"..." => key=..."...
$SED_INPLACE -r -e '/^[-.A-Za-z0-9_]+[ ]*=[ ]*".*"$/ {
s/^([-.A-Za-z0-9_]+)[ ]*=[ ]*"/\1=/
s/"$//
s/\\"/"/g
}' ./options/locale/*.ini
# * if the escaped line is incomplete like `key="...` or `key=..."`, quote it with backticks
# * eg: key="... => key=`"...`
# * eg: key=..." => key=`..."`
$SED_INPLACE -r -e 's/^([-.A-Za-z0-9_]+)[ ]*=[ ]*(".*[^"])$/\1=`\2`/' ./options/locale/*.ini
$SED_INPLACE -r -e 's/^([-.A-Za-z0-9_]+)[ ]*=[ ]*([^"].*")$/\1=`\2`/' ./options/locale/*.ini
# Remove translation under 25% of en_us
baselines=$(wc -l "./options/locale_en-US.ini" | cut -d" " -f1)
baselines=$((baselines / 4))

View File

@ -272,6 +272,7 @@ func runDump(ctx *cli.Context) error {
fatal("Failed to create tmp file: %v", err)
}
defer func() {
_ = dbDump.Close()
if err := util.Remove(dbDump.Name()); err != nil {
log.Warn("Unable to remove temporary file: %s: Error: %v", dbDump.Name(), err)
}

View File

@ -11,6 +11,7 @@ import (
"net/url"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
@ -290,17 +291,21 @@ func runServ(c *cli.Context) error {
return nil
}
// Special handle for Windows.
if setting.IsWindows {
verb = strings.Replace(verb, "-", " ", 1)
}
var gitcmd *exec.Cmd
verbs := strings.Split(verb, " ")
if len(verbs) == 2 {
gitcmd = exec.CommandContext(ctx, verbs[0], verbs[1], repoPath)
} else {
gitcmd = exec.CommandContext(ctx, verb, repoPath)
gitBinPath := filepath.Dir(git.GitExecutable) // e.g. /usr/bin
gitBinVerb := filepath.Join(gitBinPath, verb) // e.g. /usr/bin/git-upload-pack
if _, err := os.Stat(gitBinVerb); err != nil {
// if the command "git-upload-pack" doesn't exist, try to split "git-upload-pack" to use the sub-command with git
// ps: Windows only has "git.exe" in the bin path, so Windows always uses this way
verbFields := strings.SplitN(verb, "-", 2)
if len(verbFields) == 2 {
// use git binary with the sub-command part: "C:\...\bin\git.exe", "upload-pack", ...
gitcmd = exec.CommandContext(ctx, git.GitExecutable, verbFields[1], repoPath)
}
}
if gitcmd == nil {
// by default, use the verb (it has been checked above by allowedCommands)
gitcmd = exec.CommandContext(ctx, gitBinVerb, repoPath)
}
process.SetSysProcAttribute(gitcmd)

View File

@ -83,6 +83,9 @@ It's not recommended to use `async` event listeners, which may lead to problems.
The reason is that the code after await is executed outside the event dispatch.
Reference: https://github.com/github/eslint-plugin-github/blob/main/docs/rules/async-preventdefault.md
If an event listener must be `async`, the `e.preventDefault()` should be before any `await`,
it's recommended to put it at the beginning of the function.
If we want to call an `async` function in a non-async context,
it's recommended to use `const _promise = asyncFoo()` to tell readers
that this is done by purpose, we want to call the async function and ignore the Promise.

View File

@ -25,7 +25,7 @@
fork_id: 0
is_template: false
template_id: 0
size: 6708
size: 7028
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false

View File

@ -151,3 +151,25 @@
num_members: 1
includes_all_repositories: false
can_create_org_repo: true
-
id: 15
org_id: 22
lower_name: owners
name: Owners
authorize: 4 # owner
num_repos: 0
num_members: 0
includes_all_repositories: false
can_create_org_repo: true
-
id: 16
org_id: 23
lower_name: owners
name: Owners
authorize: 4 # owner
num_repos: 0
num_members: 0
includes_all_repositories: false
can_create_org_repo: true

View File

@ -807,7 +807,7 @@
num_following: 0
num_stars: 0
num_repos: 2
num_teams: 0
num_teams: 1
num_members: 0
visibility: 1
repo_admin_change_team_access: false
@ -844,7 +844,7 @@
num_following: 0
num_stars: 0
num_repos: 2
num_teams: 0
num_teams: 1
num_members: 0
visibility: 2
repo_admin_change_team_access: false

View File

@ -7,12 +7,12 @@ package issues
import (
"context"
"fmt"
"regexp"
"strconv"
"strings"
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/label"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
@ -78,9 +78,6 @@ func (err ErrLabelNotExist) Unwrap() error {
return util.ErrNotExist
}
// LabelColorPattern is a regexp witch can validate LabelColor
var LabelColorPattern = regexp.MustCompile("^#?(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{3})$")
// Label represents a label of repository for issues.
type Label struct {
ID int64 `xorm:"pk autoincr"`
@ -109,12 +106,12 @@ func init() {
}
// CalOpenIssues sets the number of open issues of a label based on the already stored number of closed issues.
func (label *Label) CalOpenIssues() {
label.NumOpenIssues = label.NumIssues - label.NumClosedIssues
func (l *Label) CalOpenIssues() {
l.NumOpenIssues = l.NumIssues - l.NumClosedIssues
}
// CalOpenOrgIssues calculates the open issues of a label for a specific repo
func (label *Label) CalOpenOrgIssues(ctx context.Context, repoID, labelID int64) {
func (l *Label) CalOpenOrgIssues(ctx context.Context, repoID, labelID int64) {
counts, _ := CountIssuesByRepo(ctx, &IssuesOptions{
RepoID: repoID,
LabelIDs: []int64{labelID},
@ -122,22 +119,22 @@ func (label *Label) CalOpenOrgIssues(ctx context.Context, repoID, labelID int64)
})
for _, count := range counts {
label.NumOpenRepoIssues += count
l.NumOpenRepoIssues += count
}
}
// LoadSelectedLabelsAfterClick calculates the set of selected labels when a label is clicked
func (label *Label) LoadSelectedLabelsAfterClick(currentSelectedLabels []int64, currentSelectedExclusiveScopes []string) {
func (l *Label) LoadSelectedLabelsAfterClick(currentSelectedLabels []int64, currentSelectedExclusiveScopes []string) {
var labelQuerySlice []string
labelSelected := false
labelID := strconv.FormatInt(label.ID, 10)
labelScope := label.ExclusiveScope()
labelID := strconv.FormatInt(l.ID, 10)
labelScope := l.ExclusiveScope()
for i, s := range currentSelectedLabels {
if s == label.ID {
if s == l.ID {
labelSelected = true
} else if -s == label.ID {
} else if -s == l.ID {
labelSelected = true
label.IsExcluded = true
l.IsExcluded = true
} else if s != 0 {
// Exclude other labels in the same scope from selection
if s < 0 || labelScope == "" || labelScope != currentSelectedExclusiveScopes[i] {
@ -148,23 +145,23 @@ func (label *Label) LoadSelectedLabelsAfterClick(currentSelectedLabels []int64,
if !labelSelected {
labelQuerySlice = append(labelQuerySlice, labelID)
}
label.IsSelected = labelSelected
label.QueryString = strings.Join(labelQuerySlice, ",")
l.IsSelected = labelSelected
l.QueryString = strings.Join(labelQuerySlice, ",")
}
// BelongsToOrg returns true if label is an organization label
func (label *Label) BelongsToOrg() bool {
return label.OrgID > 0
func (l *Label) BelongsToOrg() bool {
return l.OrgID > 0
}
// BelongsToRepo returns true if label is a repository label
func (label *Label) BelongsToRepo() bool {
return label.RepoID > 0
func (l *Label) BelongsToRepo() bool {
return l.RepoID > 0
}
// Get color as RGB values in 0..255 range
func (label *Label) ColorRGB() (float64, float64, float64, error) {
color, err := strconv.ParseUint(label.Color[1:], 16, 64)
func (l *Label) ColorRGB() (float64, float64, float64, error) {
color, err := strconv.ParseUint(l.Color[1:], 16, 64)
if err != nil {
return 0, 0, 0, err
}
@ -176,9 +173,9 @@ func (label *Label) ColorRGB() (float64, float64, float64, error) {
}
// Determine if label text should be light or dark to be readable on background color
func (label *Label) UseLightTextColor() bool {
if strings.HasPrefix(label.Color, "#") {
if r, g, b, err := label.ColorRGB(); err == nil {
func (l *Label) UseLightTextColor() bool {
if strings.HasPrefix(l.Color, "#") {
if r, g, b, err := l.ColorRGB(); err == nil {
// Perceived brightness from: https://www.w3.org/TR/AERT/#color-contrast
// In the future WCAG 3 APCA may be a better solution
brightness := (0.299*r + 0.587*g + 0.114*b) / 255
@ -190,40 +187,26 @@ func (label *Label) UseLightTextColor() bool {
}
// Return scope substring of label name, or empty string if none exists
func (label *Label) ExclusiveScope() string {
if !label.Exclusive {
func (l *Label) ExclusiveScope() string {
if !l.Exclusive {
return ""
}
lastIndex := strings.LastIndex(label.Name, "/")
if lastIndex == -1 || lastIndex == 0 || lastIndex == len(label.Name)-1 {
lastIndex := strings.LastIndex(l.Name, "/")
if lastIndex == -1 || lastIndex == 0 || lastIndex == len(l.Name)-1 {
return ""
}
return label.Name[:lastIndex]
return l.Name[:lastIndex]
}
// NewLabel creates a new label
func NewLabel(ctx context.Context, label *Label) error {
if !LabelColorPattern.MatchString(label.Color) {
return fmt.Errorf("bad color code: %s", label.Color)
func NewLabel(ctx context.Context, l *Label) error {
color, err := label.NormalizeColor(l.Color)
if err != nil {
return err
}
l.Color = color
// normalize case
label.Color = strings.ToLower(label.Color)
// add leading hash
if label.Color[0] != '#' {
label.Color = "#" + label.Color
}
// convert 3-character shorthand into 6-character version
if len(label.Color) == 4 {
r := label.Color[1]
g := label.Color[2]
b := label.Color[3]
label.Color = fmt.Sprintf("#%c%c%c%c%c%c", r, r, g, g, b, b)
}
return db.Insert(ctx, label)
return db.Insert(ctx, l)
}
// NewLabels creates new labels
@ -234,11 +217,14 @@ func NewLabels(labels ...*Label) error {
}
defer committer.Close()
for _, label := range labels {
if !LabelColorPattern.MatchString(label.Color) {
return fmt.Errorf("bad color code: %s", label.Color)
for _, l := range labels {
color, err := label.NormalizeColor(l.Color)
if err != nil {
return err
}
if err := db.Insert(ctx, label); err != nil {
l.Color = color
if err := db.Insert(ctx, l); err != nil {
return err
}
}
@ -247,15 +233,18 @@ func NewLabels(labels ...*Label) error {
// UpdateLabel updates label information.
func UpdateLabel(l *Label) error {
if !LabelColorPattern.MatchString(l.Color) {
return fmt.Errorf("bad color code: %s", l.Color)
color, err := label.NormalizeColor(l.Color)
if err != nil {
return err
}
l.Color = color
return updateLabelCols(db.DefaultContext, l, "name", "description", "color", "exclusive")
}
// DeleteLabel delete a label
func DeleteLabel(id, labelID int64) error {
label, err := GetLabelByID(db.DefaultContext, labelID)
l, err := GetLabelByID(db.DefaultContext, labelID)
if err != nil {
if IsErrLabelNotExist(err) {
return nil
@ -271,10 +260,10 @@ func DeleteLabel(id, labelID int64) error {
sess := db.GetEngine(ctx)
if label.BelongsToOrg() && label.OrgID != id {
if l.BelongsToOrg() && l.OrgID != id {
return nil
}
if label.BelongsToRepo() && label.RepoID != id {
if l.BelongsToRepo() && l.RepoID != id {
return nil
}
@ -682,14 +671,14 @@ func newIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *us
if err = issue.LoadRepo(ctx); err != nil {
return err
}
for _, label := range labels {
for _, l := range labels {
// Don't add already present labels and invalid labels
if HasIssueLabel(ctx, issue.ID, label.ID) ||
(label.RepoID != issue.RepoID && label.OrgID != issue.Repo.OwnerID) {
if HasIssueLabel(ctx, issue.ID, l.ID) ||
(l.RepoID != issue.RepoID && l.OrgID != issue.Repo.OwnerID) {
continue
}
if err = newIssueLabel(ctx, issue, label, doer); err != nil {
if err = newIssueLabel(ctx, issue, l, doer); err != nil {
return fmt.Errorf("newIssueLabel: %w", err)
}
}

View File

@ -15,8 +15,6 @@ import (
"github.com/stretchr/testify/assert"
)
// TODO TestGetLabelTemplateFile
func TestLabel_CalOpenIssues(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})

View File

@ -52,13 +52,16 @@ func listPullRequestStatement(baseRepoID int64, opts *PullRequestsOptions) (*xor
// GetUnmergedPullRequestsByHeadInfo returns all pull requests that are open and has not been merged
// by given head information (repo and branch).
func GetUnmergedPullRequestsByHeadInfo(repoID int64, branch string) ([]*PullRequest, error) {
// arg `includeClosed` controls whether the SQL returns closed PRs
func GetUnmergedPullRequestsByHeadInfo(repoID int64, branch string, includeClosed bool) ([]*PullRequest, error) {
prs := make([]*PullRequest, 0, 2)
return prs, db.GetEngine(db.DefaultContext).
Where("head_repo_id = ? AND head_branch = ? AND has_merged = ? AND issue.is_closed = ? AND flow = ?",
repoID, branch, false, false, PullRequestFlowGithub).
sess := db.GetEngine(db.DefaultContext).
Join("INNER", "issue", "issue.id = pull_request.issue_id").
Find(&prs)
Where("head_repo_id = ? AND head_branch = ? AND has_merged = ? AND flow = ?", repoID, branch, false, PullRequestFlowGithub)
if !includeClosed {
sess.Where("issue.is_closed = ?", false)
}
return prs, sess.Find(&prs)
}
// CanMaintainerWriteToBranch check whether user is a maintainer and could write to the branch
@ -71,7 +74,7 @@ func CanMaintainerWriteToBranch(p access_model.Permission, branch string, user *
return false
}
prs, err := GetUnmergedPullRequestsByHeadInfo(p.Units[0].RepoID, branch)
prs, err := GetUnmergedPullRequestsByHeadInfo(p.Units[0].RepoID, branch, false)
if err != nil {
return false
}
@ -111,6 +114,7 @@ func GetUnmergedPullRequestsByBaseInfo(repoID int64, branch string) ([]*PullRequ
return prs, db.GetEngine(db.DefaultContext).
Where("base_repo_id=? AND base_branch=? AND has_merged=? AND issue.is_closed=?",
repoID, branch, false, false).
OrderBy("issue.updated_unix DESC").
Join("INNER", "issue", "issue.id=pull_request.issue_id").
Find(&prs)
}

View File

@ -118,7 +118,7 @@ func TestHasUnmergedPullRequestsByHeadInfo(t *testing.T) {
func TestGetUnmergedPullRequestsByHeadInfo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(1, "branch2")
prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(1, "branch2", false)
assert.NoError(t, err)
assert.Len(t, prs, 1)
for _, pr := range prs {

View File

@ -41,9 +41,8 @@ var RecommendedHashAlgorithms = []string{
"pbkdf2_hi",
}
// SetDefaultPasswordHashAlgorithm will take a provided algorithmName and dealias it to
// a complete algorithm specification.
func SetDefaultPasswordHashAlgorithm(algorithmName string) (string, *PasswordHashAlgorithm) {
// hashAlgorithmToSpec converts an algorithm name or a specification to a full algorithm specification
func hashAlgorithmToSpec(algorithmName string) string {
if algorithmName == "" {
algorithmName = DefaultHashAlgorithmName
}
@ -52,10 +51,26 @@ func SetDefaultPasswordHashAlgorithm(algorithmName string) (string, *PasswordHas
algorithmName = alias
alias, has = aliasAlgorithmNames[algorithmName]
}
// algorithmName should now be a full algorithm specification
// e.g. pbkdf2$50000$50 rather than pbdkf2
DefaultHashAlgorithm = Parse(algorithmName)
return algorithmName, DefaultHashAlgorithm
return algorithmName
}
// SetDefaultPasswordHashAlgorithm will take a provided algorithmName and de-alias it to
// a complete algorithm specification.
func SetDefaultPasswordHashAlgorithm(algorithmName string) (string, *PasswordHashAlgorithm) {
algoSpec := hashAlgorithmToSpec(algorithmName)
// now we get a full specification, e.g. pbkdf2$50000$50 rather than pbdkf2
DefaultHashAlgorithm = Parse(algoSpec)
return algoSpec, DefaultHashAlgorithm
}
// ConfigHashAlgorithm will try to find a "recommended algorithm name" defined by RecommendedHashAlgorithms for config
// This function is not fast and is only used for the installation page
func ConfigHashAlgorithm(algorithm string) string {
algorithm = hashAlgorithmToSpec(algorithm)
for _, recommAlgo := range RecommendedHashAlgorithms {
if algorithm == hashAlgorithmToSpec(recommAlgo) {
return recommAlgo
}
}
return algorithm
}

View File

@ -312,7 +312,7 @@ func CheckGitVersionAtLeast(atLeast string) error {
}
func configSet(key, value string) error {
stdout, _, err := NewCommand(DefaultContext, "config", "--get").AddDynamicArguments(key).RunStdString(nil)
stdout, _, err := NewCommand(DefaultContext, "config", "--global", "--get").AddDynamicArguments(key).RunStdString(nil)
if err != nil && !err.IsExitCode(1) {
return fmt.Errorf("failed to get git config %s, err: %w", key, err)
}
@ -331,7 +331,7 @@ func configSet(key, value string) error {
}
func configSetNonExist(key, value string) error {
_, _, err := NewCommand(DefaultContext, "config", "--get").AddDynamicArguments(key).RunStdString(nil)
_, _, err := NewCommand(DefaultContext, "config", "--global", "--get").AddDynamicArguments(key).RunStdString(nil)
if err == nil {
// already exist
return nil
@ -349,7 +349,7 @@ func configSetNonExist(key, value string) error {
}
func configAddNonExist(key, value string) error {
_, _, err := NewCommand(DefaultContext, "config", "--get").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(nil)
_, _, err := NewCommand(DefaultContext, "config", "--global", "--get").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(nil)
if err == nil {
// already exist
return nil
@ -366,7 +366,7 @@ func configAddNonExist(key, value string) error {
}
func configUnsetAll(key, value string) error {
_, _, err := NewCommand(DefaultContext, "config", "--get").AddDynamicArguments(key).RunStdString(nil)
_, _, err := NewCommand(DefaultContext, "config", "--global", "--get").AddDynamicArguments(key).RunStdString(nil)
if err == nil {
// exist, need to remove
_, _, err = NewCommand(DefaultContext, "config", "--global", "--unset-all").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(nil)

View File

@ -7,7 +7,6 @@
package git
import (
"fmt"
"strings"
"github.com/go-git/go-git/v5/plumbing"
@ -67,38 +66,6 @@ func (repo *Repository) IsCommitExist(name string) bool {
return err == nil
}
func convertPGPSignatureForTag(t *object.Tag) *CommitGPGSignature {
if t.PGPSignature == "" {
return nil
}
var w strings.Builder
var err error
if _, err = fmt.Fprintf(&w,
"object %s\ntype %s\ntag %s\ntagger ",
t.Target.String(), t.TargetType.Bytes(), t.Name); err != nil {
return nil
}
if err = t.Tagger.Encode(&w); err != nil {
return nil
}
if _, err = fmt.Fprintf(&w, "\n\n"); err != nil {
return nil
}
if _, err = fmt.Fprintf(&w, t.Message); err != nil {
return nil
}
return &CommitGPGSignature{
Signature: t.PGPSignature,
Payload: strings.TrimSpace(w.String()) + "\n",
}
}
func (repo *Repository) getCommit(id SHA1) (*Commit, error) {
var tagObject *object.Tag
@ -122,12 +89,6 @@ func (repo *Repository) getCommit(id SHA1) (*Commit, error) {
commit := convertCommit(gogitCommit)
commit.repo = repo
if tagObject != nil {
commit.CommitMessage = strings.TrimSpace(tagObject.Message)
commit.Author = &tagObject.Tagger
commit.Signature = convertPGPSignatureForTag(tagObject)
}
tree, err := gogitCommit.Tree()
if err != nil {
return nil, err

View File

@ -107,10 +107,6 @@ func (repo *Repository) getCommitFromBatchReader(rd *bufio.Reader, id SHA1) (*Co
return nil, err
}
commit.CommitMessage = strings.TrimSpace(tag.Message)
commit.Author = tag.Tagger
commit.Signature = tag.Signature
return commit, nil
case "commit":
commit, err := CommitFromReader(repo, id, io.LimitReader(rd, size))

View File

@ -43,12 +43,13 @@ func TestGetTagCommitWithSignature(t *testing.T) {
assert.NoError(t, err)
defer bareRepo1.Close()
commit, err := bareRepo1.GetCommit("3ad28a9149a2864384548f3d17ed7f38014c9e8a")
// both the tag and the commit are signed here, this validates only the commit signature
commit, err := bareRepo1.GetCommit("28b55526e7100924d864dd89e35c1ea62e7a5a32")
assert.NoError(t, err)
assert.NotNil(t, commit)
assert.NotNil(t, commit.Signature)
// test that signature is not in message
assert.Equal(t, "tag", commit.CommitMessage)
assert.Equal(t, "signed-commit\n", commit.CommitMessage)
}
func TestGetCommitWithBadCommitID(t *testing.T) {

View File

@ -277,11 +277,18 @@ func (repo *Repository) GetPatch(base, head string, w io.Writer) error {
// GetFilesChangedBetween returns a list of all files that have been changed between the given commits
func (repo *Repository) GetFilesChangedBetween(base, head string) ([]string, error) {
stdout, _, err := NewCommand(repo.Ctx, "diff", "--name-only").AddDynamicArguments(base + ".." + head).RunStdString(&RunOpts{Dir: repo.Path})
stdout, _, err := NewCommand(repo.Ctx, "diff", "--name-only", "-z").AddDynamicArguments(base + ".." + head).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
return strings.Split(stdout, "\n"), err
split := strings.Split(stdout, "\000")
// Because Git will always emit filenames with a terminal NUL ignore the last entry in the split - which will always be empty.
if len(split) > 0 {
split = split[:len(split)-1]
}
return split, err
}
// GetDiffFromMergeBase generates and return patch data from merge base to head

View File

@ -19,13 +19,14 @@ func TestRepository_GetRefs(t *testing.T) {
refs, err := bareRepo1.GetRefs()
assert.NoError(t, err)
assert.Len(t, refs, 5)
assert.Len(t, refs, 6)
expectedRefs := []string{
BranchPrefix + "branch1",
BranchPrefix + "branch2",
BranchPrefix + "master",
TagPrefix + "test",
TagPrefix + "signed-tag",
NotesRef,
}
@ -43,9 +44,12 @@ func TestRepository_GetRefsFiltered(t *testing.T) {
refs, err := bareRepo1.GetRefsFiltered(TagPrefix)
assert.NoError(t, err)
if assert.Len(t, refs, 1) {
assert.Equal(t, TagPrefix+"test", refs[0].Name)
if assert.Len(t, refs, 2) {
assert.Equal(t, TagPrefix+"signed-tag", refs[0].Name)
assert.Equal(t, "tag", refs[0].Type)
assert.Equal(t, "3ad28a9149a2864384548f3d17ed7f38014c9e8a", refs[0].Object.String())
assert.Equal(t, "36f97d9a96457e2bab511db30fe2db03893ebc64", refs[0].Object.String())
assert.Equal(t, TagPrefix+"test", refs[1].Name)
assert.Equal(t, "tag", refs[1].Type)
assert.Equal(t, "3ad28a9149a2864384548f3d17ed7f38014c9e8a", refs[1].Object.String())
}
}

View File

@ -24,9 +24,9 @@ func TestRepository_GetCodeActivityStats(t *testing.T) {
assert.NoError(t, err)
assert.NotNil(t, code)
assert.EqualValues(t, 9, code.CommitCount)
assert.EqualValues(t, 10, code.CommitCount)
assert.EqualValues(t, 3, code.AuthorCount)
assert.EqualValues(t, 9, code.CommitCountInAllBranches)
assert.EqualValues(t, 10, code.CommitCountInAllBranches)
assert.EqualValues(t, 10, code.Additions)
assert.EqualValues(t, 1, code.Deletions)
assert.Len(t, code.Authors, 3)

View File

@ -25,11 +25,14 @@ func TestRepository_GetTags(t *testing.T) {
assert.NoError(t, err)
return
}
assert.Len(t, tags, 1)
assert.Len(t, tags, 2)
assert.Equal(t, len(tags), total)
assert.EqualValues(t, "test", tags[0].Name)
assert.EqualValues(t, "3ad28a9149a2864384548f3d17ed7f38014c9e8a", tags[0].ID.String())
assert.EqualValues(t, "signed-tag", tags[0].Name)
assert.EqualValues(t, "36f97d9a96457e2bab511db30fe2db03893ebc64", tags[0].ID.String())
assert.EqualValues(t, "tag", tags[0].Type)
assert.EqualValues(t, "test", tags[1].Name)
assert.EqualValues(t, "3ad28a9149a2864384548f3d17ed7f38014c9e8a", tags[1].ID.String())
assert.EqualValues(t, "tag", tags[1].Type)
}
func TestRepository_GetTag(t *testing.T) {

View File

@ -14,10 +14,10 @@ func TestGetLatestCommitTime(t *testing.T) {
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
lct, err := GetLatestCommitTime(DefaultContext, bareRepo1Path)
assert.NoError(t, err)
// Time is Sun Jul 21 22:43:13 2019 +0200
// Time is Sun Nov 13 16:40:14 2022 +0100
// which is the time of commit
// feaf4ba6bc635fec442f46ddd4512416ec43c2c2 (refs/heads/master)
assert.EqualValues(t, 1563741793, lct.Unix())
// ce064814f4a0d337b333e646ece456cd39fab612 (refs/heads/master)
assert.EqualValues(t, 1668354014, lct.Unix())
}
func TestRepoIsEmpty(t *testing.T) {

Binary file not shown.

View File

@ -1 +1,2 @@
37991dec2c8e592043f47155ce4808d4580f9123 feaf4ba6bc635fec442f46ddd4512416ec43c2c2 silverwind <me@silverwind.io> 1563741799 +0200 push
feaf4ba6bc635fec442f46ddd4512416ec43c2c2 ce064814f4a0d337b333e646ece456cd39fab612 silverwind <me@silverwind.io> 1668354026 +0100 push

View File

@ -1 +1,2 @@
37991dec2c8e592043f47155ce4808d4580f9123 feaf4ba6bc635fec442f46ddd4512416ec43c2c2 silverwind <me@silverwind.io> 1563741799 +0200 push
feaf4ba6bc635fec442f46ddd4512416ec43c2c2 ce064814f4a0d337b333e646ece456cd39fab612 silverwind <me@silverwind.io> 1668354026 +0100 push

View File

@ -1 +1 @@
feaf4ba6bc635fec442f46ddd4512416ec43c2c2
ce064814f4a0d337b333e646ece456cd39fab612

View File

@ -0,0 +1 @@
36f97d9a96457e2bab511db30fe2db03893ebc64

46
modules/label/label.go Normal file
View File

@ -0,0 +1,46 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package label
import (
"fmt"
"regexp"
"strings"
)
// colorPattern is a regexp which can validate label color
var colorPattern = regexp.MustCompile("^#?(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{3})$")
// Label represents label information loaded from template
type Label struct {
Name string `yaml:"name"`
Color string `yaml:"color"`
Description string `yaml:"description,omitempty"`
Exclusive bool `yaml:"exclusive,omitempty"`
}
// NormalizeColor normalizes a color string to a 6-character hex code
func NormalizeColor(color string) (string, error) {
// normalize case
color = strings.TrimSpace(strings.ToLower(color))
// add leading hash
if len(color) == 6 || len(color) == 3 {
color = "#" + color
}
if !colorPattern.MatchString(color) {
return "", fmt.Errorf("bad color code: %s", color)
}
// convert 3-character shorthand into 6-character version
if len(color) == 4 {
r := color[1]
g := color[2]
b := color[3]
color = fmt.Sprintf("#%c%c%c%c%c%c", r, r, g, g, b, b)
}
return color, nil
}

126
modules/label/parser.go Normal file
View File

@ -0,0 +1,126 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package label
import (
"errors"
"fmt"
"strings"
"code.gitea.io/gitea/modules/options"
"gopkg.in/yaml.v3"
)
type labelFile struct {
Labels []*Label `yaml:"labels"`
}
// ErrTemplateLoad represents a "ErrTemplateLoad" kind of error.
type ErrTemplateLoad struct {
TemplateFile string
OriginalError error
}
// IsErrTemplateLoad checks if an error is a ErrTemplateLoad.
func IsErrTemplateLoad(err error) bool {
_, ok := err.(ErrTemplateLoad)
return ok
}
func (err ErrTemplateLoad) Error() string {
return fmt.Sprintf("Failed to load label template file '%s': %v", err.TemplateFile, err.OriginalError)
}
// GetTemplateFile loads the label template file by given name,
// then parses and returns a list of name-color pairs and optionally description.
func GetTemplateFile(name string) ([]*Label, error) {
data, err := options.GetRepoInitFile("label", name+".yaml")
if err == nil && len(data) > 0 {
return parseYamlFormat(name+".yaml", data)
}
data, err = options.GetRepoInitFile("label", name+".yml")
if err == nil && len(data) > 0 {
return parseYamlFormat(name+".yml", data)
}
data, err = options.GetRepoInitFile("label", name)
if err != nil {
return nil, ErrTemplateLoad{name, fmt.Errorf("GetRepoInitFile: %w", err)}
}
return parseLegacyFormat(name, data)
}
func parseYamlFormat(name string, data []byte) ([]*Label, error) {
lf := &labelFile{}
if err := yaml.Unmarshal(data, lf); err != nil {
return nil, err
}
// Validate label data and fix colors
for _, l := range lf.Labels {
l.Color = strings.TrimSpace(l.Color)
if len(l.Name) == 0 || len(l.Color) == 0 {
return nil, ErrTemplateLoad{name, errors.New("label name and color are required fields")}
}
color, err := NormalizeColor(l.Color)
if err != nil {
return nil, ErrTemplateLoad{name, fmt.Errorf("bad HTML color code '%s' in label: %s", l.Color, l.Name)}
}
l.Color = color
}
return lf.Labels, nil
}
func parseLegacyFormat(name string, data []byte) ([]*Label, error) {
lines := strings.Split(string(data), "\n")
list := make([]*Label, 0, len(lines))
for i := 0; i < len(lines); i++ {
line := strings.TrimSpace(lines[i])
if len(line) == 0 {
continue
}
parts, description, _ := strings.Cut(line, ";")
color, name, ok := strings.Cut(parts, " ")
if !ok {
return nil, ErrTemplateLoad{name, fmt.Errorf("line is malformed: %s", line)}
}
color, err := NormalizeColor(color)
if err != nil {
return nil, ErrTemplateLoad{name, fmt.Errorf("bad HTML color code '%s' in line: %s", color, line)}
}
list = append(list, &Label{
Name: strings.TrimSpace(name),
Color: color,
Description: strings.TrimSpace(description),
})
}
return list, nil
}
// LoadFormatted loads the labels' list of a template file as a string separated by comma
func LoadFormatted(name string) (string, error) {
var buf strings.Builder
list, err := GetTemplateFile(name)
if err != nil {
return "", err
}
for i := 0; i < len(list); i++ {
if i > 0 {
buf.WriteString(", ")
}
buf.WriteString(list[i].Name)
}
return buf.String(), nil
}

View File

@ -0,0 +1,72 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package label
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestYamlParser(t *testing.T) {
data := []byte(`labels:
- name: priority/low
exclusive: true
color: "#0000ee"
description: "Low priority"
- name: priority/medium
exclusive: true
color: "0e0"
description: "Medium priority"
- name: priority/high
exclusive: true
color: "#ee0000"
description: "High priority"
- name: type/bug
color: "#f00"
description: "Bug"`)
labels, err := parseYamlFormat("test", data)
require.NoError(t, err)
require.Len(t, labels, 4)
assert.Equal(t, "priority/low", labels[0].Name)
assert.True(t, labels[0].Exclusive)
assert.Equal(t, "#0000ee", labels[0].Color)
assert.Equal(t, "Low priority", labels[0].Description)
assert.Equal(t, "priority/medium", labels[1].Name)
assert.True(t, labels[1].Exclusive)
assert.Equal(t, "#00ee00", labels[1].Color)
assert.Equal(t, "Medium priority", labels[1].Description)
assert.Equal(t, "priority/high", labels[2].Name)
assert.True(t, labels[2].Exclusive)
assert.Equal(t, "#ee0000", labels[2].Color)
assert.Equal(t, "High priority", labels[2].Description)
assert.Equal(t, "type/bug", labels[3].Name)
assert.False(t, labels[3].Exclusive)
assert.Equal(t, "#ff0000", labels[3].Color)
assert.Equal(t, "Bug", labels[3].Description)
}
func TestLegacyParser(t *testing.T) {
data := []byte(`#ee0701 bug ; Something is not working
#cccccc duplicate ; This issue or pull request already exists
#84b6eb enhancement`)
labels, err := parseLegacyFormat("test", data)
require.NoError(t, err)
require.Len(t, labels, 3)
assert.Equal(t, "bug", labels[0].Name)
assert.False(t, labels[0].Exclusive)
assert.Equal(t, "#ee0701", labels[0].Color)
assert.Equal(t, "Something is not working", labels[0].Description)
assert.Equal(t, "duplicate", labels[1].Name)
assert.False(t, labels[1].Exclusive)
assert.Equal(t, "#cccccc", labels[1].Color)
assert.Equal(t, "This issue or pull request already exists", labels[1].Description)
assert.Equal(t, "enhancement", labels[2].Name)
assert.False(t, labels[2].Exclusive)
assert.Equal(t, "#84b6eb", labels[2].Color)
assert.Empty(t, labels[2].Description)
}

View File

@ -132,6 +132,8 @@ func createDefaultPolicy() *bluemonday.Policy {
policy.AllowAttrs(generalSafeAttrs...).OnElements(generalSafeElements...)
policy.AllowAttrs("src", "autoplay", "controls").OnElements("video")
policy.AllowAttrs("itemscope", "itemtype").OnElements("div")
// FIXME: Need to handle longdesc in img but there is no easy way to do it

44
modules/options/repo.go Normal file
View File

@ -0,0 +1,44 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package options
import (
"fmt"
"os"
"path"
"strings"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
)
// GetRepoInitFile returns repository init files
func GetRepoInitFile(tp, name string) ([]byte, error) {
cleanedName := strings.TrimLeft(path.Clean("/"+name), "/")
relPath := path.Join("options", tp, cleanedName)
// Use custom file when available.
customPath := path.Join(setting.CustomPath, relPath)
isFile, err := util.IsFile(customPath)
if err != nil {
log.Error("Unable to check if %s is a file. Error: %v", customPath, err)
}
if isFile {
return os.ReadFile(customPath)
}
switch tp {
case "readme":
return Readme(cleanedName)
case "gitignore":
return Gitignore(cleanedName)
case "license":
return License(cleanedName)
case "label":
return Labels(cleanedName)
default:
return []byte{}, fmt.Errorf("Invalid init file type")
}
}

View File

@ -124,7 +124,10 @@ func (q *ChannelQueue) Shutdown() {
log.Trace("ChannelQueue: %s Flushing", q.name)
// We can't use Cleanup here because that will close the channel
if err := q.FlushWithContext(q.terminateCtx); err != nil {
log.Warn("ChannelQueue: %s Terminated before completed flushing", q.name)
count := atomic.LoadInt64(&q.numInQueue)
if count > 0 {
log.Warn("ChannelQueue: %s Terminated before completed flushing", q.name)
}
return
}
log.Debug("ChannelQueue: %s Flushed", q.name)

View File

@ -94,7 +94,8 @@ func NewPersistableChannelQueue(handle HandlerFunc, cfg, exemplar interface{}) (
},
Workers: 0,
},
DataDir: config.DataDir,
DataDir: config.DataDir,
QueueName: config.Name + "-level",
}
levelQueue, err := NewLevelQueue(wrappedHandle, levelCfg, exemplar)
@ -172,16 +173,18 @@ func (q *PersistableChannelQueue) Run(atShutdown, atTerminate func(func())) {
atShutdown(q.Shutdown)
atTerminate(q.Terminate)
if lq, ok := q.internal.(*LevelQueue); ok && lq.byteFIFO.Len(lq.shutdownCtx) != 0 {
if lq, ok := q.internal.(*LevelQueue); ok && lq.byteFIFO.Len(lq.terminateCtx) != 0 {
// Just run the level queue - we shut it down once it's flushed
go q.internal.Run(func(_ func()) {}, func(_ func()) {})
go func() {
for !q.IsEmpty() {
_ = q.internal.Flush(0)
for !lq.IsEmpty() {
_ = lq.Flush(0)
select {
case <-time.After(100 * time.Millisecond):
case <-q.internal.(*LevelQueue).shutdownCtx.Done():
log.Warn("LevelQueue: %s shut down before completely flushed", q.internal.(*LevelQueue).Name())
case <-lq.shutdownCtx.Done():
if lq.byteFIFO.Len(lq.terminateCtx) > 0 {
log.Warn("LevelQueue: %s shut down before completely flushed", q.internal.(*LevelQueue).Name())
}
return
}
}
@ -316,10 +319,22 @@ func (q *PersistableChannelQueue) Shutdown() {
// Redirect all remaining data in the chan to the internal channel
log.Trace("PersistableChannelQueue: %s Redirecting remaining data", q.delayedStarter.name)
close(q.channelQueue.dataChan)
countOK, countLost := 0, 0
for data := range q.channelQueue.dataChan {
_ = q.internal.Push(data)
err := q.internal.Push(data)
if err != nil {
log.Error("PersistableChannelQueue: %s Unable redirect %v due to: %v", q.delayedStarter.name, data, err)
countLost++
} else {
countOK++
}
atomic.AddInt64(&q.channelQueue.numInQueue, -1)
}
if countLost > 0 {
log.Warn("PersistableChannelQueue: %s %d will be restored on restart, %d lost", q.delayedStarter.name, countOK, countLost)
} else if countOK > 0 {
log.Warn("PersistableChannelQueue: %s %d will be restored on restart", q.delayedStarter.name, countOK)
}
log.Trace("PersistableChannelQueue: %s Done Redirecting remaining data", q.delayedStarter.name)
log.Debug("PersistableChannelQueue: %s Shutdown", q.delayedStarter.name)

View File

@ -39,7 +39,7 @@ func TestPersistableChannelQueue(t *testing.T) {
Workers: 1,
BoostWorkers: 0,
MaxWorkers: 10,
Name: "first",
Name: "test-queue",
}, &testData{})
assert.NoError(t, err)
@ -135,7 +135,7 @@ func TestPersistableChannelQueue(t *testing.T) {
Workers: 1,
BoostWorkers: 0,
MaxWorkers: 10,
Name: "second",
Name: "test-queue",
}, &testData{})
assert.NoError(t, err)
@ -227,7 +227,7 @@ func TestPersistableChannelQueue_Pause(t *testing.T) {
Workers: 1,
BoostWorkers: 0,
MaxWorkers: 10,
Name: "first",
Name: "test-queue",
}, &testData{})
assert.NoError(t, err)
@ -433,7 +433,7 @@ func TestPersistableChannelQueue_Pause(t *testing.T) {
Workers: 1,
BoostWorkers: 0,
MaxWorkers: 10,
Name: "second",
Name: "test-queue",
}, &testData{})
assert.NoError(t, err)
pausable, ok = queue.(Pausable)

View File

@ -177,7 +177,9 @@ func (q *ChannelUniqueQueue) Shutdown() {
go func() {
log.Trace("ChannelUniqueQueue: %s Flushing", q.name)
if err := q.FlushWithContext(q.terminateCtx); err != nil {
log.Warn("ChannelUniqueQueue: %s Terminated before completed flushing", q.name)
if !q.IsEmpty() {
log.Warn("ChannelUniqueQueue: %s Terminated before completed flushing", q.name)
}
return
}
log.Debug("ChannelUniqueQueue: %s Flushed", q.name)

View File

@ -8,10 +8,13 @@ import (
"testing"
"time"
"code.gitea.io/gitea/modules/log"
"github.com/stretchr/testify/assert"
)
func TestChannelUniqueQueue(t *testing.T) {
_ = log.NewLogger(1000, "console", "console", `{"level":"warn","stacktracelevel":"NONE","stderr":true}`)
handleChan := make(chan *testData)
handle := func(data ...Data) []Data {
for _, datum := range data {
@ -52,6 +55,8 @@ func TestChannelUniqueQueue(t *testing.T) {
}
func TestChannelUniqueQueue_Batch(t *testing.T) {
_ = log.NewLogger(1000, "console", "console", `{"level":"warn","stacktracelevel":"NONE","stderr":true}`)
handleChan := make(chan *testData)
handle := func(data ...Data) []Data {
for _, datum := range data {
@ -98,6 +103,8 @@ func TestChannelUniqueQueue_Batch(t *testing.T) {
}
func TestChannelUniqueQueue_Pause(t *testing.T) {
_ = log.NewLogger(1000, "console", "console", `{"level":"warn","stacktracelevel":"NONE","stderr":true}`)
lock := sync.Mutex{}
var queue Queue
var err error

View File

@ -94,7 +94,8 @@ func NewPersistableChannelUniqueQueue(handle HandlerFunc, cfg, exemplar interfac
},
Workers: 0,
},
DataDir: config.DataDir,
DataDir: config.DataDir,
QueueName: config.Name + "-level",
}
queue.channelQueue = channelUniqueQueue.(*ChannelUniqueQueue)
@ -209,17 +210,29 @@ func (q *PersistableChannelUniqueQueue) Run(atShutdown, atTerminate func(func())
atTerminate(q.Terminate)
_ = q.channelQueue.AddWorkers(q.channelQueue.workers, 0)
if luq, ok := q.internal.(*LevelUniqueQueue); ok && luq.ByteFIFOUniqueQueue.byteFIFO.Len(luq.shutdownCtx) != 0 {
if luq, ok := q.internal.(*LevelUniqueQueue); ok && !luq.IsEmpty() {
// Just run the level queue - we shut it down once it's flushed
go q.internal.Run(func(_ func()) {}, func(_ func()) {})
go luq.Run(func(_ func()) {}, func(_ func()) {})
go func() {
_ = q.internal.Flush(0)
log.Debug("LevelUniqueQueue: %s flushed so shutting down", q.internal.(*LevelUniqueQueue).Name())
q.internal.(*LevelUniqueQueue).Shutdown()
GetManager().Remove(q.internal.(*LevelUniqueQueue).qid)
_ = luq.Flush(0)
for !luq.IsEmpty() {
_ = luq.Flush(0)
select {
case <-time.After(100 * time.Millisecond):
case <-luq.shutdownCtx.Done():
if luq.byteFIFO.Len(luq.terminateCtx) > 0 {
log.Warn("LevelUniqueQueue: %s shut down before completely flushed", luq.Name())
}
return
}
}
log.Debug("LevelUniqueQueue: %s flushed so shutting down", luq.Name())
luq.Shutdown()
GetManager().Remove(luq.qid)
}()
} else {
log.Debug("PersistableChannelUniqueQueue: %s Skipping running the empty level queue", q.delayedStarter.name)
_ = q.internal.Flush(0)
q.internal.(*LevelUniqueQueue).Shutdown()
GetManager().Remove(q.internal.(*LevelUniqueQueue).qid)
}
@ -285,8 +298,20 @@ func (q *PersistableChannelUniqueQueue) Shutdown() {
// Redirect all remaining data in the chan to the internal channel
close(q.channelQueue.dataChan)
log.Trace("PersistableChannelUniqueQueue: %s Redirecting remaining data", q.delayedStarter.name)
countOK, countLost := 0, 0
for data := range q.channelQueue.dataChan {
_ = q.internal.Push(data)
err := q.internal.(*LevelUniqueQueue).Push(data)
if err != nil {
log.Error("PersistableChannelUniqueQueue: %s Unable redirect %v due to: %v", q.delayedStarter.name, data, err)
countLost++
} else {
countOK++
}
}
if countLost > 0 {
log.Warn("PersistableChannelUniqueQueue: %s %d will be restored on restart, %d lost", q.delayedStarter.name, countOK, countLost)
} else if countOK > 0 {
log.Warn("PersistableChannelUniqueQueue: %s %d will be restored on restart", q.delayedStarter.name, countOK)
}
log.Trace("PersistableChannelUniqueQueue: %s Done Redirecting remaining data", q.delayedStarter.name)

View File

@ -0,0 +1,259 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package queue
import (
"fmt"
"strconv"
"sync"
"testing"
"time"
"code.gitea.io/gitea/modules/log"
"github.com/stretchr/testify/assert"
)
func TestPersistableChannelUniqueQueue(t *testing.T) {
tmpDir := t.TempDir()
fmt.Printf("TempDir %s\n", tmpDir)
_ = log.NewLogger(1000, "console", "console", `{"level":"warn","stacktracelevel":"NONE","stderr":true}`)
// Common function to create the Queue
newQueue := func(name string, handle func(data ...Data) []Data) Queue {
q, err := NewPersistableChannelUniqueQueue(handle,
PersistableChannelUniqueQueueConfiguration{
Name: name,
DataDir: tmpDir,
QueueLength: 200,
MaxWorkers: 1,
BlockTimeout: 1 * time.Second,
BoostTimeout: 5 * time.Minute,
BoostWorkers: 1,
Workers: 0,
}, "task-0")
assert.NoError(t, err)
return q
}
// runs the provided queue and provides some timer function
type channels struct {
readyForShutdown chan struct{} // closed when shutdown functions have been assigned
readyForTerminate chan struct{} // closed when terminate functions have been assigned
signalShutdown chan struct{} // Should close to signal shutdown
doneShutdown chan struct{} // closed when shutdown function is done
queueTerminate []func() // list of atTerminate functions to call atTerminate - need to be accessed with lock
}
runQueue := func(q Queue, lock *sync.Mutex) *channels {
chans := &channels{
readyForShutdown: make(chan struct{}),
readyForTerminate: make(chan struct{}),
signalShutdown: make(chan struct{}),
doneShutdown: make(chan struct{}),
}
go q.Run(func(atShutdown func()) {
go func() {
lock.Lock()
select {
case <-chans.readyForShutdown:
default:
close(chans.readyForShutdown)
}
lock.Unlock()
<-chans.signalShutdown
atShutdown()
close(chans.doneShutdown)
}()
}, func(atTerminate func()) {
lock.Lock()
defer lock.Unlock()
select {
case <-chans.readyForTerminate:
default:
close(chans.readyForTerminate)
}
chans.queueTerminate = append(chans.queueTerminate, atTerminate)
})
return chans
}
// call to shutdown and terminate the queue associated with the channels
doTerminate := func(chans *channels, lock *sync.Mutex) {
<-chans.readyForTerminate
lock.Lock()
callbacks := []func(){}
callbacks = append(callbacks, chans.queueTerminate...)
lock.Unlock()
for _, callback := range callbacks {
callback()
}
}
mapLock := sync.Mutex{}
executedInitial := map[string][]string{}
hasInitial := map[string][]string{}
fillQueue := func(name string, done chan struct{}) {
t.Run("Initial Filling: "+name, func(t *testing.T) {
lock := sync.Mutex{}
startAt100Queued := make(chan struct{})
stopAt20Shutdown := make(chan struct{}) // stop and shutdown at the 20th item
handle := func(data ...Data) []Data {
<-startAt100Queued
for _, datum := range data {
s := datum.(string)
mapLock.Lock()
executedInitial[name] = append(executedInitial[name], s)
mapLock.Unlock()
if s == "task-20" {
close(stopAt20Shutdown)
}
}
return nil
}
q := newQueue(name, handle)
// add 100 tasks to the queue
for i := 0; i < 100; i++ {
_ = q.Push("task-" + strconv.Itoa(i))
}
close(startAt100Queued)
chans := runQueue(q, &lock)
<-chans.readyForShutdown
<-stopAt20Shutdown
close(chans.signalShutdown)
<-chans.doneShutdown
_ = q.Push("final")
// check which tasks are still in the queue
for i := 0; i < 100; i++ {
if has, _ := q.(UniqueQueue).Has("task-" + strconv.Itoa(i)); has {
mapLock.Lock()
hasInitial[name] = append(hasInitial[name], "task-"+strconv.Itoa(i))
mapLock.Unlock()
}
}
if has, _ := q.(UniqueQueue).Has("final"); has {
mapLock.Lock()
hasInitial[name] = append(hasInitial[name], "final")
mapLock.Unlock()
} else {
assert.Fail(t, "UnqueQueue %s should have \"final\"", name)
}
doTerminate(chans, &lock)
mapLock.Lock()
assert.Equal(t, 101, len(executedInitial[name])+len(hasInitial[name]))
mapLock.Unlock()
})
close(done)
}
doneA := make(chan struct{})
doneB := make(chan struct{})
go fillQueue("QueueA", doneA)
go fillQueue("QueueB", doneB)
<-doneA
<-doneB
executedEmpty := map[string][]string{}
hasEmpty := map[string][]string{}
emptyQueue := func(name string, done chan struct{}) {
t.Run("Empty Queue: "+name, func(t *testing.T) {
lock := sync.Mutex{}
stop := make(chan struct{})
// collect the tasks that have been executed
handle := func(data ...Data) []Data {
lock.Lock()
for _, datum := range data {
mapLock.Lock()
executedEmpty[name] = append(executedEmpty[name], datum.(string))
mapLock.Unlock()
if datum.(string) == "final" {
close(stop)
}
}
lock.Unlock()
return nil
}
q := newQueue(name, handle)
chans := runQueue(q, &lock)
<-chans.readyForShutdown
<-stop
close(chans.signalShutdown)
<-chans.doneShutdown
// check which tasks are still in the queue
for i := 0; i < 100; i++ {
if has, _ := q.(UniqueQueue).Has("task-" + strconv.Itoa(i)); has {
mapLock.Lock()
hasEmpty[name] = append(hasEmpty[name], "task-"+strconv.Itoa(i))
mapLock.Unlock()
}
}
doTerminate(chans, &lock)
mapLock.Lock()
assert.Equal(t, 101, len(executedInitial[name])+len(executedEmpty[name]))
assert.Equal(t, 0, len(hasEmpty[name]))
mapLock.Unlock()
})
close(done)
}
doneA = make(chan struct{})
doneB = make(chan struct{})
go emptyQueue("QueueA", doneA)
go emptyQueue("QueueB", doneB)
<-doneA
<-doneB
mapLock.Lock()
t.Logf("TestPersistableChannelUniqueQueue executedInitiallyA=%v, executedInitiallyB=%v, executedToEmptyA=%v, executedToEmptyB=%v",
len(executedInitial["QueueA"]), len(executedInitial["QueueB"]), len(executedEmpty["QueueA"]), len(executedEmpty["QueueB"]))
// reset and rerun
executedInitial = map[string][]string{}
hasInitial = map[string][]string{}
executedEmpty = map[string][]string{}
hasEmpty = map[string][]string{}
mapLock.Unlock()
doneA = make(chan struct{})
doneB = make(chan struct{})
go fillQueue("QueueA", doneA)
go fillQueue("QueueB", doneB)
<-doneA
<-doneB
doneA = make(chan struct{})
doneB = make(chan struct{})
go emptyQueue("QueueA", doneA)
go emptyQueue("QueueB", doneB)
<-doneA
<-doneB
mapLock.Lock()
t.Logf("TestPersistableChannelUniqueQueue executedInitiallyA=%v, executedInitiallyB=%v, executedToEmptyA=%v, executedToEmptyB=%v",
len(executedInitial["QueueA"]), len(executedInitial["QueueB"]), len(executedEmpty["QueueA"]), len(executedEmpty["QueueB"]))
mapLock.Unlock()
}

View File

@ -23,6 +23,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/label"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
@ -189,7 +190,7 @@ func CreateRepository(doer, u *user_model.User, opts CreateRepoOptions) (*repo_m
// Check if label template exist
if len(opts.IssueLabels) > 0 {
if _, err := GetLabelTemplateFile(opts.IssueLabels); err != nil {
if _, err := label.GetTemplateFile(opts.IssueLabels); err != nil {
return nil, err
}
}

View File

@ -18,6 +18,7 @@ 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/label"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/options"
"code.gitea.io/gitea/modules/setting"
@ -40,114 +41,6 @@ var (
LabelTemplates map[string]string
)
// ErrIssueLabelTemplateLoad represents a "ErrIssueLabelTemplateLoad" kind of error.
type ErrIssueLabelTemplateLoad struct {
TemplateFile string
OriginalError error
}
// IsErrIssueLabelTemplateLoad checks if an error is a ErrIssueLabelTemplateLoad.
func IsErrIssueLabelTemplateLoad(err error) bool {
_, ok := err.(ErrIssueLabelTemplateLoad)
return ok
}
func (err ErrIssueLabelTemplateLoad) Error() string {
return fmt.Sprintf("Failed to load label template file '%s': %v", err.TemplateFile, err.OriginalError)
}
// GetRepoInitFile returns repository init files
func GetRepoInitFile(tp, name string) ([]byte, error) {
cleanedName := strings.TrimLeft(path.Clean("/"+name), "/")
relPath := path.Join("options", tp, cleanedName)
// Use custom file when available.
customPath := path.Join(setting.CustomPath, relPath)
isFile, err := util.IsFile(customPath)
if err != nil {
log.Error("Unable to check if %s is a file. Error: %v", customPath, err)
}
if isFile {
return os.ReadFile(customPath)
}
switch tp {
case "readme":
return options.Readme(cleanedName)
case "gitignore":
return options.Gitignore(cleanedName)
case "license":
return options.License(cleanedName)
case "label":
return options.Labels(cleanedName)
default:
return []byte{}, fmt.Errorf("Invalid init file type")
}
}
// GetLabelTemplateFile loads the label template file by given name,
// then parses and returns a list of name-color pairs and optionally description.
func GetLabelTemplateFile(name string) ([][3]string, error) {
data, err := GetRepoInitFile("label", name)
if err != nil {
return nil, ErrIssueLabelTemplateLoad{name, fmt.Errorf("GetRepoInitFile: %w", err)}
}
lines := strings.Split(string(data), "\n")
list := make([][3]string, 0, len(lines))
for i := 0; i < len(lines); i++ {
line := strings.TrimSpace(lines[i])
if len(line) == 0 {
continue
}
parts := strings.SplitN(line, ";", 2)
fields := strings.SplitN(parts[0], " ", 2)
if len(fields) != 2 {
return nil, ErrIssueLabelTemplateLoad{name, fmt.Errorf("line is malformed: %s", line)}
}
color := strings.Trim(fields[0], " ")
if len(color) == 6 {
color = "#" + color
}
if !issues_model.LabelColorPattern.MatchString(color) {
return nil, ErrIssueLabelTemplateLoad{name, fmt.Errorf("bad HTML color code in line: %s", line)}
}
var description string
if len(parts) > 1 {
description = strings.TrimSpace(parts[1])
}
fields[1] = strings.TrimSpace(fields[1])
list = append(list, [3]string{fields[1], color, description})
}
return list, nil
}
func loadLabels(labelTemplate string) ([]string, error) {
list, err := GetLabelTemplateFile(labelTemplate)
if err != nil {
return nil, err
}
labels := make([]string, len(list))
for i := 0; i < len(list); i++ {
labels[i] = list[i][0]
}
return labels, nil
}
// LoadLabelsFormatted loads the labels' list of a template file as a string separated by comma
func LoadLabelsFormatted(labelTemplate string) (string, error) {
labels, err := loadLabels(labelTemplate)
return strings.Join(labels, ", "), err
}
// LoadRepoConfig loads the repository config
func LoadRepoConfig() {
// Load .gitignore and license files and readme templates.
@ -158,6 +51,14 @@ func LoadRepoConfig() {
if err != nil {
log.Fatal("Failed to get %s files: %v", t, err)
}
if t == "label" {
for i, f := range files {
ext := strings.ToLower(filepath.Ext(f))
if ext == ".yaml" || ext == ".yml" {
files[i] = f[:len(f)-len(ext)]
}
}
}
customPath := path.Join(setting.CustomPath, "options", t)
isDir, err := util.IsDir(customPath)
if err != nil {
@ -190,7 +91,7 @@ func LoadRepoConfig() {
// Load label templates
LabelTemplates = make(map[string]string)
for _, templateFile := range LabelTemplatesFiles {
labels, err := LoadLabelsFormatted(templateFile)
labels, err := label.LoadFormatted(templateFile)
if err != nil {
log.Error("Failed to load labels: %v", err)
}
@ -235,7 +136,7 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir,
}
// README
data, err := GetRepoInitFile("readme", opts.Readme)
data, err := options.GetRepoInitFile("readme", opts.Readme)
if err != nil {
return fmt.Errorf("GetRepoInitFile[%s]: %w", opts.Readme, err)
}
@ -263,7 +164,7 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir,
var buf bytes.Buffer
names := strings.Split(opts.Gitignores, ",")
for _, name := range names {
data, err = GetRepoInitFile("gitignore", name)
data, err = options.GetRepoInitFile("gitignore", name)
if err != nil {
return fmt.Errorf("GetRepoInitFile[%s]: %w", name, err)
}
@ -281,7 +182,7 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir,
// LICENSE
if len(opts.License) > 0 {
data, err = GetRepoInitFile("license", opts.License)
data, err = options.GetRepoInitFile("license", opts.License)
if err != nil {
return fmt.Errorf("GetRepoInitFile[%s]: %w", opts.License, err)
}
@ -443,7 +344,7 @@ func initRepository(ctx context.Context, repoPath string, u *user_model.User, re
// InitializeLabels adds a label set to a repository using a template
func InitializeLabels(ctx context.Context, id int64, labelTemplate string, isOrg bool) error {
list, err := GetLabelTemplateFile(labelTemplate)
list, err := label.GetTemplateFile(labelTemplate)
if err != nil {
return err
}
@ -451,9 +352,10 @@ func InitializeLabels(ctx context.Context, id int64, labelTemplate string, isOrg
labels := make([]*issues_model.Label, len(list))
for i := 0; i < len(list); i++ {
labels[i] = &issues_model.Label{
Name: list[i][0],
Description: list[i][2],
Color: list[i][1],
Name: list[i].Name,
Exclusive: list[i].Exclusive,
Description: list[i].Description,
Color: list[i].Color,
}
if isOrg {
labels[i].OrgID = id

View File

@ -0,0 +1,70 @@
labels:
- name: "Kind/Bug"
color: ee0701
description: Something is not working
- name: "Kind/Feature"
color: 0288d1
description: New functionality
- name: "Kind/Enhancement"
color: 84b6eb
description: Improve existing functionality
- name: "Kind/Security"
color: 9c27b0
description: This is security issue
- name: "Kind/Testing"
color: 795548
description: Issue or pull request related to testing
- name: "Kind/Breaking"
color: c62828
description: Breaking change that won't be backward compatible
- name: "Kind/Documentation"
color: 37474f
description: Documentation changes
- name: "Reviewed/Duplicate"
exclusive: true
color: 616161
description: This issue or pull request already exists
- name: "Reviewed/Invalid"
exclusive: true
color: 546e7a
description: Invalid issue
- name: "Reviewed/Confirmed"
exclusive: true
color: 795548
description: Issue has been confirmed
- name: "Reviewed/Won't Fix"
exclusive: true
color: eeeeee
description: This issue won't be fixed
- name: "Status/Need More Info"
exclusive: true
color: 424242
description: Feedback is required to reproduce issue or to continue work
- name: "Status/Blocked"
exclusive: true
color: 880e4f
description: Something is blocking this issue or pull request
- name: "Status/Abandoned"
exclusive: true
color: "222222"
description: Somebody has started to work on this but abandoned work
- name: "Priority/Critical"
exclusive: true
color: b71c1c
description: The priority is critical
priority: critical
- name: "Priority/High"
exclusive: true
color: d32f2f
description: The priority is high
priority: high
- name: "Priority/Medium"
exclusive: true
color: e64a19
description: The priority is medium
priority: medium
- name: "Priority/Low"
exclusive: true
color: 4caf50
description: The priority is low
priority: low

View File

@ -237,7 +237,6 @@ internal_token_failed = Failed to generate internal token: %v
secret_key_failed = Failed to generate secret key: %v
save_config_failed = Failed to save configuration: %v
invalid_admin_setting = Administrator account setting is invalid: %v
install_success = Welcome! Thank you for choosing Gitea. Have fun and take care!
invalid_log_root_path = The log path is invalid: %v
default_keep_email_private = Hide Email Addresses by Default
default_keep_email_private_popup = Hide email addresses of new user accounts by default.
@ -248,6 +247,7 @@ default_enable_timetracking_popup = Enable time tracking for new repositories by
no_reply_address = Hidden Email Domain
no_reply_address_helper = Domain name for users with a hidden email address. For example, the username 'joe' will be logged in Git as 'joe@noreply.example.org' if the hidden email domain is set to 'noreply.example.org'.
password_algorithm = Password Hash Algorithm
invalid_password_algorithm = Invalid password hash algorithm
password_algorithm_helper = Set the password hashing algorithm. Algorithms have differing requirements and strength. `argon2` whilst having good characteristics uses a lot of memory and may be inappropriate for small systems.
enable_update_checker = Enable Update Checker
enable_update_checker_helper = Checks for new version releases periodically by connecting to gitea.io.
@ -1475,6 +1475,7 @@ issues.error_removing_due_date = "Failed to remove the due date."
issues.push_commit_1 = "added %d commit %s"
issues.push_commits_n = "added %d commits %s"
issues.force_push_codes = `force-pushed %[1]s from <a class="ui sha" href="%[3]s"><code>%[2]s</code></a> to <a class="ui sha" href="%[5]s"><code>%[4]s</code></a> %[6]s`
issues.force_push_compare = Compare
issues.due_date_form = "yyyy-mm-dd"
issues.due_date_form_add = "Add due date"
issues.due_date_form_edit = "Edit"

View File

@ -57,6 +57,7 @@ new_mirror=新しいミラー
new_fork=新しいフォーク
new_org=新しい組織
new_project=新しいプロジェクト
new_project_column=新しい列
manage_org=組織を管理
admin_panel=サイト管理
account_settings=アカウント設定
@ -90,6 +91,7 @@ disabled=無効
copy=コピー
copy_url=URLをコピー
copy_content=内容をコピー
copy_branch=ブランチ名をコピー
copy_success=コピーされました!
copy_error=コピーに失敗しました
@ -247,6 +249,8 @@ no_reply_address=メールを隠すときのドメイン
no_reply_address_helper=メールアドレスを隠しているユーザーに使用するドメイン名。 例えば 'noreply.example.org' と設定した場合、ユーザー名 'joe' はGitに 'joe@noreply.example.org' としてログインすることになります。
password_algorithm=パスワードハッシュアルゴリズム
password_algorithm_helper=パスワードハッシュアルゴリズムを設定します。 アルゴリズムにより動作要件と強度が異なります。 `argon2`は良い特性を備えていますが、多くのメモリを使用するため小さなシステムには適さない場合があります。
enable_update_checker=アップデートチェッカーを有効にする
enable_update_checker_helper=gitea.ioに接続して定期的に新しいバージョンのリリースを確認します。
[home]
uname_holder=ユーザー名またはメールアドレス
@ -324,6 +328,7 @@ email_not_associate=このメールアドレスは、どのアカウントにも
send_reset_mail=アカウント回復メールを送信
reset_password=アカウントの回復
invalid_code=確認コードが無効か期限切れです。
invalid_password=アカウントの作成に使用されたパスワードと一致しません。
reset_password_helper=アカウント回復
reset_password_wrong_user=アカウント回復のリンクは %[2]s のものですが、あなたは %[1]s でサインイン中です
password_too_short=%d文字未満のパスワードは設定できません。
@ -367,6 +372,7 @@ password_pwned_err=HaveIBeenPwnedへのリクエストを完了できません
[mail]
view_it_on=%s で見る
reply=またはこのメールに直接返信してください
link_not_working_do_paste=開かないですか? コピーしてブラウザーに貼り付けてみてください。
hi_user_x=こんにちは、<b>%s</b> さん。
@ -510,6 +516,7 @@ duplicate_invite_to_team=指定したユーザーはすでにチームメンバ
invalid_ssh_key=SSHキーが確認できません: %s
invalid_gpg_key=GPGキーが確認できません: %s
invalid_ssh_principal=無効なプリンシパル: %s
must_use_public_key=あなたが提供したキーは秘密鍵です。秘密鍵をどこにもアップロードしないでください。代わりに公開鍵を使用してください。
unable_verify_ssh_key=SSHキーが確認できません。間違いが無いか、よく確認してください。
auth_failed=認証に失敗しました: %v
@ -746,6 +753,8 @@ access_token_deletion_cancel_action=キャンセル
access_token_deletion_confirm_action=削除
access_token_deletion_desc=トークンを削除すると、それを使用しているアプリケーションは、アカウントへのアクセスができなくなります。これは元に戻せません。続行しますか?
delete_token_success=トークンを削除しました。 削除したトークンを使用しているアプリケーションは、今後あなたのアカウントにアクセスできません。
select_scopes=スコープを選択
scopes_list=スコープ:
manage_oauth2_applications=OAuth2アプリケーションの管理
edit_oauth2_application=OAuth2アプリケーションの編集
@ -1018,10 +1027,12 @@ unstar=スター取消
star=スター
fork=フォーク
download_archive=リポジトリをダウンロード
more_operations=その他の操作
no_desc=説明なし
quick_guide=クイック ガイド
clone_this_repo=このリポジトリのクローンを作成
cite_this_repo=このリポジトリを引用
create_new_repo_command=コマンドラインから新しいリポジトリを作成
push_exist_repo=コマンドラインから既存のリポジトリをプッシュ
empty_message=このリポジトリの中には何もありません。
@ -1120,6 +1131,7 @@ editor.commit_directly_to_this_branch=ブランチ<strong class="branch-name">%s
editor.create_new_branch=<strong>新しいブランチ</strong>にコミットしてプルリクエストを作成する。
editor.create_new_branch_np=<strong>新しいブランチ</strong>にコミットする。
editor.propose_file_change=ファイル修正を提案
editor.new_branch_name=このコミットの新しいブランチに名前を付けます
editor.new_branch_name_desc=新しいブランチ名…
editor.cancel=キャンセル
editor.filename_cannot_be_empty=ファイル名は空にできません。
@ -1171,6 +1183,7 @@ commits.signed_by_untrusted_user_unmatched=コミッターと一致しない信
commits.gpg_key_id=GPGキーID
commits.ssh_key_fingerprint=SSH鍵のフィンガープリント
commit.operations=操作
commit.revert=リバート
commit.revert-header=リバート: %s
commit.revert-content=リバートするブランチを選択:
@ -1195,19 +1208,30 @@ projects.deletion_desc=プロジェクトを削除し、関連するすべての
projects.deletion_success=プロジェクトを削除しました。
projects.edit=プロジェクトの編集
projects.edit_subheader=プロジェクトはイシューをまとめ、進捗を管理します。
projects.modify=プロジェクトを更新
projects.modify=プロジェクトを編集
projects.edit_success=プロジェクト '%s' を更新しました。
projects.type.none=なし
projects.type.basic_kanban=基本的なカンバン
projects.type.bug_triage=バグ トリアージ
projects.template.desc=プロジェクト テンプレート
projects.template.desc=テンプレート
projects.template.desc_helper=開始するプロジェクトテンプレートを選択
projects.type.uncategorized=未分類
projects.column.edit=列を編集
projects.column.edit_title=名称
projects.column.new_title=名称
projects.column.new_submit=列を作成
projects.column.new=新しい列
projects.column.set_default=デフォルトに設定
projects.column.set_default_desc=この列を未分類のイシューやプルリクエストが入るデフォルトの列にします
projects.column.delete=列を削除
projects.column.deletion_desc=プロジェクト列を削除すると、関連するすべてのイシューが '未分類' に移動します。 続行しますか?
projects.column.color=カラー
projects.open=オープン
projects.close=クローズ
projects.column.assigned_to=担当
projects.card_type.desc=カードプレビュー
projects.card_type.images_and_text=画像とテキスト
projects.card_type.text_only=テキストのみ
issues.desc=バグ報告、タスク、マイルストーンの作成。
issues.filter_assignees=候補者の絞り込み
@ -1284,6 +1308,7 @@ issues.filter_label_no_select=すべてのラベル
issues.filter_milestone=マイルストーン
issues.filter_milestone_no_select=すべてのマイルストーン
issues.filter_project=プロジェクト
issues.filter_project_all=すべてのプロジェクト
issues.filter_project_none=プロジェクトなし
issues.filter_assignee=担当者
issues.filter_assginee_no_select=すべての担当者
@ -1295,6 +1320,7 @@ issues.filter_type.assigned_to_you=自分が担当
issues.filter_type.created_by_you=自分が作成
issues.filter_type.mentioning_you=自分が関係
issues.filter_type.review_requested=レビュー依頼あり
issues.filter_type.reviewed_by_you=自分がレビュー
issues.filter_sort=並べ替え
issues.filter_sort.latest=新しい順
issues.filter_sort.oldest=古い順
@ -1316,6 +1342,8 @@ issues.action_milestone=マイルストーン
issues.action_milestone_no_select=マイルストーンなし
issues.action_assignee=担当者
issues.action_assignee_no_select=担当者なし
issues.action_check=チェックを設定/解除します
issues.action_check_all=すべての項目のチェックを設定/解除します
issues.opened_by=<a href="%[2]s">%[3]s</a>が%[1]sに作成
pulls.merged_by=<a href="%[2]s">%[3]s</a>が作成、%[1]sにマージ
pulls.merged_by_fake=%[2]sが作成、%[1]sにマージ
@ -1622,6 +1650,7 @@ pulls.reopened_at=`がプルリクエストを再オープン <a id="%[1]s" href
pulls.merge_instruction_hint=`<a class="show-instruction">コマンドラインの手順</a>も確認できます。`
pulls.merge_instruction_step1_desc=あなたのプロジェクトリポジトリで新しいブランチをチェックアウトし、変更内容をテストします。
pulls.merge_instruction_step2_desc=変更内容をマージして、Giteaに反映します。
pulls.clear_merge_message=マージメッセージをクリア
pulls.auto_merge_button_when_succeed=(チェックがすべて成功した場合)
pulls.auto_merge_when_succeed=すべてのチェックが成功したら自動マージ
@ -1813,6 +1842,7 @@ settings.mirror_sync_in_progress=ミラー同期を実行しています。 し
settings.site=Webサイト
settings.update_settings=設定を更新
settings.branches.update_default_branch=デフォルトブランチを更新
settings.branches.add_new_rule=新しいルールを追加
settings.advanced_settings=拡張設定
settings.wiki_desc=Wikiを有効にする
settings.use_internal_wiki=ビルトインのWikiを使用する
@ -1842,8 +1872,11 @@ settings.pulls.ignore_whitespace=空白文字のコンフリクトを無視す
settings.pulls.enable_autodetect_manual_merge=手動マージの自動検出を有効にする (注意: 特殊なケースでは判定ミスが発生する場合があります)
settings.pulls.allow_rebase_update=リベースでプルリクエストのブランチの更新を可能にする
settings.pulls.default_delete_branch_after_merge=デフォルトでプルリクエストのブランチをマージ後に削除する
settings.pulls.default_allow_edits_from_maintainers=デフォルトでメンテナからの編集を許可する
settings.releases_desc=リリースを有効にする
settings.packages_desc=リポジトリパッケージレジストリを有効にする
settings.projects_desc=リポジトリプロジェクトを有効にする
settings.actions_desc=Actionsを有効にする
settings.admin_settings=管理者用設定
settings.admin_enable_health_check=リポジトリのヘルスチェックを有効にする (git fsck)
settings.admin_code_indexer=コードインデクサ
@ -2053,6 +2086,8 @@ settings.deploy_key_deletion_desc=デプロイキーを削除し、リポジト
settings.deploy_key_deletion_success=デプロイキーを削除しました。
settings.branches=ブランチ
settings.protected_branch=ブランチの保護
settings.protected_branch.save_rule=ルールを保存
settings.protected_branch.delete_rule=ルールを削除
settings.protected_branch_can_push=プッシュを許可する
settings.protected_branch_can_push_yes=プッシュできます
settings.protected_branch_can_push_no=プッシュできません
@ -2087,15 +2122,17 @@ settings.dismiss_stale_approvals=古くなった承認を取り消す
settings.dismiss_stale_approvals_desc=プルリクエストの内容を変える新たなコミットがブランチにプッシュされた場合、以前の承認を取り消します。
settings.require_signed_commits=コミット署名必須
settings.require_signed_commits_desc=署名されていない場合、または署名が検証できなかった場合は、このブランチへのプッシュを拒否します。
settings.protect_branch_name_pattern=保護ブランチ名のパターン
settings.protect_protected_file_patterns=保護されるファイルのパターン (セミコロン'\;'で区切る):
settings.protect_protected_file_patterns_desc=保護されたファイルは、このブランチにファイルを追加・編集・削除する権限を持つユーザーであっても、直接変更することができなくなります。 セミコロン('\;')で区切って複数のパターンを指定できます。 パターンの文法については <a href="https://pkg.go.dev/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> を参照してください。 例: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>
settings.protect_unprotected_file_patterns=保護しないファイルのパターン (セミコロン'\;'で区切る):
settings.protect_unprotected_file_patterns_desc=保護しないファイルは、ユーザーに書き込み権限があればプッシュ制限をバイパスして直接変更できます。 セミコロン('\;')で区切って複数のパターンを指定できます。 パターンの文法については <a href="https://pkg.go.dev/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> を参照してください。 例: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>
settings.add_protected_branch=保護を有効にする
settings.delete_protected_branch=保護を無効にする
settings.update_protect_branch_success=ブランチ '%s' の保護を更新しました。
settings.remove_protected_branch_success=ブランチ '%s' の保護を無効にしました。
settings.protected_branch_deletion=ブランチ保護の無効化
settings.update_protect_branch_success=ルール '%s' に対するブランチ保護を更新しました。
settings.remove_protected_branch_success=ルール '%s' に対するブランチ保護を削除しました。
settings.remove_protected_branch_failed=ブランチ保護ルール '%s' を削除できませんでした。
settings.protected_branch_deletion=ブランチ保護の削除
settings.protected_branch_deletion_desc=ブランチ保護を無効にすると、書き込み権限を持つユーザーにブランチへのプッシュを許可することになります。 続行しますか?
settings.block_rejected_reviews=不承認レビューでマージをブロック
settings.block_rejected_reviews_desc=公式レビューアが変更を要請しているときは、承認数を満たしていてもマージできないようにします。
@ -2104,10 +2141,13 @@ settings.block_on_official_review_requests_desc=公式レビュー依頼があ
settings.block_outdated_branch=遅れているプルリクエストのマージをブロック
settings.block_outdated_branch_desc=baseブランチがheadブランチより進んでいる場合、マージできないようにします。
settings.default_branch_desc=プルリクエストやコミット表示のデフォルトのブランチを選択:
settings.default_merge_style_desc=プルリクエストのデフォルトのマージ方法:
settings.merge_style_desc=マージ スタイル
settings.default_merge_style_desc=デフォルトのマージスタイル
settings.choose_branch=ブランチを選択…
settings.no_protected_branch=保護しているブランチはありません。
settings.edit_protected_branch=編集
settings.protected_branch_required_rule_name=ルール名は必須です
settings.protected_branch_duplicate_rule_name=ルール名が重複しています
settings.protected_branch_required_approvals_min=必要な承認数は負の数にできません。
settings.tags=タグ
settings.tags.protection=タグの保護
@ -2530,6 +2570,10 @@ dashboard.delete_old_actions=データベースから古い操作履歴をすべ
dashboard.delete_old_actions.started=データベースからの古い操作履歴の削除を開始しました。
dashboard.update_checker=更新チェック
dashboard.delete_old_system_notices=データベースから古いシステム通知をすべて削除
dashboard.gc_lfs=LFSメタオブジェクトのガベージコレクション
dashboard.stop_zombie_tasks=ゾンビタスクを停止
dashboard.stop_endless_tasks=終わらないタスクを停止
dashboard.cancel_abandoned_jobs=放置されたままのジョブをキャンセル
users.user_manage_panel=ユーザーアカウント管理
users.new_account=ユーザーアカウントを作成
@ -2973,6 +3017,7 @@ monitor.queue.pool.cancel_desc=キューをワーカーグループ無しのま
notices.system_notice_list=システム通知
notices.view_detail_header=通知の詳細を表示
notices.operations=操作
notices.select_all=すべて選択
notices.deselect_all=すべて選択解除
notices.inverse_selection=選択を反転
@ -3097,6 +3142,8 @@ keywords=キーワード
details=詳細
details.author=著作者
details.project_site=プロジェクトサイト
details.repository_site=リポジトリサイト
details.documentation_site=ドキュメンテーションサイト
details.license=ライセンス
assets=アセット
versions=バージョン
@ -3104,6 +3151,8 @@ versions.on=on
versions.view_all=すべて表示
dependency.id=ID
dependency.version=バージョン
cargo.details.repository_site=リポジトリサイト
cargo.details.documentation_site=ドキュメンテーションサイト
chef.install=パッケージをインストールするには、次のコマンドを実行します:
composer.registry=あなたの <code>~/.composer/config.json</code> ファイルに、このレジストリをセットアップします:
composer.install=Composer を使用してパッケージをインストールするには、次のコマンドを実行します:
@ -3114,6 +3163,11 @@ conan.details.repository=リポジトリ
conan.registry=このレジストリをコマンドラインからセットアップします:
conan.install=Conan を使用してパッケージをインストールするには、次のコマンドを実行します:
conan.documentation=Conan レジストリの詳細については、 <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/conan/">ドキュメント</a> を参照してください。
conda.registry=あなたの <code>.condarc</code> ファイルに、このレジストリを Conda リポジトリとしてセットアップします:
conda.install=Conda を使用してパッケージをインストールするには、次のコマンドを実行します:
conda.documentation=Condaレジストリの詳細については、 <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/conda/">ドキュメント</a> を参照してください。
conda.details.repository_site=リポジトリサイト
conda.details.documentation_site=ドキュメンテーションサイト
container.details.type=イメージタイプ
container.details.platform=プラットフォーム
container.pull=コマンドラインでイメージを取得します:
@ -3172,26 +3226,91 @@ settings.delete.description=パッケージの削除は恒久的で元に戻す
settings.delete.notice=%s (%s) を削除しようとしています。この操作は元に戻せません。よろしいですか?
settings.delete.success=パッケージを削除しました。
settings.delete.error=パッケージの削除に失敗しました。
owner.settings.cleanuprules.title=クリーンアップルールの管理
owner.settings.cleanuprules.add=クリーンアップルールを追加
owner.settings.cleanuprules.edit=クリーンアップルールを編集
owner.settings.cleanuprules.none=クリーンアップルールはありません。詳細はドキュメントをご覧ください。
owner.settings.cleanuprules.preview=クリーンアップルールをプレビュー
owner.settings.cleanuprules.preview.overview=%d パッケージが削除される予定です。
owner.settings.cleanuprules.preview.none=クリーンアップルールと一致するパッケージがありません。
owner.settings.cleanuprules.enabled=有効
owner.settings.cleanuprules.success.update=クリーンアップルールが更新されました。
owner.settings.cleanuprules.success.delete=クリーンアップルールが削除されました。
owner.settings.chef.title=Chefレジストリ
owner.settings.chef.keypair=キーペアを生成
owner.settings.chef.keypair.description=Chefレジストリの認証に使用するキーペアを生成します。 それ以降は、以前のキーは使用できなくなります。
[secrets]
secrets=シークレット
description=シークレットは特定のActionsに渡されます。 それ以外で読み出されることはありません。
none=まだシークレットはありません。
value=
name=名称
creation=シークレットを追加
creation.name_placeholder=大文字小文字の区別なし、英数字とアンダースコアのみ、GITEA_ や GITHUB_ で始まるものは不可
creation.value_placeholder=内容を入力してください。前後の空白は除去されます。
creation.success=シークレット '%s' を追加しました。
creation.failed=シークレットの追加に失敗しました。
deletion=シークレットの削除
deletion.description=シークレットの削除は恒久的で元に戻すことはできません。 続行しますか?
deletion.success=シークレットを削除しました。
deletion.failed=シークレットの削除に失敗しました。
[actions]
actions=Actions
unit.desc=Actionsの管理
status.unknown=不明
status.waiting=待機中
status.running=実行中
status.success=成功
status.failure=失敗
status.cancelled=キャンセルされた
status.skipped=スキップ
status.blocked=ブロックされた
runners=ランナー
runners.runner_manage_panel=ランナーの管理
runners.new=新しいランナーを作成
runners.new_notice=ランナーの開始方法
runners.status=ステータス
runners.id=ID
runners.name=名称
runners.owner_type=タイプ
runners.description=説明
runners.labels=ラベル
runners.last_online=最終オンライン時刻
runners.agent_labels=エージェントのラベル
runners.custom_labels=カスタムラベル
runners.custom_labels_helper=カスタムラベルは管理者により手動で追加されたラベルです。 ラベルはカンマで区切り、各ラベルの前後の空白は無視されます。
runners.runner_title=ランナー
runners.task_list=このランナーの最近のタスク
runners.task_list.run=実行
runners.task_list.status=ステータス
runners.task_list.repository=リポジトリ
runners.task_list.commit=コミット
runners.status.active=有効
runners.task_list.done_at=終了時刻
runners.edit_runner=ランナーの編集
runners.update_runner=変更を保存
runners.update_runner_success=ランナーを更新しました
runners.update_runner_failed=ランナーの更新に失敗しました
runners.delete_runner=このランナーを削除
runners.delete_runner_success=ランナーを削除しました
runners.delete_runner_failed=ランナーの削除に失敗しました
runners.delete_runner_header=ランナー削除の確認
runners.delete_runner_notice=このランナーでタスクが実行されている場合、タスクは停止され失敗扱いとなります。 それによりビルドワークフローが途中で終了することになるかもしれません。
runners.none=利用可能なランナーはありません
runners.status.unspecified=不明
runners.status.idle=アイドル
runners.status.active=稼働中
runners.status.offline=オフライン
runs.all_workflows=すべてのワークフロー
runs.open_tab=%d オープン
runs.closed_tab=%d クローズ
runs.commit=コミット
runs.pushed_by=Pushed by
need_approval_desc=フォークプルリクエストのワークフローを実行するには承認が必要です。

View File

@ -112,7 +112,9 @@ never=Nunca
rss_feed=Feed RSS
[aria]
navbar=Barra de navegação
footer=Rodapé
footer.software=Sobre o Software
footer.links=Links
[filter]
@ -248,6 +250,7 @@ no_reply_address_helper=Nome de domínio para usuários com um endereço de e-ma
password_algorithm=Algoritmo Hash de Senha
password_algorithm_helper=Escolha o algoritmo de hash para as senhas. Diferentes algoritmos têm requerimentos e forças diversos. O `Argon2` possui boa qualidade, porém usa muita memória e pode ser inapropriado para sistemas com menos recursos.
enable_update_checker=Habilitar Verificador de Atualizações
enable_update_checker_helper=Procura por novas versões periodicamente conectando-se ao gitea.io.
[home]
uname_holder=Usuário ou e-mail
@ -283,7 +286,9 @@ organizations=Organizações
search=Pesquisar
code=Código
search.fuzzy=Similar
search.fuzzy.tooltip=Incluir resultados que sejam próximos ao termo de busca
search.match=Correspondência
search.match.tooltip=Incluir somente resultados que correspondam exatamente ao termo de busca
code_search_unavailable=A pesquisa por código não está disponível no momento. Entre em contato com o administrador do site.
repo_no_results=Nenhum repositório correspondente foi encontrado.
user_no_results=Nenhum usuário correspondente foi encontrado.
@ -322,6 +327,7 @@ email_not_associate=O endereço de e-mail não está associado à nenhuma conta.
send_reset_mail=Enviar e-mail de recuperação de conta
reset_password=Recuperação de conta
invalid_code=Seu código de confirmação é inválido ou expirou.
invalid_password=Sua senha não coincide com a senha que foi usada para criar a conta.
reset_password_helper=Recuperar conta
reset_password_wrong_user=Você está conectado como %s, mas o link de recuperação de conta é para %s
password_too_short=A senha deve ter %d ou mais caracteres.
@ -365,6 +371,7 @@ password_pwned_err=Não foi possível concluir a requisição ao HaveIBeenPwned
[mail]
view_it_on=Veja em %s
reply=ou responda diretamente a este email
link_not_working_do_paste=Não está funcionando? Tente copiá-lo e colá-lo no seu navegador.
hi_user_x=Olá <b>%s</b>,
@ -423,6 +430,10 @@ repo.transfer.body=Para o aceitar ou rejeitar visite %s, ou simplesmente o ignor
repo.collaborator.added.subject=%s adicionou você a %s
repo.collaborator.added.text=Você foi adicionado como um colaborador do repositório:
team_invite.subject=%[1]s convidou você para participar da organização %[2]s
team_invite.text_1=%[1]s convidou você para participar da equipe %[2]s na organização %[3]s.
team_invite.text_2=Por favor, clique no seguinte link para se juntar à equipe:
team_invite.text_3=Nota: este convite foi destinado a %[1]s. Se você não estava esperando este convite, você pode ignorar este e-mail.
[modal]
yes=Sim
@ -464,6 +475,8 @@ url_error=`'%s' não é uma URL válida.`
include_error=` deve conter '%s'.`
glob_pattern_error=` padrão glob é inválido: %s.`
regex_pattern_error=` o regex é inválido: %s.`
username_error=` só pode conter caracteres alfanuméricos ('0-9','a-z','A-Z'), traço ('-'), sublinhado ('_') e ponto ('.'). Não pode começar ou terminar com caracteres não alfanuméricos, e caracteres não-alfanuméricos consecutivos também são proibidos.`
invalid_group_team_map_error=` mapeamento é inválido: %s`
unknown_error=Erro desconhecido:
captcha_incorrect=O código CAPTCHA está incorreto.
password_not_match=As senhas não coincidem.
@ -499,10 +512,13 @@ user_not_exist=O usuário não existe.
team_not_exist=A equipe não existe.
last_org_owner=Você não pode remover o último usuário do time 'proprietários'. Deve haver pelo menos um proprietário em uma organização.
cannot_add_org_to_team=Uma organização não pode ser adicionada como membro de uma equipe.
duplicate_invite_to_team=O usuário já foi convidado para se juntar da equipe.
organization_leave_success=Você saiu da organização %s com sucesso.
invalid_ssh_key=Não é possível verificar sua chave SSH: %s
invalid_gpg_key=Não é possível verificar sua chave GPG: %s
invalid_ssh_principal=Nome principal inválido: %s
must_use_public_key=A chave que você forneceu é uma chave privada. Por favor, não envie sua chave privada em nenhum lugar. Use sua chave pública em vez disso.
unable_verify_ssh_key=Não é possível verificar sua chave SSH
auth_failed=Autenticação falhou: %v
@ -739,6 +755,7 @@ access_token_deletion_cancel_action=Cancelar
access_token_deletion_confirm_action=Excluir
access_token_deletion_desc=A exclusão de um token revoga o acesso à sua conta para aplicativos que o usam. Continuar?
delete_token_success=O token foi excluído. Os aplicativos que o utilizam já não têm acesso à sua conta.
select_scopes=Selecione os escopos
scopes_list=Escopos:
manage_oauth2_applications=Gerenciar aplicativos OAuth2
@ -1011,10 +1028,12 @@ unstar=Retirar dos favoritos
star=Juntar aos favoritos
fork=Fork
download_archive=Baixar repositório
more_operations=Mais Operações
no_desc=Nenhuma descrição
quick_guide=Guia Rápido
clone_this_repo=Clonar este repositório
cite_this_repo=Citar este repositório
create_new_repo_command=Criando um novo repositório por linha de comando
push_exist_repo=Realizando push para um repositório existente por linha de comando
empty_message=Este repositório está vazio.
@ -1113,6 +1132,7 @@ editor.commit_directly_to_this_branch=Commit diretamente no branch <strong class
editor.create_new_branch=Crie um <strong>novo branch</strong> para este commit e crie um pull request.
editor.create_new_branch_np=Crie um <strong>novo branch</strong> para este commit.
editor.propose_file_change=Propor alteração de arquivo
editor.new_branch_name=Nome do novo branch para este commit
editor.new_branch_name_desc=Novo nome do branch...
editor.cancel=Cancelar
editor.filename_cannot_be_empty=Nome do arquivo não pode ser em branco.
@ -1197,12 +1217,21 @@ projects.type.bug_triage=Triagem de Bugs
projects.template.desc=Modelo de projeto
projects.template.desc_helper=Selecione um modelo de projeto para começar
projects.type.uncategorized=Sem categoria
projects.column.edit=Editar coluna
projects.column.edit_title=Nome
projects.column.new_title=Nome
projects.column.new_submit=Criar coluna
projects.column.new=Nova Coluna
projects.column.set_default=Definir padrão
projects.column.set_default_desc=Definir esta coluna como padrão para pull e issues sem categoria
projects.column.delete=Excluir coluna
projects.column.deletion_desc=Excluir uma coluna do projeto move todas as issues relacionadas para 'Sem categoria'. Continuar?
projects.column.color=Colorido
projects.open=Abrir
projects.close=Fechar
projects.column.assigned_to=Atribuído a
projects.card_type.images_and_text=Imagens e Texto
projects.card_type.text_only=Somente texto
issues.desc=Organize relatórios de bugs, tarefas e marcos.
issues.filter_assignees=Filtrar Atribuição
@ -1279,6 +1308,7 @@ issues.filter_label_no_select=Todas as etiquetas
issues.filter_milestone=Marco
issues.filter_milestone_no_select=Todos os marcos
issues.filter_project=Projeto
issues.filter_project_all=Todos os projetos
issues.filter_project_none=Sem projeto
issues.filter_assignee=Atribuído
issues.filter_assginee_no_select=Todos os responsáveis
@ -1290,6 +1320,7 @@ issues.filter_type.assigned_to_you=Atribuídos a você
issues.filter_type.created_by_you=Criado por você
issues.filter_type.mentioning_you=Mencionando você
issues.filter_type.review_requested=Revisão solicitada
issues.filter_type.reviewed_by_you=Revisado por você
issues.filter_sort=Ordenação
issues.filter_sort.latest=Mais recentes
issues.filter_sort.oldest=Mais antigos
@ -1364,6 +1395,7 @@ issues.save=Salvar
issues.label_title=Nome da etiqueta
issues.label_description=Descrição da etiqueta
issues.label_color=Cor da etiqueta
issues.label_exclusive=Exclusivo
issues.label_count=%d etiquetas
issues.label_open_issues=%d issues abertas
issues.label_edit=Editar
@ -1617,6 +1649,7 @@ pulls.reopened_at=`reabriu este pull request <a id="%[1]s" href="#%[1]s">%[2]s</
pulls.merge_instruction_hint=`Você também pode ver as <a class="show-instruction">instruções para a linha de comandos</a>.`
pulls.merge_instruction_step1_desc=No repositório do seu projeto, crie um novo branch e teste as alterações.
pulls.merge_instruction_step2_desc=Faça merge das alterações e atualize no Gitea.
pulls.clear_merge_message=Limpar mensagem do merge
pulls.auto_merge_button_when_succeed=(Quando a verificação for bem-sucedida)
pulls.auto_merge_when_succeed=Mesclar automaticamente quando todas as verificações forem bem sucedidas
@ -1773,7 +1806,9 @@ activity.git_stats_deletion_n=%d exclusões
search=Pesquisar
search.search_repo=Pesquisar no repositório...
search.fuzzy=Aproximada
search.fuzzy.tooltip=Incluir resultados que sejam próximos ao termo de busca
search.match=Corresponde
search.match.tooltip=Incluir somente resultados que correspondam exatamente ao termo de busca
search.results=Resultados da pesquisa para "%s" em <a href="%s">%s</a>
search.code_no_results=Nenhum código-fonte correspondente ao seu termo de pesquisa foi encontrado.
search.code_search_unavailable=A pesquisa por código não está disponível no momento. Entre em contato com o administrador do site.
@ -1805,6 +1840,7 @@ settings.mirror_sync_in_progress=Sincronização do espelhamento está em andame
settings.site=Site
settings.update_settings=Atualizar configurações
settings.branches.update_default_branch=Atualizar Branch Padrão
settings.branches.add_new_rule=Adicionar Nova Regra
settings.advanced_settings=Configurações avançadas
settings.wiki_desc=Habilitar a wiki do repositório
settings.use_internal_wiki=Usar a wiki nativa
@ -1834,8 +1870,11 @@ settings.pulls.ignore_whitespace=Ignorar espaço em branco em conflitos
settings.pulls.enable_autodetect_manual_merge=Habilitar a detecção automática de merge manual (Nota: Em alguns casos especiais, podem ocorrer julgamentos errados)
settings.pulls.allow_rebase_update=Ativar atualização do branch do pull request por rebase
settings.pulls.default_delete_branch_after_merge=Excluir o branch de pull request após o merge por padrão
settings.pulls.default_allow_edits_from_maintainers=Permitir edições de mantenedores por padrão
settings.releases_desc=Habilitar versões do Repositório
settings.packages_desc=Habilitar Registro de Pacotes de Repositório
settings.projects_desc=Habilitar Projetos do Repositório
settings.actions_desc=Habilitar ações do repositório
settings.admin_settings=Configurações do administrador
settings.admin_enable_health_check=Habilitar verificações de integridade (git fsck) no repositório
settings.admin_code_indexer=Indexador de código
@ -2044,6 +2083,8 @@ settings.deploy_key_deletion_desc=A exclusão de uma chave de deploy irá revoga
settings.deploy_key_deletion_success=A chave de deploy foi removida.
settings.branches=Branches
settings.protected_branch=Proteção de Branch
settings.protected_branch.save_rule=Salvar Regra
settings.protected_branch.delete_rule=Excluir Regra
settings.protected_branch_can_push=Permitir push?
settings.protected_branch_can_push_yes=Você pode fazer push
settings.protected_branch_can_push_no=Você não pode fazer push
@ -2095,6 +2136,7 @@ settings.block_on_official_review_requests_desc=O merge não será possível qua
settings.block_outdated_branch=Bloquear o merge se o pull request estiver desatualizado
settings.block_outdated_branch_desc=O merge não será possível quando o branch de topo estiver atrás do branch base.
settings.default_branch_desc=Selecione um branch padrão para pull requests e commits de código:
settings.merge_style_desc=Estilos de Merge
settings.default_merge_style_desc=Estilo de merge padrão para pull requests:
settings.choose_branch=Escolha um branch...
settings.no_protected_branch=Não há branches protegidos.
@ -2661,6 +2703,7 @@ auths.admin_filter=Filtro de administrador
auths.restricted_filter=Filtro de restrição
auths.restricted_filter_helper=Deixe em branco para não definir nenhum usuário como restrito. Use um asterisco ('*') para definir todos os usuários que não correspondem ao Filtro de administrador como restritos.
auths.verify_group_membership=Verificar associação ao grupo no LDAP (deixe o filtro vazio para ignorar)
auths.group_search_base=Grupo de Pesquisa DN Base
auths.group_attribute_list_users=Atributo do Grupo que Contém a Lista de Usuários
auths.user_attribute_in_group=Atributo do Usuário Listado em Grupo
auths.map_group_to_team=Mapear grupos LDAP para Organizações (deixe o campo vazio para pular)
@ -3087,6 +3130,8 @@ keywords=Palavras-chave
details=Detalhes
details.author=Autor
details.project_site=Site do Projeto
details.repository_site=Site do Repositório
details.documentation_site=Site da Documentação
details.license=Licença
assets=Recursos
versions=Versões
@ -3094,6 +3139,11 @@ versions.on=em
versions.view_all=Ver todas
dependency.id=ID
dependency.version=Versão
cargo.registry=Configurar este registro no arquivo de configuração de Cargo (por exemplo <code>~/.cargo/config.toml</code>):
cargo.install=Para instalar o pacote usando Cargo, execute o seguinte comando:
cargo.details.repository_site=Site do Repositório
cargo.details.documentation_site=Site da Documentação
chef.registry=Configure este registro em seu arquivo <code>~/.chef/config.rb</code>:
chef.install=Para instalar o pacote, execute o seguinte comando:
composer.registry=Configure este registro em seu arquivo <code>~/.composer/config.json</code>:
composer.install=Para instalar o pacote usando o Composer, execute o seguinte comando:
@ -3104,6 +3154,11 @@ conan.details.repository=Repositório
conan.registry=Configure este registro pela linha de comando:
conan.install=Para instalar o pacote usando o Conan, execute o seguinte comando:
conan.documentation=Para obter mais informações sobre o registro Conan, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/conan/">a documentação</a>.
conda.registry=Configure este registro como um repositório Conda no arquivo <code>.condarc</code>:
conda.install=Para instalar o pacote usando o Conda, execute o seguinte comando:
conda.documentation=Para obter mais informações sobre o registro Conda, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/conda/">a documentação</a>.
conda.details.repository_site=Site do Repositório
conda.details.documentation_site=Site da Documentação
container.details.type=Tipo de Imagem
container.details.platform=Plataforma
container.pull=Puxe a imagem pela linha de comando:
@ -3162,26 +3217,78 @@ settings.delete.description=A exclusão de um pacote é permanente e não pode s
settings.delete.notice=Você está prestes a excluir %s (%s). Esta operação é irreversível, tem certeza?
settings.delete.success=O pacote foi excluído.
settings.delete.error=Falha ao excluir o pacote.
owner.settings.cleanuprules.title=Gerenciar Regras de Limpeza
owner.settings.cleanuprules.add=Adicionar Regra de Limpeza
owner.settings.cleanuprules.edit=Editar Regra de Limpeza
owner.settings.cleanuprules.none=Não há regras de limpeza disponíveis. Leia a documentação para saber mais.
owner.settings.cleanuprules.preview=Pré-visualizar Regra de Limpeza
owner.settings.cleanuprules.preview.overview=%d pacotes agendados para serem removidos.
owner.settings.cleanuprules.preview.none=A regra de limpeza não corresponde a nenhum pacote.
owner.settings.cleanuprules.enabled=Habilitado
owner.settings.cleanuprules.keep.title=Versões que correspondem a estas regras são mantidas, mesmo se corresponderem a uma regra de remoção abaixo.
owner.settings.cleanuprules.keep.count=Manter o mais recente
owner.settings.cleanuprules.keep.count.1=1 versão por pacote
owner.settings.cleanuprules.keep.count.n=%d versões por pacote
owner.settings.cleanuprules.keep.pattern=Manter versões correspondentes
owner.settings.cleanuprules.keep.pattern.container=A versão <code>latest</code> é sempre mantida para pacotes de Container.
owner.settings.cleanuprules.remove.title=Versões que correspondem a essas regras são removidas, a menos que uma regra acima diga para mantê-las.
owner.settings.cleanuprules.remove.days=Remover versões mais antigas que
owner.settings.cleanuprules.remove.pattern=Remover versões correspondentes
owner.settings.cleanuprules.success.update=Regra de limpeza foi atualizada.
owner.settings.cleanuprules.success.delete=Regra de limpeza foi excluída.
owner.settings.chef.title=Registro Chef
owner.settings.chef.keypair=Gerar par de chaves
[secrets]
secrets=Segredos
description=Os segredos serão passados a certas ações e não poderão ser lidos de outra forma.
none=Não há segredos ainda.
value=Valor
name=Nome
creation=Adicionar Segredo
creation.success=O segredo '%s' foi adicionado.
creation.failed=Falha ao adicionar segredo.
deletion=Excluir segredo
deletion.description=A exclusão de um segredo é permanente e não pode ser desfeita. Continuar?
deletion.success=O segredo foi excluído.
deletion.failed=Falha ao excluir segredo.
[actions]
actions=Ações
unit.desc=Gerenciar ações
status.unknown=Desconhecido
status.waiting=Em espera
status.running=Rodando
status.success=Sucesso
status.failure=Falha
status.cancelled=Cancelado
status.skipped=Ignorado
status.blocked=Bloqueado
runners.status=Status
runners.id=ID
runners.name=Nome
runners.owner_type=Tipo
runners.description=Descrição
runners.labels=Rótulos
runners.last_online=Última Vez Online
runners.custom_labels=Etiquetas Personalizadas
runners.custom_labels_helper=Etiquetas personalizadas são etiquetas que são adicionadas manualmente por um administrador. Separe as etiquetas com vírgula. Espaço em branco no começo ou no final de cada etiqueta é ignorado.
runners.task_list.run=Executar
runners.task_list.status=Status
runners.task_list.repository=Repositório
runners.task_list.commit=Commit
runners.update_runner=Atualizar as Alterações
runners.status.unspecified=Desconhecido
runners.status.idle=Inativo
runners.status.active=Ativo
runners.status.offline=Offiline
runs.open_tab=%d Aberto
runs.closed_tab=%d Fechado
runs.commit=Commit
runs.pushed_by=Push realizado por

View File

@ -57,6 +57,7 @@ new_mirror=Nova réplica
new_fork=Nova derivação do repositório
new_org=Nova organização
new_project=Novo planeamento
new_project_column=Nova coluna
manage_org=Gerir organizações
admin_panel=Administração do sítio
account_settings=Configurações da conta
@ -90,9 +91,11 @@ disabled=Desabilitado
copy=Copiar
copy_url=Copiar URL
copy_content=Copiar conteúdo
copy_branch=Copiar nome do ramo
copy_success=Copiado!
copy_error=Falha ao copiar
copy_type_unsupported=Este tipo de ficheiro não pode ser copiado
write=Escrever
preview=Pré-visualizar
@ -109,6 +112,10 @@ never=Nunca
rss_feed=Fonte RSS
[aria]
navbar=Barra de navegação
footer=Rodapé
footer.software=Sobre o Software
footer.links=Ligações
[filter]
string.asc=A - Z
@ -321,6 +328,7 @@ email_not_associate=O endereço de email não está associado a qualquer conta.
send_reset_mail=Enviar email de recuperação da conta
reset_password=Recuperação de conta
invalid_code=O seu código de confirmação é inválido ou expirou.
invalid_password=A sua senha não corresponde à senha que foi usada para criar a conta.
reset_password_helper=Recuperar conta
reset_password_wrong_user=Tem conta iniciada como %s, mas a ligação de recuperação de conta é para %s
password_too_short=O tamanho da senha não pode ser inferior a %d caracteres.
@ -364,6 +372,7 @@ password_pwned_err=Não foi possível completar o pedido ao HaveIBeenPwned
[mail]
view_it_on=Ver em %s
reply=ou responda a este email imediatamente
link_not_working_do_paste=Não está a funcionar? Tente copiar e colar no seu navegador.
hi_user_x=Olá <b>%s</b>,
@ -467,6 +476,8 @@ url_error=`'%s' não é um URL válido.`
include_error=` tem que conter o texto '%s'.`
glob_pattern_error=` o padrão glob é inválido: %s.`
regex_pattern_error=` o padrão regex é inválido: %s.`
username_error=` só pode conter caracteres alfanuméricos ('0-9','a-z','A-Z'), hífen ('-'), sublinhado ('_') e ponto ('.') Não pode começar nem terminar com caracteres não alfanuméricos, e caracteres não alfanuméricos consecutivos também são proibidos.`
invalid_group_team_map_error=` o mapeamento é inválido: %s`
unknown_error=Erro desconhecido:
captcha_incorrect=O código CAPTCHA está errado.
password_not_match=As senhas não coincidem.
@ -503,10 +514,12 @@ team_not_exist=A equipa não existe.
last_org_owner=Não pode remover o último utilizador da equipa 'proprietários'. Tem que haver pelo menos um proprietário numa organização.
cannot_add_org_to_team=Uma organização não pode ser adicionada como membro de uma equipa.
duplicate_invite_to_team=O(A) utilizador(a) já tinha sido convidado(a) para ser membro da equipa.
organization_leave_success=Você deixou a organização %s com sucesso.
invalid_ssh_key=Não é possível validar a sua chave SSH: %s
invalid_gpg_key=Não é possível validar a sua chave GPG: %s
invalid_ssh_principal=Protagonista inválido: %s
must_use_public_key=A chave que você forneceu é privada. Não carregue a sua chave em lugar nenhum, em vez disso use a sua chave pública.
unable_verify_ssh_key=Não é possível validar a chave SSH
auth_failed=Falhou a autenticação: %v
@ -743,6 +756,8 @@ access_token_deletion_cancel_action=Cancelar
access_token_deletion_confirm_action=Eliminar
access_token_deletion_desc=Eliminar um código revoga o acesso à sua conta nas aplicações que o usem. Esta operação não poderá ser revertida. Quer continuar?
delete_token_success=O código foi eliminado. Aplicações que o usavam deixaram de ter acesso à sua conta.
select_scopes=Escolha os âmbitos
scopes_list=Âmbitos:
manage_oauth2_applications=Gerir aplicações OAuth2
edit_oauth2_application=Editar aplicação OAuth2
@ -1015,10 +1030,12 @@ unstar=Tirar dos favoritos
star=Juntar aos favoritos
fork=Derivar
download_archive=Descarregar repositório
more_operations=Mais operações
no_desc=Sem descrição
quick_guide=Guia rápido
clone_this_repo=Clonar este repositório
cite_this_repo=Citar este repositório
create_new_repo_command=Criando um novo repositório na linha de comandos
push_exist_repo=Enviando, pela linha de comandos, um repositório existente
empty_message=Este repositório não contém qualquer conteúdo.
@ -1117,6 +1134,7 @@ editor.commit_directly_to_this_branch=Cometer imediatamente no ramo <strong clas
editor.create_new_branch=Crie um <strong>novo ramo</strong> para este cometimento e inicie um pedido de integração.
editor.create_new_branch_np=Criar um <strong>novo ramo</strong> para este cometimento.
editor.propose_file_change=Propor modificação do ficheiro
editor.new_branch_name=Dê um nome ao novo ramo para este cometimento
editor.new_branch_name_desc=Nome do novo ramo…
editor.cancel=Cancelar
editor.filename_cannot_be_empty=O nome do ficheiro não pode estar em branco.
@ -1168,6 +1186,7 @@ commits.signed_by_untrusted_user_unmatched=Assinado por um utilizador não fiáv
commits.gpg_key_id=ID da chave GPG
commits.ssh_key_fingerprint=Identificação digital da chave SSH
commit.operations=Operações
commit.revert=Reverter
commit.revert-header=Reverter: %s
commit.revert-content=Escolha o ramo para onde vai reverter:
@ -1200,11 +1219,22 @@ projects.type.bug_triage=Triagem de erros
projects.template.desc=Modelo de planeamento
projects.template.desc_helper=Escolha um modelo de planeamento para começar
projects.type.uncategorized=Sem categoria
projects.column.edit=Editar coluna
projects.column.edit_title=Nome
projects.column.new_title=Nome
projects.column.new_submit=Criar coluna
projects.column.new=Nova coluna
projects.column.set_default=Definir como padrão
projects.column.set_default_desc=Defina esta coluna como padrão para questões e pedidos de integração não categorizados
projects.column.delete=Eliminar coluna
projects.column.deletion_desc=Eliminar uma coluna de um planeamento faz com que todas as questões que nela constam sejam movidas para a coluna 'Sem categoria'. Continuar?
projects.column.color=Colorido
projects.open=Abrir
projects.close=Fechar
projects.column.assigned_to=Atribuída a
projects.card_type.desc=Previsão dos cartões
projects.card_type.images_and_text=Imagens e texto
projects.card_type.text_only=Apenas texto
issues.desc=Organize relatórios de erros, tarefas e etapas.
issues.filter_assignees=Filtrar encarregado
@ -1281,6 +1311,7 @@ issues.filter_label_no_select=Todos os rótulos
issues.filter_milestone=Etapa
issues.filter_milestone_no_select=Todas as etapas
issues.filter_project=Planeamento
issues.filter_project_all=Todos os planeamentos
issues.filter_project_none=Nenhum planeamento
issues.filter_assignee=Encarregado
issues.filter_assginee_no_select=Todos os encarregados
@ -1292,6 +1323,7 @@ issues.filter_type.assigned_to_you=Atribuídas a si
issues.filter_type.created_by_you=Criadas por si
issues.filter_type.mentioning_you=Mencionando a si
issues.filter_type.review_requested=Revisão solicitada
issues.filter_type.reviewed_by_you=Revistos por si
issues.filter_sort=Ordem
issues.filter_sort.latest=Mais recentes
issues.filter_sort.oldest=Mais antigas
@ -1313,6 +1345,8 @@ issues.action_milestone=Etapa
issues.action_milestone_no_select=Sem etapa
issues.action_assignee=Encarregado
issues.action_assignee_no_select=Sem encarregado
issues.action_check=Marcar/desmarcar
issues.action_check_all=Marcar/desmarcar todos os itens
issues.opened_by=aberta %[1]s por <a href="%[2]s">%[3]s</a>
pulls.merged_by=por <a href="%[2]s">%[3]s</a> foi executado %[1]s
pulls.merged_by_fake=por %[2]s foi executado %[1]s
@ -1366,6 +1400,9 @@ issues.save=Guardar
issues.label_title=Nome do rótulo
issues.label_description=Descrição do rótulo
issues.label_color=Cor do rótulo
issues.label_exclusive=Exclusivo
issues.label_exclusive_desc=Nomeie o rótulo <code>âmbito/item</code> para torná-lo mutuamente exclusivo com outros rótulos do <code>âmbito/</code>.
issues.label_exclusive_warning=Quaisquer rótulos com âmbito que estejam em conflito irão ser removidos ao editar os rótulos de uma questão ou de um pedido de integração.
issues.label_count=%d rótulos
issues.label_open_issues=%d questões abertas
issues.label_edit=Editar
@ -1619,6 +1656,8 @@ pulls.reopened_at=`reabriu este pedido de integração <a id="%[1]s" href="#%[1]
pulls.merge_instruction_hint=`Também pode ver as <a class="show-instruction">instruções para a linha de comandos</a>.`
pulls.merge_instruction_step1_desc=No seu repositório, crie um novo ramo e teste as modificações.
pulls.merge_instruction_step2_desc=Integre as modificações e envie para o Gitea.
pulls.clear_merge_message=Apagar mensagem de integração
pulls.clear_merge_message_hint=Apagar a mensagem de integração apenas remove o conteúdo da mensagem de cometimento e mantém os rodapés do git, tais como "Co-Autorado-Por …".
pulls.auto_merge_button_when_succeed=(quando as verificações forem bem-sucedidas)
pulls.auto_merge_when_succeed=Integrar automaticamente quando todas as verificações forem bem-sucedidas
@ -1810,6 +1849,7 @@ settings.mirror_sync_in_progress=A sincronização da réplica está em andament
settings.site=Sítio web
settings.update_settings=Modificar configurações
settings.branches.update_default_branch=Definir o ramo principal
settings.branches.add_new_rule=Adicionar nova regra
settings.advanced_settings=Configurações avançadas
settings.wiki_desc=Habilitar wiki do repositório
settings.use_internal_wiki=Usar o wiki nativo
@ -1839,8 +1879,11 @@ settings.pulls.ignore_whitespace=Ignorar espaços em branco nos conflitos
settings.pulls.enable_autodetect_manual_merge=Habilitar a identificação automática de integrações manuais (obs.: nalguns casos especiais a avaliação pode ser errada)
settings.pulls.allow_rebase_update=Habilitar a modificação do ramo do pedido de integração através da mudança de base
settings.pulls.default_delete_branch_after_merge=Eliminar o ramo do pedido de integração depois de finalizada a integração, como predefinição
settings.pulls.default_allow_edits_from_maintainers=Permitir, por padrão, que os responsáveis editem
settings.releases_desc=Habilitar lançamentos no repositório
settings.packages_desc=Habilitar o registo de pacotes do repositório
settings.projects_desc=Habilitar planeamentos no repositório
settings.actions_desc=Habilitar operações no repositório
settings.admin_settings=Configurações do administrador
settings.admin_enable_health_check=Habilitar verificações de integridade (git fsck) no repositório
settings.admin_code_indexer=Indexador de código
@ -2050,6 +2093,8 @@ settings.deploy_key_deletion_desc=Remover uma chave de instalação irá revogar
settings.deploy_key_deletion_success=A chave de instalação foi removida.
settings.branches=Ramos
settings.protected_branch=Salvaguarda do ramo
settings.protected_branch.save_rule=Guardar regra
settings.protected_branch.delete_rule=Eliminar regra
settings.protected_branch_can_push=Permitir envios?
settings.protected_branch_can_push_yes=Pode enviar
settings.protected_branch_can_push_no=Não pode enviar
@ -2084,6 +2129,7 @@ settings.dismiss_stale_approvals=Descartar aprovações obsoletas
settings.dismiss_stale_approvals_desc=Quando novos cometimentos que mudam o conteúdo do pedido de integração forem enviados para o ramo, as aprovações antigas serão descartadas.
settings.require_signed_commits=Exigir cometimentos assinados
settings.require_signed_commits_desc=Rejeitar envios para este ramo que não estejam assinados ou que não sejam validáveis.
settings.protect_branch_name_pattern=Padrão do nome do ramo protegido
settings.protect_protected_file_patterns=Padrões de ficheiros protegidos (separados com ponto e vírgula '\;'):
settings.protect_protected_file_patterns_desc=Ficheiros protegidos que não podem ser modificados, mesmo que o utilizador tenha direitos para adicionar, editar ou eliminar ficheiros neste ramo. Múltiplos padrões podem ser separados com ponto e vírgula ('\;'). Veja a documentação em <a href="https://godoc.org/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> para ver a sintaxe. Exemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
settings.protect_unprotected_file_patterns=Padrões de ficheiros desprotegidos (separados com ponto e vírgula '\;'):
@ -2092,6 +2138,7 @@ settings.add_protected_branch=Habilitar salvaguarda
settings.delete_protected_branch=Desabilitar salvaguarda
settings.update_protect_branch_success=A salvaguarda do ramo '%s' foi modificada.
settings.remove_protected_branch_success=A salvaguarda do ramo '%s' foi desabilitada.
settings.remove_protected_branch_failed=A remoção da regra '%s' de salvaguarda do ramo falhou.
settings.protected_branch_deletion=Desabilitar salvaguarda do ramo
settings.protected_branch_deletion_desc=Desabilitar a salvaguarda do ramo irá permitir que os utilizadores que tenham permissão de escrita enviem para o ramo. Quer continuar?
settings.block_rejected_reviews=Bloquear a integração quando há revisões rejeitadas
@ -2101,10 +2148,13 @@ settings.block_on_official_review_requests_desc=A integração não será possí
settings.block_outdated_branch=Bloquear integração se o pedido de integração for obsoleto
settings.block_outdated_branch_desc=A integração não será possível quando o ramo de topo estiver abaixo do ramo base.
settings.default_branch_desc=Escolha um ramo do repositório como sendo o predefinido para pedidos de integração e cometimentos:
settings.merge_style_desc=Estilos de integração
settings.default_merge_style_desc=Tipo de integração predefinido para pedidos de integração:
settings.choose_branch=Escolha um ramo…
settings.no_protected_branch=Não existem ramos protegidos.
settings.edit_protected_branch=Editar
settings.protected_branch_required_rule_name=Nome de regra obrigatório
settings.protected_branch_duplicate_rule_name=Nome de regra duplicado
settings.protected_branch_required_approvals_min=O número mínimo exigido de aprovações não pode ser negativo.
settings.tags=Etiquetas
settings.tags.protection=Proteger etiquetas
@ -2260,6 +2310,8 @@ release.downloads=Descargas
release.download_count=Descargas: %s
release.add_tag_msg=Usar o título e o conteúdo do lançamento como mensagem da etiqueta.
release.add_tag=Criar apenas a etiqueta
release.releases_for=Lançamentos para %s
release.tags_for=Etiquetas para %s
branch.name=Nome do ramo
branch.search=Procurar ramos
@ -2527,6 +2579,10 @@ dashboard.delete_old_actions=Eliminar todas as operações antigas da base de da
dashboard.delete_old_actions.started=Foi iniciado o processo de eliminação de todas as operações antigas da base de dados.
dashboard.update_checker=Verificador de novas versões
dashboard.delete_old_system_notices=Eliminar todas as notificações do sistema antigas da base de dados
dashboard.gc_lfs=Recolher lixo dos meta-elementos LFS
dashboard.stop_zombie_tasks=Parar tarefas zombies
dashboard.stop_endless_tasks=Parar tarefas intermináveis
dashboard.cancel_abandoned_jobs=Cancelar trabalhos abandonados
users.user_manage_panel=Gestão das contas de utilizadores
users.new_account=Criar conta de utilizador
@ -2615,6 +2671,7 @@ repos.size=Tamanho
packages.package_manage_panel=Gestão de pacotes
packages.total_size=Tamanho total: %s
packages.unreferenced_size=Tamanho não referenciado: %s
packages.owner=Proprietário
packages.creator=Criador
packages.name=Nome
@ -2708,6 +2765,8 @@ auths.oauth2_required_claim_value_helper=Defina este valor para restringir o in
auths.oauth2_group_claim_name=Reivindicar nome que fornece nomes de grupo para esta fonte. (Opcional)
auths.oauth2_admin_group=Valor da Reivindicação de Grupo para utilizadores administradores. (Opcional - exige a reivindicação de nome acima)
auths.oauth2_restricted_group=Valor da Reivindicação de Grupo para utilizadores restritos. (Opcional - exige a reivindicação de nome acima)
auths.oauth2_map_group_to_team=Mapear grupos reclamados em equipas da organização (opcional — requer nome de reclamação acima).
auths.oauth2_map_group_to_team_removal=Remover utilizadores das equipas sincronizadas se esses utilizadores não pertencerem ao grupo correspondente.
auths.enable_auto_register=Habilitar o registo automático
auths.sspi_auto_create_users=Criar utilizadores automaticamente
auths.sspi_auto_create_users_helper=Permitir que o método de autenticação SSPI crie, automaticamente, novas contas para utilizadores que iniciam a sessão pela primeira vez
@ -2970,6 +3029,7 @@ monitor.queue.pool.cancel_desc=Deixar uma fila sem quaisquer grupos de trabalhad
notices.system_notice_list=Notificações do sistema
notices.view_detail_header=Ver os detalhes da notificação
notices.operations=Operações
notices.select_all=Marcar todas
notices.deselect_all=Desmarcar todas
notices.inverse_selection=Inverter as marcações
@ -3094,6 +3154,8 @@ keywords=Palavras-chave
details=Detalhes
details.author=Autor(a)
details.project_site=Página web do projecto
details.repository_site=Página web do repositório
details.documentation_site=Página web da documentação
details.license=Licença
assets=Recursos
versions=Versões
@ -3101,7 +3163,14 @@ versions.on=ligado
versions.view_all=Ver todas
dependency.id=ID
dependency.version=Versão
cargo.registry=Configurar este registo no ficheiro de configuração do Cargo (por exemplo: <code>~/.cargo/config.toml</code>):
cargo.install=Para instalar o pacote usando o Cargo, execute o seguinte comando:
cargo.documentation=Para obter mais informações sobre o registo do Cargo, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/cargo/">a documentação</a>.
cargo.details.repository_site=Página web do repositório
cargo.details.documentation_site=Página web da documentação
chef.registry=Configure este registo no seu ficheiro <code>~/.chef/config.rb</code>:
chef.install=Para instalar o pacote, execute o seguinte comando:
chef.documentation=Para obter mais informações sobre o registo do Chef, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/chef/">a documentação</a>.
composer.registry=Configure este registo no seu ficheiro <code>~/.composer/config.json</code>:
composer.install=Para instalar o pacote usando o Composer, execute o seguinte comando:
composer.documentation=Para obter mais informações sobre o registo do Composer, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/composer/">a documentação</a>.
@ -3111,6 +3180,11 @@ conan.details.repository=Repositório
conan.registry=Configurar este registo usando a linha de comandos:
conan.install=Para instalar o pacote usando o Conan, execute o seguinte comando:
conan.documentation=Para obter mais informações sobre o registo do Conan, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/conan/">a documentação</a>.
conda.registry=Configure este registo como um repositório Conda no seu ficheiro <code>.condarc</code>:
conda.install=Para instalar o pacote usando o Conda, execute o seguinte comando:
conda.documentation=Para obter mais informações sobre o registo do Conda, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/conda/">a documentação</a>.
conda.details.repository_site=Página web do repositório
conda.details.documentation_site=Página web da documentação
container.details.type=Tipo de imagem
container.details.platform=Plataforma
container.pull=Puxar a imagem usando a linha de comandos:
@ -3169,26 +3243,110 @@ settings.delete.description=Eliminar o pacote é permanente e não pode ser desf
settings.delete.notice=Está prestes a eliminar %s (%s). Esta operação é irreversível. Tem a certeza?
settings.delete.success=O pacote foi eliminado.
settings.delete.error=Falhou a eliminação do pacote.
owner.settings.cargo.title=Índice do registo do Cargo
owner.settings.cargo.initialize=Inicializar índice
owner.settings.cargo.initialize.description=Para usar o registo Cargo, é necessário um repositório git de índice especial. Aqui pode (re)criá-lo com a configuração necessária.
owner.settings.cargo.initialize.error=Falhou ao inicializar o índice do Cargo: %v
owner.settings.cargo.initialize.success=O índice do Cargo foi criado com sucesso.
owner.settings.cargo.rebuild=Reconstruir índice
owner.settings.cargo.rebuild.description=Se o índice não estiver sincronizado com os pacotes Cargo armazenados, pode reconstruí-lo aqui.
owner.settings.cargo.rebuild.error=Falhou ao reconstruir o índice do Cargo: %v
owner.settings.cargo.rebuild.success=O índice do Cargo foi reconstruído com sucesso.
owner.settings.cleanuprules.title=Gerir regras de limpeza
owner.settings.cleanuprules.add=Adicionar regra de limpeza
owner.settings.cleanuprules.edit=Editar regra de limpeza
owner.settings.cleanuprules.none=Não há regras de limpeza disponíveis. Leia a documentação para obter mais informação.
owner.settings.cleanuprules.preview=Previsão da regra de limpeza
owner.settings.cleanuprules.preview.overview=%d pacotes estão agendados para serem removidos.
owner.settings.cleanuprules.preview.none=A regra de limpeza não corresponde a nenhum pacote.
owner.settings.cleanuprules.enabled=Habilitado
owner.settings.cleanuprules.pattern_full_match=Aplicar o padrão ao nome completo do pacote
owner.settings.cleanuprules.keep.title=As versões que correspondem a estas regras serão mantidas, mesmo que correspondam à regra de remoção abaixo.
owner.settings.cleanuprules.keep.count=Manter a mais recente
owner.settings.cleanuprules.keep.count.1=1 versão por pacote
owner.settings.cleanuprules.keep.count.n=%d versões por pacote
owner.settings.cleanuprules.keep.pattern=Manter as versões correspondentes
owner.settings.cleanuprules.keep.pattern.container=A <code>última</code> versão será sempre mantida para pacotes de contentor.
owner.settings.cleanuprules.remove.title=Versões que correspondam a estas regras serão removidos, a não ser que a regra acima diga para os manter.
owner.settings.cleanuprules.remove.days=Remover versões mais antigas do que
owner.settings.cleanuprules.remove.pattern=Remover as versões correspondentes
owner.settings.cleanuprules.success.update=A regra de limpeza foi modificada.
owner.settings.cleanuprules.success.delete=A regra de limpeza foi eliminada.
owner.settings.chef.title=Registo do Chef
owner.settings.chef.keypair=Gerar par de chaves
owner.settings.chef.keypair.description=Gerar um par de chaves para se autenticar no registo do Chef. A chave anterior deixará de poder ser utilizada.
[secrets]
secrets=Segredos
description=Os segredos serão transmitidos a certas operações e não poderão ser lidos de outra forma.
none=Ainda não há segredos.
value=Valor
name=Nome
creation=Adicionar segredo
creation.name_placeholder=apenas caracteres sem distinção de maiúsculas, alfanuméricos ou sublinhados, não podem começar com GITEA_ nem com GITHUB_
creation.value_placeholder=Insira um conteúdo qualquer. Espaços em branco no início ou no fim serão omitidos.
creation.success=O segredo '%s' foi adicionado.
creation.failed=Falhou ao adicionar o segredo.
deletion=Remover segredo
deletion.description=Remover um segredo é permanente e não pode ser revertido. Continuar?
deletion.success=O segredo foi removido.
deletion.failed=Falhou ao remover o segredo.
[actions]
actions=Operações
unit.desc=Gerir operações
status.unknown=Desconhecido
status.waiting=Aguardando
status.running=Em execução
status.success=Sucesso
status.failure=Falha
status.cancelled=Cancelada
status.skipped=Ignorada
status.blocked=Bloqueada
runners=Executores
runners.runner_manage_panel=Gestão de executores
runners.new=Criar um novo executor
runners.new_notice=Como iniciar um executor
runners.status=Estado
runners.id=ID
runners.name=Nome
runners.owner_type=Tipo
runners.description=Descrição
runners.labels=Rótulos
runners.last_online=Última vez ligado
runners.agent_labels=Rótulos do agente
runners.custom_labels=Rótulos personalizados
runners.custom_labels_helper=Rótulos personalizados são rótulos que são adicionados manualmente por um administrador. São separados por vírgulas e espaços em branco antes e após cada rótulo são ignorados.
runners.runner_title=Executor
runners.task_list=Tarefas recentes deste executor
runners.task_list.run=Executar
runners.task_list.status=Estado
runners.task_list.repository=Repositório
runners.task_list.commit=Cometimento
runners.task_list.done_at=Feito em
runners.edit_runner=Editar executor
runners.update_runner=Guardar alterações
runners.update_runner_success=O executor foi modificado com sucesso
runners.update_runner_failed=Falhou ao modificar o executor
runners.delete_runner=Eliminar o executor
runners.delete_runner_success=O executor foi eliminado com sucesso
runners.delete_runner_failed=Falhou ao eliminar o executor
runners.delete_runner_header=Confirme que quer eliminar este executor
runners.delete_runner_notice=Se uma tarefa estiver a correr sob este executor, será terminada e marcada como tendo falhado. Pode quebrar o fluxo de trabalho de construção.
runners.none=Não há executores disponíveis
runners.status.unspecified=Desconhecido
runners.status.idle=Parada
runners.status.active=Em funcionamento
runners.status.offline=Desconectada
runs.all_workflows=Todos os fluxos de trabalho
runs.open_tab=%d abertas
runs.closed_tab=%d fechadas
runs.commit=Cometimento
runs.pushed_by=Enviada por
need_approval_desc=É necessária aprovação para executar fluxos de trabalho para a derivação do pedido de integração.

View File

@ -57,6 +57,7 @@ new_mirror=创建新的镜像
new_fork=新的仓库Fork
new_org=创建组织
new_project=创建项目
new_project_column=创建列
manage_org=管理我的组织
admin_panel=管理后台
account_settings=帐户设置
@ -90,9 +91,11 @@ disabled=禁用
copy=复制
copy_url=复制网址
copy_content=复制内容
copy_branch=复制分支名
copy_success=复制成功!
copy_error=复制失败
copy_type_unsupported=无法复制此类型的文件内容
write=撰写
preview=预览
@ -109,6 +112,10 @@ never=从不
rss_feed=RSS 订阅源
[aria]
navbar=导航栏
footer=页脚
footer.software=关于软件
footer.links=链接
[filter]
string.asc=A - Z
@ -258,24 +265,24 @@ view_home=访问 %s
search_repos=查找仓库…
filter=其他过滤器
filter_by_team_repositories=按团队仓库筛选
feed_of="%s\" 的源
feed_of=`"%s"的源`
show_archived=
show_both_archived_unarchived=显示已存档和未存档的
show_archived=
show_both_archived_unarchived=显示已归档和未归档的
show_only_archived=只显示已归档的
show_only_unarchived=只显示未档的
show_only_unarchived=只显示未档的
show_private=私有
show_both_private_public=显示公开的和私有的
show_private=私有
show_both_private_public=同时显示公开的和私有的
show_only_private=只显示私有的
show_only_public=只显示公开的
issues.in_your_repos=属于该用户仓库的
issues.in_your_repos=在您的仓库中
[explore]
repos=仓库
repos=仓库管理
users=用户
organizations=组织
organizations=组织管理
search=搜索
code=代码
search.type.tooltip=搜索类型
@ -283,24 +290,15 @@ search.fuzzy=模糊
search.fuzzy.tooltip=包含近似匹配搜索词的结果
search.match=匹配
search.match.tooltip=仅包含精确匹配搜索词的结果
code_search_unavailable=前代码搜索不可用。请与网站管理员联系。
code_search_unavailable=前代码搜索不可用。请与网站管理员联系。
repo_no_results=未找到匹配的仓库。
user_no_results=未找到匹配的用户。
org_no_results=未找到匹配的组织。
code_no_results=未找到与搜索字词匹配的源代码。
code_search_results=“%s” 的搜索结果
code_search_results=“%s” 的搜索结果
code_last_indexed_at=最后索引于 %s
relevant_repositories_tooltip=派生的仓库,以及缺少主题、图标和描述的仓库将被隐藏。
relevant_repositories=只显示相关的仓库, <a href="
show_private=私有库
[explore]
repos=仓库管理
organizations=组织管理
code=代码
relevant_repositories=只显示相关的仓库, <a href="%s">显示未过滤结果</a>。
[auth]
@ -330,6 +328,7 @@ email_not_associate=您输入的邮箱地址未被关联到任何帐号!
send_reset_mail=发送账户恢复邮件
reset_password=账户恢复
invalid_code=此确认密钥无效或已过期。
invalid_password=您的密码与用于创建账户的密码不匹配。
reset_password_helper=恢复账户
reset_password_wrong_user=您已作为 %s 登录,无法使用链接恢复 %s 的账户。
password_too_short=密码长度不能少于 %d 位。
@ -373,6 +372,7 @@ password_pwned_err=无法完成对 HaveIBeenPwned 的请求
[mail]
view_it_on=在 %s 上查看
reply=或直接回复此邮件
link_not_working_do_paste=不起作用?尝试复制并粘贴到您的浏览器。
hi_user_x=<b>%s</b> 您好,
@ -476,6 +476,8 @@ url_error=`'%s' 不是一个有效的 URL。`
include_error=`必须包含子字符串 '%s'。`
glob_pattern_error=`匹配模式无效:%s.`
regex_pattern_error=`正则表达式无效:%s.`
username_error=` 只能包含字母数字字符('0-9','a-z','A-Z'), 破折号 ('-'), 下划线 ('_') 和点 ('.'). 不能以非字母数字字符开头或结尾,并且不允许连续的非字母数字字符。`
invalid_group_team_map_error=`映射无效: %s`
unknown_error=未知错误:
captcha_incorrect=验证码不正确。
password_not_match=密码不匹配。
@ -512,10 +514,12 @@ team_not_exist=团队不存在
last_org_owner=您不能从 "所有者" 团队中删除最后一个用户。组织中必须至少有一个所有者。
cannot_add_org_to_team=组织不能被加入到团队中。
duplicate_invite_to_team=此用户已被邀请为团队成员。
organization_leave_success=您已成功离开组织 %s。
invalid_ssh_key=无法验证您的 SSH 密钥: %s
invalid_gpg_key=无法验证您的 GPG 密钥: %s
invalid_ssh_principal=无效的规则: %s
must_use_public_key=您提供的密钥是私钥。不要在任何地方上传您的私钥,请改用您的公钥。
unable_verify_ssh_key=无法验证SSH密钥再次检查是否有误。
auth_failed=授权验证失败:%v
@ -752,6 +756,8 @@ access_token_deletion_cancel_action=取消
access_token_deletion_confirm_action=刪除
access_token_deletion_desc=删除令牌将撤销程序对您账户的访问权限。此操作无法撤消。是否继续?
delete_token_success=令牌已经被删除。使用该令牌的应用将不再能够访问你的账号。
select_scopes=选择范围
scopes_list=范围:
manage_oauth2_applications=管理 OAuth2 应用程序
edit_oauth2_application=编辑 OAuth2 应用程序
@ -926,9 +932,9 @@ delete_preexisting_success=删除 %s 中未收录的文件
blame_prior=查看此更改前的 blame
transfer.accept=接受转移
transfer.accept_desc=转移到 "%s"
transfer.accept_desc=`转移到 "%s"`
transfer.reject=拒绝转移
transfer.reject_desc=取消转移到 "%s"
transfer.reject_desc=`取消转移到 "%s"`
transfer.no_permission_to_accept=您没有接受的权限
transfer.no_permission_to_reject=您没有拒绝的权限
@ -1024,10 +1030,12 @@ unstar=取消点赞
star=点赞
fork=派生
download_archive=下载此仓库
more_operations=更多操作
no_desc=暂无描述
quick_guide=快速帮助
clone_this_repo=克隆当前仓库
cite_this_repo=引用此仓库
create_new_repo_command=从命令行创建一个新的仓库
push_exist_repo=从命令行推送已经创建的仓库
empty_message=这个家伙很懒,什么都没有推送。
@ -1126,6 +1134,7 @@ editor.commit_directly_to_this_branch=直接提交至 <strong class="branch-name
editor.create_new_branch=为此提交创建一个 <strong>新的分支</strong> 并发起合并请求。
editor.create_new_branch_np=为此提交创建 <strong>新分支</strong>。
editor.propose_file_change=提议文件更改
editor.new_branch_name=为这次提交的新分支命名
editor.new_branch_name_desc=新的分支名称...
editor.cancel=取消
editor.filename_cannot_be_empty=文件名不能为空。
@ -1177,6 +1186,7 @@ commits.signed_by_untrusted_user_unmatched=由与提交者不匹配的未授信
commits.gpg_key_id=GPG 密钥 ID
commits.ssh_key_fingerprint=SSH 密钥指纹
commit.operations=操作
commit.revert=还原
commit.revert-header=还原: %s
commit.revert-content=选择要还原的分支:
@ -1209,11 +1219,22 @@ projects.type.bug_triage=Bug分类看板
projects.template.desc=项目模板
projects.template.desc_helper=选择一个项目模板以开始
projects.type.uncategorized=未分类
projects.column.edit=编辑列
projects.column.edit_title=名称
projects.column.new_title=名称
projects.column.new_submit=创建列
projects.column.new=创建列
projects.column.set_default=设为默认
projects.column.set_default_desc=Set this column as default for uncategorized issues and pulls
projects.column.delete=删除列
projects.column.deletion_desc=删除项目列会将所有相关问题移到“未分类”。是否继续?
projects.column.color=彩色
projects.open=开启
projects.close=关闭
projects.column.assigned_to=指派给
projects.card_type.desc=卡片预览
projects.card_type.images_and_text=图标和文字
projects.card_type.text_only=仅文本
issues.desc=组织 bug 报告、任务和里程碑。
issues.filter_assignees=筛选指派人
@ -1290,6 +1311,7 @@ issues.filter_label_no_select=所有标签
issues.filter_milestone=里程碑筛选
issues.filter_milestone_no_select=所有里程碑
issues.filter_project=项目
issues.filter_project_all=所有项目
issues.filter_project_none=暂无项目
issues.filter_assignee=指派人筛选
issues.filter_assginee_no_select=所有指派成员
@ -1301,6 +1323,7 @@ issues.filter_type.assigned_to_you=指派给您的
issues.filter_type.created_by_you=由您创建的
issues.filter_type.mentioning_you=提及您的
issues.filter_type.review_requested=已请求评审
issues.filter_type.reviewed_by_you=您评审过的
issues.filter_sort=排序
issues.filter_sort.latest=最新创建
issues.filter_sort.oldest=最早创建
@ -1322,6 +1345,8 @@ issues.action_milestone=里程碑
issues.action_milestone_no_select=无里程碑
issues.action_assignee=指派人筛选
issues.action_assignee_no_select=未指派
issues.action_check=选中/取消选中
issues.action_check_all=选中/取消选中所有项目
issues.opened_by=由 <a href="%[2]s">%[3]s</a> 于 %[1]s创建
pulls.merged_by=由 <a href="%[2]s">%[3]s</a> 创建,被合并于 %[1]s
pulls.merged_by_fake=由 %[2]s 创建,被合并于 %[1]s
@ -1375,6 +1400,9 @@ issues.save=保存
issues.label_title=标签名称
issues.label_description=标签描述
issues.label_color=标签颜色
issues.label_exclusive=独有
issues.label_exclusive_desc=命名标签为 <code>scope/item</code> 以使其与其他以 <code>scope/</code> 开头的标签互斥。
issues.label_exclusive_warning=在编辑工单或合并请求的标签时,任何冲突的范围标签都将被删除。
issues.label_count=%d 个标签
issues.label_open_issues=%d 个开启的工单
issues.label_edit=编辑
@ -1442,6 +1470,7 @@ issues.error_removing_due_date=删除到期时间失败。
issues.push_commit_1=于 %[2]s 推送了 %[1]d 个提交
issues.push_commits_n=于 %[2]s 推送了 %[1]d 个提交
issues.force_push_codes=`于 %[6]s 强制推送 %[1]s从 <a class="ui sha" href="%[3]s"><code>%[2]s</code></a>,至 <a class="ui sha" href="%[5]s"><code>%[4]s</code></a>`
issues.force_push_compare=比较
issues.due_date_form=yyyy年mm月dd日
issues.due_date_form_add=设置到期时间
issues.due_date_form_edit=编辑
@ -1628,6 +1657,8 @@ pulls.reopened_at=`重新打开此合并请求 <a id="%[1]s" href="#%[1]s">%[2]s
pulls.merge_instruction_hint=`你也可以查看 <a class="show-instruction">命令行指令</a>`
pulls.merge_instruction_step1_desc=从你的仓库中签出一个新的分支并测试变更。
pulls.merge_instruction_step2_desc=合并变更并更新到 Gitea 上
pulls.clear_merge_message=清除合并信息
pulls.clear_merge_message_hint=Clearing the merge message will only remove the commit message content and keep generated git trailers such as "Co-Authored-By …".
pulls.auto_merge_button_when_succeed=(当检查成功时)
pulls.auto_merge_when_succeed=在所有检查成功后自动合并
@ -1819,6 +1850,7 @@ settings.mirror_sync_in_progress=镜像同步正在进行中,请稍后再试
settings.site=网站
settings.update_settings=更新仓库设置
settings.branches.update_default_branch=更新默认分支
settings.branches.add_new_rule=添加新规则
settings.advanced_settings=高级设置
settings.wiki_desc=启用仓库百科
settings.use_internal_wiki=使用内置百科
@ -1848,8 +1880,11 @@ settings.pulls.ignore_whitespace=忽略空白冲突
settings.pulls.enable_autodetect_manual_merge=启用自动检测手动合并 (注意:在某些特殊情况下可能发生错误判断)
settings.pulls.allow_rebase_update=允许通过变基更新拉取请求分支
settings.pulls.default_delete_branch_after_merge=默认合并后删除合并请求分支
settings.pulls.default_allow_edits_from_maintainers=默认开启允许维护者编辑
settings.releases_desc=启用发布
settings.packages_desc=启用仓库软件包注册中心
settings.projects_desc=启用仓库项目
settings.actions_desc=启用 Actions
settings.admin_settings=管理员设置
settings.admin_enable_health_check=启用仓库健康检查 (git fsck)
settings.admin_code_indexer=代码索引器
@ -2059,6 +2094,8 @@ settings.deploy_key_deletion_desc=删除部署密钥将取消此密钥对此仓
settings.deploy_key_deletion_success=部署密钥已删除。
settings.branches=分支
settings.protected_branch=分支保护
settings.protected_branch.save_rule=保存规则
settings.protected_branch.delete_rule=删除规则
settings.protected_branch_can_push=允许推吗?
settings.protected_branch_can_push_yes=你可以推
settings.protected_branch_can_push_no=你不能推
@ -2093,6 +2130,7 @@ settings.dismiss_stale_approvals=取消过时的批准
settings.dismiss_stale_approvals_desc=当新的提交更改合并请求内容被推送到分支时,旧的批准将被撤销。
settings.require_signed_commits=需要签名提交
settings.require_signed_commits_desc=拒绝推送未签名或无法验证的提交到分支
settings.protect_branch_name_pattern=受保护的分支名称模式
settings.protect_protected_file_patterns=受保护的文件模式(使用分号分隔)
settings.protect_protected_file_patterns_desc=即使用户有权在此分支中添加、编辑或删除文件,也不允许直接更改受保护文件。 可以使用分号分隔多个模式 ('\;')。语法文档见 <a href="https://pkg.go.dev/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a>。示例:<code>.drone.yml</code><code>/docs/**/*.txt</code>。
settings.protect_unprotected_file_patterns=不受保护的文件模式 (使用分号 '\;' 分隔)
@ -2101,6 +2139,7 @@ settings.add_protected_branch=启用保护
settings.delete_protected_branch=禁用保护
settings.update_protect_branch_success=分支 "%s" 的分支保护已更新。
settings.remove_protected_branch_success=分支 "%s" 的分支保护已被禁用。
settings.remove_protected_branch_failed=删除分支保护规则 '%s' 失败。
settings.protected_branch_deletion=禁用分支保护
settings.protected_branch_deletion_desc=禁用分支保护允许具有写入权限的用户推送提交到此分支。继续?
settings.block_rejected_reviews=拒绝审核阻止了此合并
@ -2110,10 +2149,13 @@ settings.block_on_official_review_requests_desc=处于评审状态时,即使
settings.block_outdated_branch=如果拉取请求已经过时,阻止合并
settings.block_outdated_branch_desc=当头部分支落后基础分支时,不能合并。
settings.default_branch_desc=请选择一个默认的分支用于合并请求和提交:
settings.merge_style_desc=合并方式
settings.default_merge_style_desc=合并请求的默认合并样式:
settings.choose_branch=选择一个分支...
settings.no_protected_branch=没有受保护的分支
settings.edit_protected_branch=编辑
settings.protected_branch_required_rule_name=必须填写规则名称
settings.protected_branch_duplicate_rule_name=规则名称已存在
settings.protected_branch_required_approvals_min=所需的审批数不能为负数。
settings.tags=标签
settings.tags.protection=Git标签保护
@ -2269,6 +2311,8 @@ release.downloads=下载附件
release.download_count=下载:%s
release.add_tag_msg=使用发布的标题和内容作为标签消息。
release.add_tag=仅创建标签
release.releases_for=%s 的版本发布
release.tags_for=%s 的标签
branch.name=分支名称
branch.search=搜索分支
@ -2536,6 +2580,10 @@ dashboard.delete_old_actions=从数据库中删除所有旧操作记录
dashboard.delete_old_actions.started=已开始从数据库中删除所有旧操作记录。
dashboard.update_checker=更新检查器
dashboard.delete_old_system_notices=从数据库中删除所有旧系统通知
dashboard.gc_lfs=垃圾回收 LFS 元数据
dashboard.stop_zombie_tasks=停止僵尸任务
dashboard.stop_endless_tasks=停止永不停止的任务
dashboard.cancel_abandoned_jobs=取消丢弃的任务
users.user_manage_panel=用户帐户管理
users.new_account=创建新帐户
@ -2624,6 +2672,7 @@ repos.size=大小
packages.package_manage_panel=软件包管理
packages.total_size=总大小:%s
packages.unreferenced_size=未引用大小: %s
packages.owner=所有者
packages.creator=创建者
packages.name=名称
@ -2717,6 +2766,8 @@ auths.oauth2_required_claim_value_helper=设置此值,只有拥有对应的声
auths.oauth2_group_claim_name=用于提供用户组名称的 Claim 声明名称。(可选)
auths.oauth2_admin_group=管理员用户组的 Claim 声明值。(可选 - 需要上面的声明名称)
auths.oauth2_restricted_group=受限用户组的 Claim 声明值。(可选 - 需要上面的声明名称)
auths.oauth2_map_group_to_team=Map claimed groups to Organization teams. (Optional - requires claim name above)
auths.oauth2_map_group_to_team_removal=如果用户不属于相应的组,从已同步团队中移除用户
auths.enable_auto_register=允许用户自动注册
auths.sspi_auto_create_users=自动创建用户
auths.sspi_auto_create_users_helper=允许 SSPI 认证在用户第一次登录时自动创建新账号
@ -2732,10 +2783,10 @@ auths.tips=帮助提示
auths.tips.oauth2.general=OAuth2 认证
auths.tips.oauth2.general.tip=当注册一个新的 OAuth2 认证,回调/重定向 URL 应该是: <host>/user/oauth2/<Authentication Name>/callback
auths.tip.oauth2_provider=OAuth2 提供程序
auths.tip.bitbucket=在 https://bitbucket.org/account/user/<your username>/oauth-consumers/new 注册新的 OAuth 消费者同时添加权限"帐户"-"读"
auths.tip.bitbucket=`在 https://bitbucket.org/account/user/<your username>/oauth-consumers/new 注册新的 OAuth 消费者同时添加权限"帐户"-"读"`
auths.tip.nextcloud=使用下面的菜单“设置Settings -> 安全Security -> OAuth 2.0 client”在您的实例上注册一个新的 OAuth 客户端。
auths.tip.dropbox=在 https://www.dropbox.com/developers/apps 上创建一个新的应用程序
auths.tip.facebook=在 https://developers.facebook.com/apps 注册一个新的应用,并添加产品"Facebook 登录"
auths.tip.facebook=`在 https://developers.facebook.com/apps 注册一个新的应用,并添加产品"Facebook 登录"`
auths.tip.github=在 https://github.com/settings/applications/new 注册一个 OAuth 应用程序
auths.tip.gitlab=在 https://gitlab.com/profile/applications 上注册新应用程序
auths.tip.google_plus=从谷歌 API 控制台 (https://console.developers.google.com/) 获得 OAuth2 客户端凭据
@ -2979,6 +3030,7 @@ monitor.queue.pool.cancel_desc=没有工作者组的队列将会引起请求永
notices.system_notice_list=系统提示管理
notices.view_detail_header=查看提示详情
notices.operations=操作
notices.select_all=选中全部
notices.deselect_all=取消全选
notices.inverse_selection=反向选中
@ -3112,9 +3164,14 @@ versions.on=于
versions.view_all=查看全部
dependency.id=ID
dependency.version=版本
cargo.registry=在 Cargo 配置文件中设置此注册中心(例如:<code>~/.cargo/config.toml</code>)
cargo.install=要使用 Cargo 安装软件包,请运行以下命令:
cargo.documentation=关于 Cargo 注册中心的更多信息,请参阅 <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/cargo/">文档</a>。
cargo.details.repository_site=仓库站点
cargo.details.documentation_site=文档站点
chef.registry=在您的 <code>~/.chef/config.rb</code> 文件中设置此注册中心:
chef.install=要安装包,请运行以下命令:
chef.documentation=关于 Chef 注册中心的更多信息,请参阅 <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/chef/">文档</a>。
composer.registry=在您的 <code>~/.composer/config.json</code> 文件中设置此注册中心:
composer.install=要使用 Composer 安装软件包,请运行以下命令:
composer.documentation=关于 Composer 注册中心的更多信息,请参阅 <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/composer/"> 文档 </a>。
@ -3124,6 +3181,9 @@ conan.details.repository=仓库
conan.registry=从命令行设置此注册中心:
conan.install=要使用 Conan 安装软件包,请运行以下命令:
conan.documentation=关于 Conan 注册中心的更多信息,请参阅 <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/conan/">文档</a>。
conda.registry=在您的 <code>.condarc</code> 文件中将此注册中心设置为 Conda 仓库:
conda.install=要使用 Conda 安装软件包,请运行以下命令:
conda.documentation=关于 Conda 注册中心的更多信息,请参阅 <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/conda/">文档</a>。
conda.details.repository_site=仓库站点
conda.details.documentation_site=文档站点
container.details.type=镜像类型
@ -3184,26 +3244,110 @@ settings.delete.description=删除软件包是永久性的,无法撤消。
settings.delete.notice=您将要删除 %s (%s)。此操作是不可逆的,您确定吗?
settings.delete.success=软件包已被删除。
settings.delete.error=删除软件包失败。
owner.settings.cargo.title=Cargo 注册中心索引
owner.settings.cargo.initialize=初始化索引
owner.settings.cargo.initialize.description=To use the Cargo registry a special index git repository is needed. Here you can (re)create it with the required config.
owner.settings.cargo.initialize.error=初始化Cargo索引失败 %v
owner.settings.cargo.initialize.success=Cargo索引已经成功创建。
owner.settings.cargo.rebuild=重建索引
owner.settings.cargo.rebuild.description=If the index is out of sync with the cargo packages stored you can rebuild it here.
owner.settings.cargo.rebuild.error=无法重建 Cargo 索引: %v
owner.settings.cargo.rebuild.success=Cargo 索引已成功重建。
owner.settings.cleanuprules.title=管理清理规则
owner.settings.cleanuprules.add=添加清理规则
owner.settings.cleanuprules.edit=编辑清理规则
owner.settings.cleanuprules.none=没有可用的清理规则。请阅读文档了解更多信息。
owner.settings.cleanuprules.preview=清理规则预览
owner.settings.cleanuprules.preview.overview=%d 个软件包计划被删除。
owner.settings.cleanuprules.preview.none=清理规则与任何软件包都不匹配。
owner.settings.cleanuprules.enabled=启用
owner.settings.cleanuprules.pattern_full_match=Apply pattern to full package name
owner.settings.cleanuprules.keep.title=Versions that match these rules are kept, even if they match a removal rule below.
owner.settings.cleanuprules.keep.count=保留最新的
owner.settings.cleanuprules.keep.count.1=每个软件包1个版本
owner.settings.cleanuprules.keep.count.n=每个软件包 %d 个版本
owner.settings.cleanuprules.keep.pattern=保持版本匹配
owner.settings.cleanuprules.keep.pattern.container=The <code>latest</code> version is always kept for Container packages.
owner.settings.cleanuprules.remove.title=与这些规则相匹配的版本将被删除,除非其中存在某个保留它们的规则。
owner.settings.cleanuprules.remove.days=移除旧于天数的版本
owner.settings.cleanuprules.remove.pattern=删除匹配的版本
owner.settings.cleanuprules.success.update=清理规则已更新。
owner.settings.cleanuprules.success.delete=清理规则已删除。
owner.settings.chef.title=Chef 注册中心
owner.settings.chef.keypair=生成密钥对
owner.settings.chef.keypair.description=生成用于验证Chef 注册中心的密钥对。之前的密钥不能在以后使用。
[secrets]
secrets=密钥
description=Secrets will be passed to certain actions and cannot be read otherwise.
none=还没有密钥。
value=
name=名称
creation=添加密钥
creation.name_placeholder=不区分大小写字母数字或下划线不能以GITEA_ 或 GITHUB_ 开头。
creation.value_placeholder=输入任何内容,开头和结尾的空白都会被省略
creation.success=您的密钥 '%s' 添加成功。
creation.failed=添加密钥失败。
deletion=删除密钥
deletion.description=删除密钥是永久性的,无法撤消。继续吗?
deletion.success=此Secret已被删除。
deletion.failed=删除密钥失败。
[actions]
actions=Actions
unit.desc=管理Actions
status.unknown=未知
status.waiting=等待中
status.running=正在运行
status.success=成功
status.failure=失败
status.cancelled=已取消
status.skipped=已忽略
status.blocked=阻塞中
runners=Runners
runners.runner_manage_panel=Runners管理
runners.new=创建 Runner
runners.new_notice=如何启动一个运行器
runners.status=状态
runners.id=ID
runners.name=名称
runners.owner_type=类型
runners.description=组织描述
runners.labels=标签
runners.last_online=上次在线时间
runners.agent_labels=代理标签
runners.custom_labels=自定义标签
runners.custom_labels_helper=自定义标签是由管理员手动添加的标签。标签之间用逗号分隔,每个标签的开头和结尾的空白被忽略。
runners.runner_title=Runner
runners.task_list=最近在此runner上的任务
runners.task_list.run=执行
runners.task_list.status=状态
runners.task_list.repository=仓库
runners.task_list.commit=提交
runners.task_list.done_at=完成于
runners.edit_runner=编辑运行器
runners.update_runner=更新更改
runners.update_runner_success=运行器更新成功
runners.update_runner_failed=更新运行器失败
runners.delete_runner=删除运行器
runners.delete_runner_success=运行器删除成功
runners.delete_runner_failed=删除运行器失败
runners.delete_runner_header=确认要删除此运行器
runners.delete_runner_notice=如果一个任务正在运行在此运行器上,它将被终止并标记为失败。它可能会打断正在构建的工作流。
runners.none=无可用的 Runner
runners.status.unspecified=未知
runners.status.idle=空闲
runners.status.active=激活
runners.status.offline=离线
runs.all_workflows=所有工作流
runs.open_tab=%d 开启中
runs.closed_tab=%d 已关闭
runs.commit=提交
runs.pushed_by=推送者
need_approval_desc=该工作流由派生仓库的合并请求所触发,需要批准方可运行。

256
package-lock.json generated
View File

@ -31,7 +31,7 @@
"less": "4.1.3",
"less-loader": "11.1.0",
"license-checker-webpack-plugin": "0.2.1",
"mermaid": "9.3.0",
"mermaid": "10.0.2",
"mini-css-extract-plugin": "2.7.2",
"monaco-editor": "0.34.1",
"monaco-editor-webpack-plugin": "7.0.1",
@ -2680,6 +2680,14 @@
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"dev": true
},
"node_modules/cose-base": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz",
"integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==",
"dependencies": {
"layout-base": "^1.0.0"
}
},
"node_modules/cosmiconfig": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz",
@ -2888,6 +2896,53 @@
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz",
"integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw=="
},
"node_modules/cytoscape": {
"version": "3.23.0",
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.23.0.tgz",
"integrity": "sha512-gRZqJj/1kiAVPkrVFvz/GccxsXhF3Qwpptl32gKKypO4IlqnKBjTOu+HbXtEggSGzC5KCaHp3/F7GgENrtsFkA==",
"dependencies": {
"heap": "^0.2.6",
"lodash": "^4.17.21"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/cytoscape-cose-bilkent": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz",
"integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==",
"dependencies": {
"cose-base": "^1.0.0"
},
"peerDependencies": {
"cytoscape": "^3.2.0"
}
},
"node_modules/cytoscape-fcose": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz",
"integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==",
"dependencies": {
"cose-base": "^2.2.0"
},
"peerDependencies": {
"cytoscape": "^3.2.0"
}
},
"node_modules/cytoscape-fcose/node_modules/cose-base": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz",
"integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==",
"dependencies": {
"layout-base": "^2.0.0"
}
},
"node_modules/cytoscape-fcose/node_modules/layout-base": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz",
"integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg=="
},
"node_modules/d3": {
"version": "7.8.2",
"resolved": "https://registry.npmjs.org/d3/-/d3-7.8.2.tgz",
@ -3267,11 +3322,11 @@
}
},
"node_modules/dagre-d3-es": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.6.tgz",
"integrity": "sha512-CaaE/nZh205ix+Up4xsnlGmpog5GGm81Upi2+/SBHxwNwrccBb3K51LzjZ1U6hgvOlAEUsVWf1xSTzCyKpJ6+Q==",
"version": "7.0.9",
"resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.9.tgz",
"integrity": "sha512-rYR4QfVmy+sR44IBDvVtcAmOReGBvRCWDpO2QjYwqgh9yijw6eSHBqaPG/LIOEy7aBsniLvtMW6pg19qJhq60w==",
"dependencies": {
"d3": "^7.7.0",
"d3": "^7.8.2",
"lodash-es": "^4.17.21"
}
},
@ -3298,6 +3353,11 @@
"node": ">=12"
}
},
"node_modules/dayjs": {
"version": "1.11.7",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
"integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ=="
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@ -3632,9 +3692,9 @@
}
},
"node_modules/dompurify": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.1.tgz",
"integrity": "sha512-ewwFzHzrrneRjxzmK6oVz/rZn9VWspGFRDb4/rRtIsM1n36t9AKma/ye8syCpcw+XJ25kOK/hOG7t1j2I2yBqA=="
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.3.tgz",
"integrity": "sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ=="
},
"node_modules/domutils": {
"version": "3.0.1",
@ -3681,6 +3741,11 @@
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz",
"integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA=="
},
"node_modules/elkjs": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.8.2.tgz",
"integrity": "sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ=="
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@ -5043,6 +5108,11 @@
"resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz",
"integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg=="
},
"node_modules/heap": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz",
"integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg=="
},
"node_modules/hosted-git-info": {
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
@ -5877,6 +5947,11 @@
"integrity": "sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg==",
"dev": true
},
"node_modules/layout-base": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz",
"integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg=="
},
"node_modules/less": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz",
@ -6049,8 +6124,7 @@
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash-es": {
"version": "4.17.21",
@ -6393,20 +6467,26 @@
}
},
"node_modules/mermaid": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-9.3.0.tgz",
"integrity": "sha512-mGl0BM19TD/HbU/LmlaZbjBi//tojelg8P/mxD6pPZTAYaI+VawcyBdqRsoUHSc7j71PrMdJ3HBadoQNdvP5cg==",
"version": "10.0.2",
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.0.2.tgz",
"integrity": "sha512-slwoB9WdNUT+/W9VhxLYRLZ0Ey12fIE+cAZjm3FmHTD+0F1uoJETfsNbVS1POnvQZhFYzfT6/z6hJZXgecqVBA==",
"dependencies": {
"@braintree/sanitize-url": "^6.0.0",
"d3": "^7.0.0",
"dagre-d3-es": "7.0.6",
"dompurify": "2.4.1",
"cytoscape": "^3.23.0",
"cytoscape-cose-bilkent": "^4.1.0",
"cytoscape-fcose": "^2.1.0",
"d3": "^7.4.0",
"dagre-d3-es": "7.0.9",
"dayjs": "^1.11.7",
"dompurify": "2.4.3",
"elkjs": "^0.8.2",
"khroma": "^2.0.0",
"lodash-es": "^4.17.21",
"moment-mini": "^2.24.0",
"non-layered-tidy-tree-layout": "^2.0.2",
"stylis": "^4.1.2",
"uuid": "^9.0.0"
"ts-dedent": "^2.2.0",
"uuid": "^9.0.0",
"web-worker": "^1.2.0"
}
},
"node_modules/micromatch": {
@ -6531,11 +6611,6 @@
"integrity": "sha512-nPdMG0Pd09HuSsr7QOKUXO2Jr9eqaDiZvDwdyIhNG5SHYujkQHYKDfGQkulBxvbDHz8oHLsTgKN86LSwYzSHAg==",
"dev": true
},
"node_modules/moment-mini": {
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment-mini/-/moment-mini-2.29.4.tgz",
"integrity": "sha512-uhXpYwHFeiTbY9KSgPPRoo1nt8OxNVdMVoTBYHfSEKeRkIkwGpO+gERmhuhBtzfaeOyTkykSrm2+noJBgqt3Hg=="
},
"node_modules/monaco-editor": {
"version": "0.34.1",
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.1.tgz",
@ -8807,6 +8882,14 @@
"node": ">=8"
}
},
"node_modules/ts-dedent": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz",
"integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==",
"engines": {
"node": ">=6.10"
}
},
"node_modules/tsconfig-paths": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
@ -9315,6 +9398,11 @@
"node": ">=10.13.0"
}
},
"node_modules/web-worker": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz",
"integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA=="
},
"node_modules/webidl-conversions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
@ -11774,6 +11862,14 @@
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"dev": true
},
"cose-base": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz",
"integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==",
"requires": {
"layout-base": "^1.0.0"
}
},
"cosmiconfig": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz",
@ -11937,6 +12033,46 @@
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz",
"integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw=="
},
"cytoscape": {
"version": "3.23.0",
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.23.0.tgz",
"integrity": "sha512-gRZqJj/1kiAVPkrVFvz/GccxsXhF3Qwpptl32gKKypO4IlqnKBjTOu+HbXtEggSGzC5KCaHp3/F7GgENrtsFkA==",
"requires": {
"heap": "^0.2.6",
"lodash": "^4.17.21"
}
},
"cytoscape-cose-bilkent": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz",
"integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==",
"requires": {
"cose-base": "^1.0.0"
}
},
"cytoscape-fcose": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz",
"integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==",
"requires": {
"cose-base": "^2.2.0"
},
"dependencies": {
"cose-base": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz",
"integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==",
"requires": {
"layout-base": "^2.0.0"
}
},
"layout-base": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz",
"integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg=="
}
}
},
"d3": {
"version": "7.8.2",
"resolved": "https://registry.npmjs.org/d3/-/d3-7.8.2.tgz",
@ -12208,11 +12344,11 @@
}
},
"dagre-d3-es": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.6.tgz",
"integrity": "sha512-CaaE/nZh205ix+Up4xsnlGmpog5GGm81Upi2+/SBHxwNwrccBb3K51LzjZ1U6hgvOlAEUsVWf1xSTzCyKpJ6+Q==",
"version": "7.0.9",
"resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.9.tgz",
"integrity": "sha512-rYR4QfVmy+sR44IBDvVtcAmOReGBvRCWDpO2QjYwqgh9yijw6eSHBqaPG/LIOEy7aBsniLvtMW6pg19qJhq60w==",
"requires": {
"d3": "^7.7.0",
"d3": "^7.8.2",
"lodash-es": "^4.17.21"
}
},
@ -12233,6 +12369,11 @@
"whatwg-url": "^11.0.0"
}
},
"dayjs": {
"version": "1.11.7",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
"integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ=="
},
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@ -12472,9 +12613,9 @@
}
},
"dompurify": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.1.tgz",
"integrity": "sha512-ewwFzHzrrneRjxzmK6oVz/rZn9VWspGFRDb4/rRtIsM1n36t9AKma/ye8syCpcw+XJ25kOK/hOG7t1j2I2yBqA=="
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.3.tgz",
"integrity": "sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ=="
},
"domutils": {
"version": "3.0.1",
@ -12518,6 +12659,11 @@
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz",
"integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA=="
},
"elkjs": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.8.2.tgz",
"integrity": "sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ=="
},
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@ -13548,6 +13694,11 @@
"resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz",
"integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg=="
},
"heap": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz",
"integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg=="
},
"hosted-git-info": {
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
@ -14129,6 +14280,11 @@
"integrity": "sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg==",
"dev": true
},
"layout-base": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz",
"integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg=="
},
"less": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz",
@ -14251,8 +14407,7 @@
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lodash-es": {
"version": "4.17.21",
@ -14531,20 +14686,26 @@
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="
},
"mermaid": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-9.3.0.tgz",
"integrity": "sha512-mGl0BM19TD/HbU/LmlaZbjBi//tojelg8P/mxD6pPZTAYaI+VawcyBdqRsoUHSc7j71PrMdJ3HBadoQNdvP5cg==",
"version": "10.0.2",
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.0.2.tgz",
"integrity": "sha512-slwoB9WdNUT+/W9VhxLYRLZ0Ey12fIE+cAZjm3FmHTD+0F1uoJETfsNbVS1POnvQZhFYzfT6/z6hJZXgecqVBA==",
"requires": {
"@braintree/sanitize-url": "^6.0.0",
"d3": "^7.0.0",
"dagre-d3-es": "7.0.6",
"dompurify": "2.4.1",
"cytoscape": "^3.23.0",
"cytoscape-cose-bilkent": "^4.1.0",
"cytoscape-fcose": "^2.1.0",
"d3": "^7.4.0",
"dagre-d3-es": "7.0.9",
"dayjs": "^1.11.7",
"dompurify": "2.4.3",
"elkjs": "^0.8.2",
"khroma": "^2.0.0",
"lodash-es": "^4.17.21",
"moment-mini": "^2.24.0",
"non-layered-tidy-tree-layout": "^2.0.2",
"stylis": "^4.1.2",
"uuid": "^9.0.0"
"ts-dedent": "^2.2.0",
"uuid": "^9.0.0",
"web-worker": "^1.2.0"
}
},
"micromatch": {
@ -14634,11 +14795,6 @@
}
}
},
"moment-mini": {
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment-mini/-/moment-mini-2.29.4.tgz",
"integrity": "sha512-uhXpYwHFeiTbY9KSgPPRoo1nt8OxNVdMVoTBYHfSEKeRkIkwGpO+gERmhuhBtzfaeOyTkykSrm2+noJBgqt3Hg=="
},
"monaco-editor": {
"version": "0.34.1",
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.1.tgz",
@ -16340,6 +16496,11 @@
"integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==",
"dev": true
},
"ts-dedent": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz",
"integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ=="
},
"tsconfig-paths": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
@ -16674,6 +16835,11 @@
"graceful-fs": "^4.1.2"
}
},
"web-worker": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz",
"integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA=="
},
"webidl-conversions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",

View File

@ -31,7 +31,7 @@
"less": "4.1.3",
"less-loader": "11.1.0",
"license-checker-webpack-plugin": "0.2.1",
"mermaid": "9.3.0",
"mermaid": "10.0.2",
"mini-css-extract-plugin": "2.7.2",
"monaco-editor": "0.34.1",
"monaco-editor-webpack-plugin": "7.0.1",

View File

@ -52,7 +52,7 @@ func (s *Service) Register(
}
if runnerToken.IsActive {
return nil, errors.New("runner token has already activated")
return nil, errors.New("runner token has already been activated")
}
// create new runner
@ -150,7 +150,7 @@ func (s *Service) UpdateTask(
}
if err := actions_service.CreateCommitStatus(ctx, task.Job); err != nil {
log.Error("Update commit status failed: %v", err)
log.Error("Update commit status for job %v failed: %v", task.Job.ID, err)
// go on
}

View File

@ -45,7 +45,7 @@ func ListUnadoptedRepositories(ctx *context.APIContext) {
if listOptions.Page == 0 {
listOptions.Page = 1
}
repoNames, count, err := repo_service.ListUnadoptedRepositories(ctx.FormString("query"), &listOptions)
repoNames, count, err := repo_service.ListUnadoptedRepositories(ctx, ctx.FormString("query"), &listOptions)
if err != nil {
ctx.InternalServerError(err)
return
@ -109,7 +109,7 @@ func AdoptRepository(ctx *context.APIContext) {
ctx.NotFound()
return
}
if _, err := repo_service.AdoptRepository(ctx.Doer, ctxUser, repo_module.CreateRepoOptions{
if _, err := repo_service.AdoptRepository(ctx, ctx.Doer, ctxUser, repo_module.CreateRepoOptions{
Name: repoName,
IsPrivate: true,
}); err != nil {
@ -172,7 +172,7 @@ func DeleteUnadoptedRepository(ctx *context.APIContext) {
return
}
if err := repo_service.DeleteUnadoptedRepository(ctx.Doer, ctxUser, repoName); err != nil {
if err := repo_service.DeleteUnadoptedRepository(ctx, ctx.Doer, ctxUser, repoName); err != nil {
ctx.InternalServerError(err)
return
}

View File

@ -4,13 +4,13 @@
package org
import (
"fmt"
"net/http"
"strconv"
"strings"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/label"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
@ -84,13 +84,12 @@ func CreateLabel(ctx *context.APIContext) {
// "$ref": "#/responses/validationError"
form := web.GetForm(ctx).(*api.CreateLabelOption)
form.Color = strings.Trim(form.Color, " ")
if len(form.Color) == 6 {
form.Color = "#" + form.Color
}
if !issues_model.LabelColorPattern.MatchString(form.Color) {
ctx.Error(http.StatusUnprocessableEntity, "ColorPattern", fmt.Errorf("bad color code: %s", form.Color))
color, err := label.NormalizeColor(form.Color)
if err != nil {
ctx.Error(http.StatusUnprocessableEntity, "Color", err)
return
}
form.Color = color
label := &issues_model.Label{
Name: form.Name,
@ -183,7 +182,7 @@ func EditLabel(ctx *context.APIContext) {
// "422":
// "$ref": "#/responses/validationError"
form := web.GetForm(ctx).(*api.EditLabelOption)
label, err := issues_model.GetLabelInOrgByID(ctx, ctx.Org.Organization.ID, ctx.ParamsInt64(":id"))
l, err := issues_model.GetLabelInOrgByID(ctx, ctx.Org.Organization.ID, ctx.ParamsInt64(":id"))
if err != nil {
if issues_model.IsErrOrgLabelNotExist(err) {
ctx.NotFound()
@ -194,30 +193,28 @@ func EditLabel(ctx *context.APIContext) {
}
if form.Name != nil {
label.Name = *form.Name
l.Name = *form.Name
}
if form.Exclusive != nil {
label.Exclusive = *form.Exclusive
l.Exclusive = *form.Exclusive
}
if form.Color != nil {
label.Color = strings.Trim(*form.Color, " ")
if len(label.Color) == 6 {
label.Color = "#" + label.Color
}
if !issues_model.LabelColorPattern.MatchString(label.Color) {
ctx.Error(http.StatusUnprocessableEntity, "ColorPattern", fmt.Errorf("bad color code: %s", label.Color))
color, err := label.NormalizeColor(*form.Color)
if err != nil {
ctx.Error(http.StatusUnprocessableEntity, "Color", err)
return
}
l.Color = color
}
if form.Description != nil {
label.Description = *form.Description
l.Description = *form.Description
}
if err := issues_model.UpdateLabel(label); err != nil {
if err := issues_model.UpdateLabel(l); err != nil {
ctx.Error(http.StatusInternalServerError, "UpdateLabel", err)
return
}
ctx.JSON(http.StatusOK, convert.ToLabel(label, nil, ctx.Org.Organization.AsUser()))
ctx.JSON(http.StatusOK, convert.ToLabel(l, nil, ctx.Org.Organization.AsUser()))
}
// DeleteLabel delete a label for an organization

View File

@ -118,7 +118,7 @@ func DeleteBranch(ctx *context.APIContext) {
branchName := ctx.Params("*")
if err := repo_service.DeleteBranch(ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil {
if err := repo_service.DeleteBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil {
switch {
case git.IsErrBranchNotExist(err):
ctx.NotFound(err)

View File

@ -5,13 +5,12 @@
package repo
import (
"fmt"
"net/http"
"strconv"
"strings"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/label"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
@ -93,14 +92,14 @@ func GetLabel(ctx *context.APIContext) {
// "$ref": "#/responses/Label"
var (
label *issues_model.Label
err error
l *issues_model.Label
err error
)
strID := ctx.Params(":id")
if intID, err2 := strconv.ParseInt(strID, 10, 64); err2 != nil {
label, err = issues_model.GetLabelInRepoByName(ctx, ctx.Repo.Repository.ID, strID)
l, err = issues_model.GetLabelInRepoByName(ctx, ctx.Repo.Repository.ID, strID)
} else {
label, err = issues_model.GetLabelInRepoByID(ctx, ctx.Repo.Repository.ID, intID)
l, err = issues_model.GetLabelInRepoByID(ctx, ctx.Repo.Repository.ID, intID)
}
if err != nil {
if issues_model.IsErrRepoLabelNotExist(err) {
@ -111,7 +110,7 @@ func GetLabel(ctx *context.APIContext) {
return
}
ctx.JSON(http.StatusOK, convert.ToLabel(label, ctx.Repo.Repository, nil))
ctx.JSON(http.StatusOK, convert.ToLabel(l, ctx.Repo.Repository, nil))
}
// CreateLabel create a label for a repository
@ -145,28 +144,27 @@ func CreateLabel(ctx *context.APIContext) {
// "$ref": "#/responses/validationError"
form := web.GetForm(ctx).(*api.CreateLabelOption)
form.Color = strings.Trim(form.Color, " ")
if len(form.Color) == 6 {
form.Color = "#" + form.Color
}
if !issues_model.LabelColorPattern.MatchString(form.Color) {
ctx.Error(http.StatusUnprocessableEntity, "ColorPattern", fmt.Errorf("bad color code: %s", form.Color))
color, err := label.NormalizeColor(form.Color)
if err != nil {
ctx.Error(http.StatusUnprocessableEntity, "StringToColor", err)
return
}
form.Color = color
label := &issues_model.Label{
l := &issues_model.Label{
Name: form.Name,
Exclusive: form.Exclusive,
Color: form.Color,
RepoID: ctx.Repo.Repository.ID,
Description: form.Description,
}
if err := issues_model.NewLabel(ctx, label); err != nil {
if err := issues_model.NewLabel(ctx, l); err != nil {
ctx.Error(http.StatusInternalServerError, "NewLabel", err)
return
}
ctx.JSON(http.StatusCreated, convert.ToLabel(label, ctx.Repo.Repository, nil))
ctx.JSON(http.StatusCreated, convert.ToLabel(l, ctx.Repo.Repository, nil))
}
// EditLabel modify a label for a repository
@ -206,7 +204,7 @@ func EditLabel(ctx *context.APIContext) {
// "$ref": "#/responses/validationError"
form := web.GetForm(ctx).(*api.EditLabelOption)
label, err := issues_model.GetLabelInRepoByID(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
l, err := issues_model.GetLabelInRepoByID(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
if err != nil {
if issues_model.IsErrRepoLabelNotExist(err) {
ctx.NotFound()
@ -217,30 +215,28 @@ func EditLabel(ctx *context.APIContext) {
}
if form.Name != nil {
label.Name = *form.Name
l.Name = *form.Name
}
if form.Exclusive != nil {
label.Exclusive = *form.Exclusive
l.Exclusive = *form.Exclusive
}
if form.Color != nil {
label.Color = strings.Trim(*form.Color, " ")
if len(label.Color) == 6 {
label.Color = "#" + label.Color
}
if !issues_model.LabelColorPattern.MatchString(label.Color) {
ctx.Error(http.StatusUnprocessableEntity, "ColorPattern", fmt.Errorf("bad color code: %s", label.Color))
color, err := label.NormalizeColor(*form.Color)
if err != nil {
ctx.Error(http.StatusUnprocessableEntity, "StringToColor", err)
return
}
l.Color = color
}
if form.Description != nil {
label.Description = *form.Description
l.Description = *form.Description
}
if err := issues_model.UpdateLabel(label); err != nil {
if err := issues_model.UpdateLabel(l); err != nil {
ctx.Error(http.StatusInternalServerError, "UpdateLabel", err)
return
}
ctx.JSON(http.StatusOK, convert.ToLabel(label, ctx.Repo.Repository, nil))
ctx.JSON(http.StatusOK, convert.ToLabel(l, ctx.Repo.Repository, nil))
}
// DeleteLabel delete a label for a repository

View File

@ -904,7 +904,7 @@ func MergePullRequest(ctx *context.APIContext) {
}
defer headRepo.Close()
}
if err := repo_service.DeleteBranch(ctx.Doer, pr.HeadRepo, headRepo, pr.HeadBranch); err != nil {
if err := repo_service.DeleteBranch(ctx, ctx.Doer, pr.HeadRepo, headRepo, pr.HeadBranch); err != nil {
switch {
case git.IsErrBranchNotExist(err):
ctx.NotFound(err)

View File

@ -268,7 +268,7 @@ func DeletePullReview(ctx *context.APIContext) {
ctx.Status(http.StatusNoContent)
}
// CreatePullReview create a review to an pull request
// CreatePullReview create a review to a pull request
func CreatePullReview(ctx *context.APIContext) {
// swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/reviews repository repoCreatePullReview
// ---
@ -360,7 +360,7 @@ func CreatePullReview(ctx *context.APIContext) {
line,
c.Body,
c.Path,
true, // is review
true, // pending review
0, // no reply
opts.CommitID,
); err != nil {

View File

@ -19,6 +19,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/label"
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
@ -230,7 +231,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre
if opt.AutoInit && opt.Readme == "" {
opt.Readme = "Default"
}
repo, err := repo_service.CreateRepository(ctx.Doer, owner, repo_module.CreateRepoOptions{
repo, err := repo_service.CreateRepository(ctx, ctx.Doer, owner, repo_module.CreateRepoOptions{
Name: opt.Name,
Description: opt.Description,
IssueLabels: opt.IssueLabels,
@ -248,7 +249,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre
ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.")
} else if db.IsErrNameReserved(err) ||
db.IsErrNamePatternNotAllowed(err) ||
repo_module.IsErrIssueLabelTemplateLoad(err) {
label.IsErrTemplateLoad(err) {
ctx.Error(http.StatusUnprocessableEntity, "", err)
} else {
ctx.Error(http.StatusInternalServerError, "CreateRepository", err)
@ -393,7 +394,7 @@ func Generate(ctx *context.APIContext) {
}
}
repo, err := repo_service.GenerateRepository(ctx.Doer, ctxUser, ctx.Repo.Repository, opts)
repo, err := repo_service.GenerateRepository(ctx, ctx.Doer, ctxUser, ctx.Repo.Repository, opts)
if err != nil {
if repo_model.IsErrRepoAlreadyExist(err) {
ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.")
@ -637,7 +638,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
}
// Check if repository name has been changed and not just a case change
if repo.LowerName != strings.ToLower(newRepoName) {
if err := repo_service.ChangeRepositoryName(ctx.Doer, repo, newRepoName); err != nil {
if err := repo_service.ChangeRepositoryName(ctx, ctx.Doer, repo, newRepoName); err != nil {
switch {
case repo_model.IsErrRepoAlreadyExist(err):
ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name is already taken [name: %s]", newRepoName), err)
@ -714,7 +715,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
repo.DefaultBranch = *opts.DefaultBranch
}
if err := repo_service.UpdateRepository(repo, visibilityChanged); err != nil {
if err := repo_service.UpdateRepository(ctx, repo, visibilityChanged); err != nil {
ctx.Error(http.StatusInternalServerError, "UpdateRepository", err)
return err
}

View File

@ -16,7 +16,7 @@ import (
"code.gitea.io/gitea/modules/web/routing"
"github.com/chi-middleware/proxy"
"github.com/go-chi/chi/v5/middleware"
chi "github.com/go-chi/chi/v5"
)
// Middlewares returns common middlewares
@ -48,7 +48,8 @@ func Middlewares() []func(http.Handler) http.Handler {
handlers = append(handlers, proxy.ForwardedHeaders(opt))
}
handlers = append(handlers, middleware.StripSlashes)
// Strip slashes.
handlers = append(handlers, stripSlashesMiddleware)
if !setting.Log.DisableRouterLog {
handlers = append(handlers, routing.NewLoggerHandler())
@ -81,3 +82,33 @@ func Middlewares() []func(http.Handler) http.Handler {
})
return handlers
}
func stripSlashesMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
var urlPath string
rctx := chi.RouteContext(req.Context())
if rctx != nil && rctx.RoutePath != "" {
urlPath = rctx.RoutePath
} else if req.URL.RawPath != "" {
urlPath = req.URL.RawPath
} else {
urlPath = req.URL.Path
}
sanitizedPath := &strings.Builder{}
prevWasSlash := false
for _, chr := range strings.TrimRight(urlPath, "/") {
if chr != '/' || !prevWasSlash {
sanitizedPath.WriteRune(chr)
}
prevWasSlash = chr == '/'
}
if rctx == nil {
req.URL.Path = sanitizedPath.String()
} else {
rctx.RoutePath = sanitizedPath.String()
}
next.ServeHTTP(resp, req)
})
}

View File

@ -0,0 +1,70 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package common
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func TestStripSlashesMiddleware(t *testing.T) {
type test struct {
name string
expectedPath string
inputPath string
}
tests := []test{
{
name: "path with multiple slashes",
inputPath: "https://github.com///go-gitea//gitea.git",
expectedPath: "/go-gitea/gitea.git",
},
{
name: "path with no slashes",
inputPath: "https://github.com/go-gitea/gitea.git",
expectedPath: "/go-gitea/gitea.git",
},
{
name: "path with slashes in the middle",
inputPath: "https://git.data.coop//halfd/new-website.git",
expectedPath: "/halfd/new-website.git",
},
{
name: "path with slashes in the middle",
inputPath: "https://git.data.coop//halfd/new-website.git",
expectedPath: "/halfd/new-website.git",
},
{
name: "path with slashes in the end",
inputPath: "/user2//repo1/",
expectedPath: "/user2/repo1",
},
{
name: "path with slashes and query params",
inputPath: "/repo//migrate?service_type=3",
expectedPath: "/repo/migrate",
},
{
name: "path with encoded slash",
inputPath: "/user2/%2F%2Frepo1",
expectedPath: "/user2/%2F%2Frepo1",
},
}
for _, tt := range tests {
testMiddleware := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, tt.expectedPath, r.URL.Path)
})
// pass the test middleware to validate the changes
handlerToTest := stripSlashesMiddleware(testMiddleware)
// create a mock request to use
req := httptest.NewRequest("GET", tt.inputPath, nil)
// call the handler using a mock response recorder
handlerToTest.ServeHTTP(httptest.NewRecorder(), req)
}
}

View File

@ -59,11 +59,6 @@ func Init(ctx goctx.Context) func(next http.Handler) http.Handler {
dbTypeNames := getSupportedDbTypeNames()
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
if setting.InstallLock {
resp.Header().Add("Refresh", "1; url="+setting.AppURL+"user/login")
_ = rnd.HTML(resp, http.StatusOK, string(tplPostInstall), nil)
return
}
locale := middleware.Locale(resp, req)
startTime := time.Now()
ctx := context.Context{
@ -93,6 +88,11 @@ func Init(ctx goctx.Context) func(next http.Handler) http.Handler {
// Install render installation page
func Install(ctx *context.Context) {
if setting.InstallLock {
InstallDone(ctx)
return
}
form := forms.InstallForm{}
// Database settings
@ -162,7 +162,7 @@ func Install(ctx *context.Context) {
form.DefaultAllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization
form.DefaultEnableTimetracking = setting.Service.DefaultEnableTimetracking
form.NoReplyAddress = setting.Service.NoReplyAddress
form.PasswordAlgorithm = setting.PasswordHashAlgo
form.PasswordAlgorithm = hash.ConfigHashAlgorithm(setting.PasswordHashAlgo)
middleware.AssignForm(form, ctx.Data)
ctx.HTML(http.StatusOK, tplInstall)
@ -234,6 +234,11 @@ func checkDatabase(ctx *context.Context, form *forms.InstallForm) bool {
// SubmitInstall response for submit install items
func SubmitInstall(ctx *context.Context) {
if setting.InstallLock {
InstallDone(ctx)
return
}
var err error
form := *web.GetForm(ctx).(*forms.InstallForm)
@ -277,7 +282,6 @@ func SubmitInstall(ctx *context.Context) {
setting.Database.Charset = form.Charset
setting.Database.Path = form.DbPath
setting.Database.LogSQL = !setting.IsProd
setting.PasswordHashAlgo = form.PasswordAlgorithm
if !checkDatabase(ctx, &form) {
return
@ -499,6 +503,12 @@ func SubmitInstall(ctx *context.Context) {
}
if len(form.PasswordAlgorithm) > 0 {
var algorithm *hash.PasswordHashAlgorithm
setting.PasswordHashAlgo, algorithm = hash.SetDefaultPasswordHashAlgorithm(form.PasswordAlgorithm)
if algorithm == nil {
ctx.RenderWithErr(ctx.Tr("install.invalid_password_algorithm"), tplInstall, &form)
return
}
cfg.Section("security").Key("PASSWORD_HASH_ALGO").SetValue(form.PasswordAlgorithm)
}
@ -571,18 +581,26 @@ func SubmitInstall(ctx *context.Context) {
}
log.Info("First-time run install finished!")
InstallDone(ctx)
ctx.Flash.Success(ctx.Tr("install.install_success"))
ctx.RespHeader().Add("Refresh", "1; url="+setting.AppURL+"user/login")
ctx.HTML(http.StatusOK, tplPostInstall)
// Now get the http.Server from this request and shut it down
// NB: This is not our hammerable graceful shutdown this is http.Server.Shutdown
srv := ctx.Value(http.ServerContextKey).(*http.Server)
go func() {
// Sleep for a while to make sure the user's browser has loaded the post-install page and its assets (images, css, js)
// What if this duration is not long enough? That's impossible -- if the user can't load the simple page in time, how could they install or use Gitea in the future ....
time.Sleep(3 * time.Second)
// Now get the http.Server from this request and shut it down
// NB: This is not our hammerable graceful shutdown this is http.Server.Shutdown
srv := ctx.Value(http.ServerContextKey).(*http.Server)
if err := srv.Shutdown(graceful.GetManager().HammerContext()); err != nil {
log.Error("Unable to shutdown the install server! Error: %v", err)
}
// After the HTTP server for "install" shuts down, the `runWeb()` will continue to run the "normal" server
}()
}
// InstallDone shows the "post-install" page, makes it easier to develop the page.
// The name is not called as "PostInstall" to avoid misinterpretation as a handler for "POST /install"
func InstallDone(ctx *context.Context) { //nolint
ctx.HTML(http.StatusOK, tplPostInstall)
}

View File

@ -6,6 +6,7 @@ package install
import (
goctx "context"
"fmt"
"html"
"net/http"
"path"
@ -37,7 +38,7 @@ func installRecovery(ctx goctx.Context) func(next http.Handler) http.Handler {
// Why we need this? The first recover will try to render a beautiful
// error page for user, but the process can still panic again, then
// we have to just recover twice and send a simple error page that
// should not panic any more.
// should not panic anymore.
defer func() {
if err := recover(); err != nil {
combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, log.Stack(2))
@ -107,8 +108,9 @@ func Routes(ctx goctx.Context) *web.Route {
r.Use(installRecovery(ctx))
r.Use(Init(ctx))
r.Get("/", Install)
r.Get("/", Install) // it must be on the root, because the "install.js" use the window.location to replace the "localhost" AppURL
r.Post("/", web.Bind(forms.InstallForm{}), SubmitInstall)
r.Get("/post-install", InstallDone)
r.Get("/api/healthz", healthcheck.Check)
r.NotFound(web.Wrap(installNotFound))
@ -116,5 +118,10 @@ func Routes(ctx goctx.Context) *web.Route {
}
func installNotFound(w http.ResponseWriter, req *http.Request) {
http.Redirect(w, req, setting.AppURL, http.StatusFound)
w.Header().Add("Content-Type", "text/html; charset=utf-8")
w.Header().Add("Refresh", fmt.Sprintf("1; url=%s", setting.AppSubURL+"/"))
// do not use 30x status, because the "post-install" page needs to use 404/200 to detect if Gitea has been installed.
// the fetch API could follow 30x requests to the page with 200 status.
w.WriteHeader(http.StatusNotFound)
_, _ = fmt.Fprintf(w, `Not Found. <a href="%s">Go to default page</a>.`, html.EscapeString(setting.AppSubURL+"/"))
}

View File

@ -368,7 +368,7 @@ func ServCommand(ctx *context.PrivateContext) {
return
}
repo, err = repo_service.PushCreateRepo(user, owner, results.RepoName)
repo, err = repo_service.PushCreateRepo(ctx, user, owner, results.RepoName)
if err != nil {
log.Error("pushCreateRepo: %v", err)
ctx.JSON(http.StatusNotFound, private.ErrServCommand{

View File

@ -96,7 +96,7 @@ func UnadoptedRepos(ctx *context.Context) {
}
ctx.Data["Keyword"] = q
repoNames, count, err := repo_service.ListUnadoptedRepositories(q, &opts)
repoNames, count, err := repo_service.ListUnadoptedRepositories(ctx, q, &opts)
if err != nil {
ctx.ServerError("ListUnadoptedRepositories", err)
}
@ -148,7 +148,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
if has || !isDir {
// Fallthrough to failure mode
} else if action == "adopt" {
if _, err := repo_service.AdoptRepository(ctx.Doer, ctxUser, repo_module.CreateRepoOptions{
if _, err := repo_service.AdoptRepository(ctx, ctx.Doer, ctxUser, repo_module.CreateRepoOptions{
Name: dirSplit[1],
IsPrivate: true,
}); err != nil {
@ -157,7 +157,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
}
ctx.Flash.Success(ctx.Tr("repo.adopt_preexisting_success", dir))
} else if action == "delete" {
if err := repo_service.DeleteUnadoptedRepository(ctx.Doer, ctxUser, dirSplit[1]); err != nil {
if err := repo_service.DeleteUnadoptedRepository(ctx, ctx.Doer, ctxUser, dirSplit[1]); err != nil {
ctx.ServerError("repository.AdoptRepository", err)
return
}

View File

@ -9,6 +9,7 @@ import (
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/label"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/forms"
@ -103,8 +104,8 @@ func InitializeLabels(ctx *context.Context) {
}
if err := repo_module.InitializeLabels(ctx, ctx.Org.Organization.ID, form.TemplateName, true); err != nil {
if repo_module.IsErrIssueLabelTemplateLoad(err) {
originalErr := err.(repo_module.ErrIssueLabelTemplateLoad).OriginalError
if label.IsErrTemplateLoad(err) {
originalErr := err.(label.ErrTemplateLoad).OriginalError
ctx.Flash.Error(ctx.Tr("repo.issues.label_templates.fail_to_load_file", form.TemplateName, originalErr))
ctx.Redirect(ctx.Org.OrgLink + "/settings/labels")
return

View File

@ -137,7 +137,7 @@ func SettingsPost(ctx *context.Context) {
}
for _, repo := range repos {
repo.OwnerName = org.Name
if err := repo_service.UpdateRepository(repo, true); err != nil {
if err := repo_service.UpdateRepository(ctx, repo, true); err != nil {
ctx.ServerError("UpdateRepository", err)
return
}

View File

@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/actions"
context_module "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
@ -51,6 +52,7 @@ type ViewResponse struct {
Run struct {
Link string `json:"link"`
Title string `json:"title"`
Status string `json:"status"`
CanCancel bool `json:"canCancel"`
CanApprove bool `json:"canApprove"` // the run needs an approval and the doer has permission to approve
Done bool `json:"done"`
@ -111,6 +113,7 @@ func ViewPost(ctx *context_module.Context) {
resp.State.Run.CanApprove = run.NeedApproval && ctx.Repo.CanWrite(unit.TypeActions)
resp.State.Run.Done = run.Status.IsDone()
resp.State.Run.Jobs = make([]*ViewJob, 0, len(jobs)) // marshal to '[]' instead fo 'null' in json
resp.State.Run.Status = run.Status.String()
for _, v := range jobs {
resp.State.Run.Jobs = append(resp.State.Run.Jobs, &ViewJob{
ID: v.ID,
@ -212,15 +215,18 @@ func Rerun(ctx *context_module.Context) {
job.Stopped = 0
if err := db.WithTx(ctx, func(ctx context.Context) error {
if _, err := actions_model.UpdateRunJob(ctx, job, builder.Eq{"status": status}, "task_id", "status", "started", "stopped"); err != nil {
return err
}
return actions_service.CreateCommitStatus(ctx, job)
_, err := actions_model.UpdateRunJob(ctx, job, builder.Eq{"status": status}, "task_id", "status", "started", "stopped")
return err
}); err != nil {
ctx.Error(http.StatusInternalServerError, err.Error())
return
}
if err := actions_service.CreateCommitStatus(ctx, job); err != nil {
log.Error("Update commit status for job %v failed: %v", job.ID, err)
// go on
}
ctx.JSON(http.StatusOK, struct{}{})
}
@ -253,9 +259,6 @@ func Cancel(ctx *context_module.Context) {
if err := actions_model.StopTask(ctx, job.TaskID, actions_model.StatusCancelled); err != nil {
return err
}
if err := actions_service.CreateCommitStatus(ctx, job); err != nil {
return err
}
}
return nil
}); err != nil {
@ -263,6 +266,13 @@ func Cancel(ctx *context_module.Context) {
return
}
for _, job := range jobs {
if err := actions_service.CreateCommitStatus(ctx, job); err != nil {
log.Error("Update commit status for job %v failed: %v", job.ID, err)
// go on
}
}
ctx.JSON(http.StatusOK, struct{}{})
}

View File

@ -91,7 +91,7 @@ func DeleteBranchPost(ctx *context.Context) {
defer redirect(ctx)
branchName := ctx.FormString("name")
if err := repo_service.DeleteBranch(ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil {
if err := repo_service.DeleteBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil {
switch {
case git.IsErrBranchNotExist(err):
log.Debug("DeleteBranch: Can't delete non existing branch '%s'", branchName)

View File

@ -276,7 +276,7 @@ func httpBase(ctx *context.Context) (h *serviceHandler) {
return
}
repo, err = repo_service.PushCreateRepo(ctx.Doer, owner, reponame)
repo, err = repo_service.PushCreateRepo(ctx, ctx.Doer, owner, reponame)
if err != nil {
log.Error("pushCreateRepo: %v", err)
ctx.Status(http.StatusNotFound)

View File

@ -590,7 +590,7 @@ func RetrieveRepoReviewers(ctx *context.Context, repo *repo_model.Repository, is
return
}
teamReviewers, err = repo_service.GetReviewerTeams(repo)
teamReviewers, err = repo_service.GetReviewerTeams(ctx, repo)
if err != nil {
ctx.ServerError("GetReviewerTeams", err)
return
@ -1421,11 +1421,12 @@ func ViewIssue(ctx *context.Context) {
}
var (
role issues_model.RoleDescriptor
ok bool
marked = make(map[int64]issues_model.RoleDescriptor)
comment *issues_model.Comment
participants = make([]*user_model.User, 1, 10)
role issues_model.RoleDescriptor
ok bool
marked = make(map[int64]issues_model.RoleDescriptor)
comment *issues_model.Comment
participants = make([]*user_model.User, 1, 10)
latestCloseCommentID int64
)
if ctx.Repo.Repository.IsTimetrackerEnabled(ctx) {
if ctx.IsSigned {
@ -1623,9 +1624,15 @@ func ViewIssue(ctx *context.Context) {
comment.Type == issues_model.CommentTypeStopTracking {
// drop error since times could be pruned from DB..
_ = comment.LoadTime()
} else if comment.Type == issues_model.CommentTypeClose {
// record ID of latest closed comment.
// if PR is closed, the comments whose type is CommentTypePullRequestPush(29) after latestCloseCommentID won't be rendered.
latestCloseCommentID = comment.ID
}
}
ctx.Data["LatestCloseCommentID"] = latestCloseCommentID
// Combine multiple label assignments into a single comment
combineLabelComments(issue)
@ -3004,7 +3011,7 @@ func ChangeIssueReaction(ctx *context.Context) {
}
html, err := ctx.RenderToString(tplReactions, map[string]interface{}{
"ctx": ctx.Data,
"ctxData": ctx.Data,
"ActionURL": fmt.Sprintf("%s/issues/%d/reactions", ctx.Repo.RepoLink, issue.Index),
"Reactions": issue.Reactions.GroupByType(),
})
@ -3106,7 +3113,7 @@ func ChangeCommentReaction(ctx *context.Context) {
}
html, err := ctx.RenderToString(tplReactions, map[string]interface{}{
"ctx": ctx.Data,
"ctxData": ctx.Data,
"ActionURL": fmt.Sprintf("%s/comments/%d/reactions", ctx.Repo.RepoLink, comment.ID),
"Reactions": comment.Reactions.GroupByType(),
})
@ -3228,7 +3235,7 @@ func updateAttachments(ctx *context.Context, item interface{}, files []string) e
func attachmentsHTML(ctx *context.Context, attachments []*repo_model.Attachment, content string) string {
attachHTML, err := ctx.RenderToString(tplAttachment, map[string]interface{}{
"ctx": ctx.Data,
"ctxData": ctx.Data,
"Attachments": attachments,
"Content": content,
})

View File

@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/label"
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/web"
@ -41,8 +42,8 @@ func InitializeLabels(ctx *context.Context) {
}
if err := repo_module.InitializeLabels(ctx, ctx.Repo.Repository.ID, form.TemplateName, false); err != nil {
if repo_module.IsErrIssueLabelTemplateLoad(err) {
originalErr := err.(repo_module.ErrIssueLabelTemplateLoad).OriginalError
if label.IsErrTemplateLoad(err) {
originalErr := err.(label.ErrTemplateLoad).OriginalError
ctx.Flash.Error(ctx.Tr("repo.issues.label_templates.fail_to_load_file", form.TemplateName, originalErr))
ctx.Redirect(ctx.Repo.RepoLink + "/labels")
return

View File

@ -587,7 +587,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
ctx.Data["HeadBranchCommitID"] = headBranchSha
ctx.Data["PullHeadCommitID"] = sha
if pull.HeadRepo == nil || !headBranchExist || headBranchSha != sha {
if pull.HeadRepo == nil || !headBranchExist || (!pull.Issue.IsClosed && (headBranchSha != sha)) {
ctx.Data["IsPullRequestBroken"] = true
if pull.IsSameRepo() {
ctx.Data["HeadTarget"] = pull.HeadBranch
@ -1399,7 +1399,7 @@ func CleanUpPullRequest(ctx *context.Context) {
func deleteBranch(ctx *context.Context, pr *issues_model.PullRequest, gitRepo *git.Repository) {
fullBranchName := pr.HeadRepo.FullName() + ":" + pr.HeadBranch
if err := repo_service.DeleteBranch(ctx.Doer, pr.HeadRepo, gitRepo, pr.HeadBranch); err != nil {
if err := repo_service.DeleteBranch(ctx, ctx.Doer, pr.HeadRepo, gitRepo, pr.HeadBranch); err != nil {
switch {
case git.IsErrBranchNotExist(err):
ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", fullBranchName))

View File

@ -77,7 +77,7 @@ func CreateCodeComment(ctx *context.Context) {
signedLine,
form.Content,
form.TreePath,
form.IsReview,
!form.SingleReview,
form.Reply,
form.LatestCommitID,
)

View File

@ -248,14 +248,14 @@ func CreatePost(ctx *context.Context) {
return
}
repo, err = repo_service.GenerateRepository(ctx.Doer, ctxUser, templateRepo, opts)
repo, err = repo_service.GenerateRepository(ctx, ctx.Doer, ctxUser, templateRepo, opts)
if err == nil {
log.Trace("Repository generated [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
ctx.Redirect(repo.Link())
return
}
} else {
repo, err = repo_service.CreateRepository(ctx.Doer, ctxUser, repo_module.CreateRepoOptions{
repo, err = repo_service.CreateRepository(ctx, ctx.Doer, ctxUser, repo_module.CreateRepoOptions{
Name: form.RepoName,
Description: form.Description,
Gitignores: form.Gitignores,
@ -302,7 +302,7 @@ func Action(ctx *context.Context) {
ctx.Repo.Repository.Description = ctx.FormString("desc")
ctx.Repo.Repository.Website = ctx.FormString("site")
err = repo_service.UpdateRepository(ctx.Repo.Repository, false)
err = repo_service.UpdateRepository(ctx, ctx.Repo.Repository, false)
}
if err != nil {

View File

@ -134,7 +134,7 @@ func SettingsPost(ctx *context.Context) {
ctx.Repo.GitRepo.Close()
ctx.Repo.GitRepo = nil
}
if err := repo_service.ChangeRepositoryName(ctx.Doer, repo, newRepoName); err != nil {
if err := repo_service.ChangeRepositoryName(ctx, ctx.Doer, repo, newRepoName); err != nil {
ctx.Data["Err_RepoName"] = true
switch {
case repo_model.IsErrRepoAlreadyExist(err):
@ -183,7 +183,7 @@ func SettingsPost(ctx *context.Context) {
}
repo.IsPrivate = form.Private
if err := repo_service.UpdateRepository(repo, visibilityChanged); err != nil {
if err := repo_service.UpdateRepository(ctx, repo, visibilityChanged); err != nil {
ctx.ServerError("UpdateRepository", err)
return
}
@ -541,7 +541,7 @@ func SettingsPost(ctx *context.Context) {
return
}
if repoChanged {
if err := repo_service.UpdateRepository(repo, false); err != nil {
if err := repo_service.UpdateRepository(ctx, repo, false); err != nil {
ctx.ServerError("UpdateRepository", err)
return
}
@ -560,7 +560,7 @@ func SettingsPost(ctx *context.Context) {
}
if changed {
if err := repo_service.UpdateRepository(repo, false); err != nil {
if err := repo_service.UpdateRepository(ctx, repo, false); err != nil {
ctx.ServerError("UpdateRepository", err)
return
}
@ -580,7 +580,7 @@ func SettingsPost(ctx *context.Context) {
repo.IsFsckEnabled = form.EnableHealthCheck
}
if err := repo_service.UpdateRepository(repo, false); err != nil {
if err := repo_service.UpdateRepository(ctx, repo, false); err != nil {
ctx.ServerError("UpdateRepository", err)
return
}
@ -672,7 +672,7 @@ func SettingsPost(ctx *context.Context) {
return
}
if err := repo_service.ConvertForkToNormalRepository(repo); err != nil {
if err := repo_service.ConvertForkToNormalRepository(ctx, repo); err != nil {
log.Error("Unable to convert repository %-v from fork. Error: %v", repo, err)
ctx.ServerError("Convert Fork", err)
return
@ -1244,7 +1244,7 @@ func UpdateAvatarSetting(ctx *context.Context, form forms.AvatarForm) error {
if !(st.IsImage() && !st.IsSvgImage()) {
return errors.New(ctx.Tr("settings.uploaded_avatar_not_a_image"))
}
if err = repo_service.UploadAvatar(ctxRepo, data); err != nil {
if err = repo_service.UploadAvatar(ctx, ctxRepo, data); err != nil {
return fmt.Errorf("UploadAvatar: %w", err)
}
return nil
@ -1264,7 +1264,7 @@ func SettingsAvatar(ctx *context.Context) {
// SettingsDeleteAvatar delete repository avatar
func SettingsDeleteAvatar(ctx *context.Context) {
if err := repo_service.DeleteAvatar(ctx.Repo.Repository); err != nil {
if err := repo_service.DeleteAvatar(ctx, ctx.Repo.Repository); err != nil {
ctx.Flash.Error(fmt.Sprintf("DeleteAvatar: %v", err))
}
ctx.Redirect(ctx.Repo.RepoLink + "/settings")

View File

@ -356,7 +356,7 @@ func RenameBranchPost(ctx *context.Context) {
return
}
msg, err := repository.RenameBranch(ctx.Repo.Repository, ctx.Doer, ctx.Repo.GitRepo, form.From, form.To)
msg, err := repository.RenameBranch(ctx, ctx.Repo.Repository, ctx.Doer, ctx.Repo.GitRepo, form.From, form.To)
if err != nil {
ctx.ServerError("RenameBranch", err)
return

View File

@ -186,7 +186,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
return
}
renderReadmeFile(ctx, readmeFile, treeLink)
renderReadmeFile(ctx, readmeFile, fmt.Sprintf("%s/%s", treeLink, readmeFile.name))
}
// localizedExtensions prepends the provided language code with and without a

View File

@ -45,7 +45,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
if has || !isDir {
// Fallthrough to failure mode
} else if action == "adopt" && allowAdopt {
if _, err := repo_service.AdoptRepository(ctxUser, ctxUser, repo_module.CreateRepoOptions{
if _, err := repo_service.AdoptRepository(ctx, ctxUser, ctxUser, repo_module.CreateRepoOptions{
Name: dir,
IsPrivate: true,
}); err != nil {
@ -54,7 +54,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
}
ctx.Flash.Success(ctx.Tr("repo.adopt_preexisting_success", dir))
} else if action == "delete" && allowDelete {
if err := repo_service.DeleteUnadoptedRepository(ctxUser, ctxUser, dir); err != nil {
if err := repo_service.DeleteUnadoptedRepository(ctx, ctxUser, ctxUser, dir); err != nil {
ctx.ServerError("repository.AdoptRepository", err)
return
}

View File

@ -43,6 +43,7 @@ func stopTasks(ctx context.Context, opts actions_model.FindTaskOptions) error {
return fmt.Errorf("find tasks: %w", err)
}
jobs := make([]*actions_model.ActionRunJob, 0, len(tasks))
for _, task := range tasks {
if err := db.WithTx(ctx, func(ctx context.Context) error {
if err := actions_model.StopTask(ctx, task.ID, actions_model.StatusFailure); err != nil {
@ -51,7 +52,8 @@ func stopTasks(ctx context.Context, opts actions_model.FindTaskOptions) error {
if err := task.LoadJob(ctx); err != nil {
return err
}
return CreateCommitStatus(ctx, task.Job)
jobs = append(jobs, task.Job)
return nil
}); err != nil {
log.Warn("Cannot stop task %v: %v", task.ID, err)
// go on
@ -61,6 +63,14 @@ func stopTasks(ctx context.Context, opts actions_model.FindTaskOptions) error {
remove()
}
}
for _, job := range jobs {
if err := CreateCommitStatus(ctx, job); err != nil {
log.Error("Update commit status for job %v failed: %v", job.ID, err)
// go on
}
}
return nil
}
@ -80,14 +90,16 @@ func CancelAbandonedJobs(ctx context.Context) error {
job.Status = actions_model.StatusCancelled
job.Stopped = now
if err := db.WithTx(ctx, func(ctx context.Context) error {
if _, err := actions_model.UpdateRunJob(ctx, job, nil, "status", "stopped"); err != nil {
return err
}
return CreateCommitStatus(ctx, job)
_, err := actions_model.UpdateRunJob(ctx, job, nil, "status", "stopped")
return err
}); err != nil {
log.Warn("cancel abandoned job %v: %v", job.ID, err)
// go on
}
if err := CreateCommitStatus(ctx, job); err != nil {
log.Error("Update commit status for job %v failed: %v", job.ID, err)
// go on
}
}
return nil

View File

@ -30,6 +30,16 @@ func CreateCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
return fmt.Errorf("GetPushEventPayload: %w", err)
}
// Since the payload comes from json data, we should check if it's broken, or it will cause panic
switch {
case payload.Repo == nil:
return fmt.Errorf("repo is missing in event payload")
case payload.Pusher == nil:
return fmt.Errorf("pusher is missing in event payload")
case payload.HeadCommit == nil:
return fmt.Errorf("head commit is missing in event payload")
}
creator, err := user_model.GetUserByID(ctx, payload.Pusher.ID)
if err != nil {
return fmt.Errorf("GetUserByID: %w", err)

View File

@ -187,7 +187,8 @@ func notify(ctx context.Context, input *notifyInput) error {
} else {
for _, job := range jobs {
if err := CreateCommitStatus(ctx, job); err != nil {
log.Error("CreateCommitStatus: %v", err)
log.Error("Update commit status for job %v failed: %v", job.ID, err)
// go on
}
}
}

View File

@ -622,7 +622,7 @@ type CodeCommentForm struct {
Side string `binding:"Required;In(previous,proposed)"`
Line int64
TreePath string `form:"path" binding:"Required"`
IsReview bool `form:"is_review"`
SingleReview bool `form:"single_review"`
Reply int64 `form:"reply"`
LatestCommitID string
}

View File

@ -127,7 +127,7 @@ func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *u
comment.Line,
content.Content,
comment.TreePath,
false,
false, // not pending review but a single review
comment.ReviewID,
"",
)

View File

@ -21,6 +21,7 @@ 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/label"
"code.gitea.io/gitea/modules/log"
base "code.gitea.io/gitea/modules/migration"
repo_module "code.gitea.io/gitea/modules/repository"
@ -217,18 +218,20 @@ func (g *GiteaLocalUploader) CreateMilestones(milestones ...*base.Milestone) err
// CreateLabels creates labels
func (g *GiteaLocalUploader) CreateLabels(labels ...*base.Label) error {
lbs := make([]*issues_model.Label, 0, len(labels))
for _, label := range labels {
// We must validate color here:
if !issues_model.LabelColorPattern.MatchString("#" + label.Color) {
log.Warn("Invalid label color: #%s for label: %s in migration to %s/%s", label.Color, label.Name, g.repoOwner, g.repoName)
label.Color = "ffffff"
for _, l := range labels {
if color, err := label.NormalizeColor(l.Color); err != nil {
log.Warn("Invalid label color: #%s for label: %s in migration to %s/%s", l.Color, l.Name, g.repoOwner, g.repoName)
l.Color = "#ffffff"
} else {
l.Color = color
}
lbs = append(lbs, &issues_model.Label{
RepoID: g.repo.ID,
Name: label.Name,
Description: label.Description,
Color: "#" + label.Color,
Name: l.Name,
Exclusive: l.Exclusive,
Description: l.Description,
Color: l.Color,
})
}

View File

@ -499,6 +499,13 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
theCommits.Commits = theCommits.Commits[:setting.UI.FeedMaxCommitNum]
}
if newCommit, err := gitRepo.GetCommit(newCommitID); err != nil {
log.Error("SyncMirrors [repo: %-v]: unable to get commit %s: %v", m.Repo, newCommitID, err)
continue
} else {
theCommits.HeadCommit = repo_module.CommitToPushCommit(newCommit)
}
theCommits.CompareURL = m.Repo.ComposeCompareURL(oldCommitID, newCommitID)
notification.NotifySyncPushCommits(ctx, m.Repo.MustOwner(ctx), m.Repo, &repo_module.PushUpdateOptions{

View File

@ -257,7 +257,7 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string,
// If you don't let it run all the way then you will lose data
// TODO: graceful: AddTestPullRequestTask needs to become a queue!
prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(repoID, branch)
prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(repoID, branch, true)
if err != nil {
log.Error("Find pull requests [head_repo_id: %d, head_branch: %s]: %v", repoID, branch, err)
return
@ -500,7 +500,7 @@ func (errs errlist) Error() string {
// CloseBranchPulls close all the pull requests who's head branch is the branch
func CloseBranchPulls(doer *user_model.User, repoID int64, branch string) error {
prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(repoID, branch)
prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(repoID, branch, false)
if err != nil {
return err
}
@ -536,7 +536,7 @@ func CloseRepoBranchesPulls(ctx context.Context, doer *user_model.User, repo *re
var errs errlist
for _, branch := range branches {
prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(repo.ID, branch.Name)
prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(repo.ID, branch.Name, false)
if err != nil {
return err
}

View File

@ -71,7 +71,7 @@ func InvalidateCodeComments(ctx context.Context, prs issues_model.PullRequestLis
}
// CreateCodeComment creates a comment on the code line
func CreateCodeComment(ctx context.Context, doer *user_model.User, gitRepo *git.Repository, issue *issues_model.Issue, line int64, content, treePath string, isReview bool, replyReviewID int64, latestCommitID string) (*issues_model.Comment, error) {
func CreateCodeComment(ctx context.Context, doer *user_model.User, gitRepo *git.Repository, issue *issues_model.Issue, line int64, content, treePath string, pendingReview bool, replyReviewID int64, latestCommitID string) (*issues_model.Comment, error) {
var (
existsReview bool
err error
@ -82,7 +82,7 @@ func CreateCodeComment(ctx context.Context, doer *user_model.User, gitRepo *git.
// - Comments that are part of a review
// - Comments that reply to an existing review
if !isReview && replyReviewID != 0 {
if !pendingReview && replyReviewID != 0 {
// It's not part of a review; maybe a reply to a review comment or a single comment.
// Check if there are reviews for that line already; if there are, this is a reply
if existsReview, err = issues_model.ReviewExists(issue, treePath, line); err != nil {
@ -91,7 +91,7 @@ func CreateCodeComment(ctx context.Context, doer *user_model.User, gitRepo *git.
}
// Comments that are replies don't require a review header to show up in the issue view
if !isReview && existsReview {
if !pendingReview && existsReview {
if err = issue.LoadRepo(ctx); err != nil {
return nil, err
}
@ -149,7 +149,7 @@ func CreateCodeComment(ctx context.Context, doer *user_model.User, gitRepo *git.
return nil, err
}
if !isReview && !existsReview {
if !pendingReview && !existsReview {
// Submit the review we've just created so the comment shows up in the issue view
if _, _, err = SubmitReview(ctx, doer, gitRepo, issue, issues_model.ReviewTypeComment, "", latestCommitID, nil); err != nil {
return nil, err

View File

@ -67,6 +67,12 @@ func createTemporaryRepo(ctx context.Context, pr *issues_model.PullRequest) (str
remoteRepoName := "head_repo"
baseBranch := "base"
fetchArgs := git.TrustedCmdArgs{"--no-tags"}
if git.CheckGitVersionAtLeast("2.25.0") == nil {
// Writing the commit graph can be slow and is not needed here
fetchArgs = append(fetchArgs, "--no-write-commit-graph")
}
// Add head repo remote.
addCacheRepo := func(staging, cache string) error {
p := filepath.Join(staging, ".git", "objects", "info", "alternates")
@ -108,7 +114,7 @@ func createTemporaryRepo(ctx context.Context, pr *issues_model.PullRequest) (str
outbuf.Reset()
errbuf.Reset()
if err := git.NewCommand(ctx, "fetch", "origin", "--no-tags").AddDashesAndList(pr.BaseBranch+":"+baseBranch, pr.BaseBranch+":original_"+baseBranch).
if err := git.NewCommand(ctx, "fetch", "origin").AddArguments(fetchArgs...).AddDashesAndList(pr.BaseBranch+":"+baseBranch, pr.BaseBranch+":original_"+baseBranch).
Run(&git.RunOpts{
Dir: tmpBasePath,
Stdout: &outbuf,
@ -171,7 +177,7 @@ func createTemporaryRepo(ctx context.Context, pr *issues_model.PullRequest) (str
} else {
headBranch = pr.GetGitRefName()
}
if err := git.NewCommand(ctx, "fetch", "--no-tags").AddDynamicArguments(remoteRepoName, headBranch+":"+trackingBranch).
if err := git.NewCommand(ctx, "fetch").AddArguments(fetchArgs...).AddDynamicArguments(remoteRepoName, headBranch+":"+trackingBranch).
Run(&git.RunOpts{
Dir: tmpBasePath,
Stdout: &outbuf,

View File

@ -26,7 +26,7 @@ import (
)
// AdoptRepository adopts pre-existing repository files for the user/organization.
func AdoptRepository(doer, u *user_model.User, opts repo_module.CreateRepoOptions) (*repo_model.Repository, error) {
func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts repo_module.CreateRepoOptions) (*repo_model.Repository, error) {
if !doer.IsAdmin && !u.CanCreateRepo() {
return nil, repo_model.ErrReachLimitOfRepo{
Limit: u.MaxRepoCreation,
@ -53,7 +53,7 @@ func AdoptRepository(doer, u *user_model.User, opts repo_module.CreateRepoOption
IsEmpty: !opts.AutoInit,
}
if err := db.WithTx(db.DefaultContext, func(ctx context.Context) error {
if err := db.WithTx(ctx, func(ctx context.Context) error {
repoPath := repo_model.RepoPath(u.Name, repo.Name)
isExist, err := util.IsExist(repoPath)
if err != nil {
@ -95,7 +95,7 @@ func AdoptRepository(doer, u *user_model.User, opts repo_module.CreateRepoOption
return nil, err
}
notification.NotifyCreateRepository(db.DefaultContext, doer, u, repo)
notification.NotifyCreateRepository(ctx, doer, u, repo)
return repo, nil
}
@ -188,7 +188,7 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r
}
// DeleteUnadoptedRepository deletes unadopted repository files from the filesystem
func DeleteUnadoptedRepository(doer, u *user_model.User, repoName string) error {
func DeleteUnadoptedRepository(ctx context.Context, doer, u *user_model.User, repoName string) error {
if err := repo_model.IsUsableRepoName(repoName); err != nil {
return err
}
@ -206,7 +206,7 @@ func DeleteUnadoptedRepository(doer, u *user_model.User, repoName string) error
}
}
if exist, err := repo_model.IsRepositoryExist(db.DefaultContext, u, repoName); err != nil {
if exist, err := repo_model.IsRepositoryExist(ctx, u, repoName); err != nil {
return err
} else if exist {
return repo_model.ErrRepoAlreadyExist{
@ -232,11 +232,11 @@ func (unadopted *unadoptedRepositories) add(repository string) {
unadopted.index++
}
func checkUnadoptedRepositories(userName string, repoNamesToCheck []string, unadopted *unadoptedRepositories) error {
func checkUnadoptedRepositories(ctx context.Context, userName string, repoNamesToCheck []string, unadopted *unadoptedRepositories) error {
if len(repoNamesToCheck) == 0 {
return nil
}
ctxUser, err := user_model.GetUserByName(db.DefaultContext, userName)
ctxUser, err := user_model.GetUserByName(ctx, userName)
if err != nil {
if user_model.IsErrUserNotExist(err) {
log.Debug("Missing user: %s", userName)
@ -271,7 +271,7 @@ func checkUnadoptedRepositories(userName string, repoNamesToCheck []string, unad
}
// ListUnadoptedRepositories lists all the unadopted repositories that match the provided query
func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, int, error) {
func ListUnadoptedRepositories(ctx context.Context, query string, opts *db.ListOptions) ([]string, int, error) {
globUser, _ := glob.Compile("*")
globRepo, _ := glob.Compile("*")
@ -315,7 +315,7 @@ func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, in
if !strings.ContainsRune(path[len(root)+1:], filepath.Separator) {
// Got a new user
if err = checkUnadoptedRepositories(userName, repoNamesToCheck, unadopted); err != nil {
if err = checkUnadoptedRepositories(ctx, userName, repoNamesToCheck, unadopted); err != nil {
return err
}
repoNamesToCheck = repoNamesToCheck[:0]
@ -338,7 +338,7 @@ func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, in
repoNamesToCheck = append(repoNamesToCheck, name)
if len(repoNamesToCheck) >= setting.Database.IterateBufferSize {
if err = checkUnadoptedRepositories(userName, repoNamesToCheck, unadopted); err != nil {
if err = checkUnadoptedRepositories(ctx, userName, repoNamesToCheck, unadopted); err != nil {
return err
}
repoNamesToCheck = repoNamesToCheck[:0]
@ -349,7 +349,7 @@ func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, in
return nil, 0, err
}
if err := checkUnadoptedRepositories(userName, repoNamesToCheck, unadopted); err != nil {
if err := checkUnadoptedRepositories(ctx, userName, repoNamesToCheck, unadopted); err != nil {
return nil, 0, err
}

View File

@ -39,7 +39,7 @@ func TestCheckUnadoptedRepositories(t *testing.T) {
// Non existent user
//
unadopted := &unadoptedRepositories{start: 0, end: 100}
err := checkUnadoptedRepositories("notauser", []string{"repo"}, unadopted)
err := checkUnadoptedRepositories(db.DefaultContext, "notauser", []string{"repo"}, unadopted)
assert.NoError(t, err)
assert.Equal(t, 0, len(unadopted.repositories))
//
@ -50,14 +50,14 @@ func TestCheckUnadoptedRepositories(t *testing.T) {
repoName := "repo2"
unadoptedRepoName := "unadopted"
unadopted = &unadoptedRepositories{start: 0, end: 100}
err = checkUnadoptedRepositories(userName, []string{repoName, unadoptedRepoName}, unadopted)
err = checkUnadoptedRepositories(db.DefaultContext, userName, []string{repoName, unadoptedRepoName}, unadopted)
assert.NoError(t, err)
assert.Equal(t, []string{path.Join(userName, unadoptedRepoName)}, unadopted.repositories)
//
// Existing (adopted) repository is not returned
//
unadopted = &unadoptedRepositories{start: 0, end: 100}
err = checkUnadoptedRepositories(userName, []string{repoName}, unadopted)
err = checkUnadoptedRepositories(db.DefaultContext, userName, []string{repoName}, unadopted)
assert.NoError(t, err)
assert.Equal(t, 0, len(unadopted.repositories))
assert.Equal(t, 0, unadopted.index)
@ -72,13 +72,13 @@ func TestListUnadoptedRepositories_ListOptions(t *testing.T) {
}
opts := db.ListOptions{Page: 1, PageSize: 1}
repoNames, count, err := ListUnadoptedRepositories("", &opts)
repoNames, count, err := ListUnadoptedRepositories(db.DefaultContext, "", &opts)
assert.NoError(t, err)
assert.Equal(t, 2, count)
assert.Equal(t, unadoptedList[0], repoNames[0])
opts = db.ListOptions{Page: 2, PageSize: 1}
repoNames, count, err = ListUnadoptedRepositories("", &opts)
repoNames, count, err = ListUnadoptedRepositories(db.DefaultContext, "", &opts)
assert.NoError(t, err)
assert.Equal(t, 2, count)
assert.Equal(t, unadoptedList[1], repoNames[0])

View File

@ -20,7 +20,7 @@ import (
// UploadAvatar saves custom avatar for repository.
// FIXME: split uploads to different subdirs in case we have massive number of repos.
func UploadAvatar(repo *repo_model.Repository, data []byte) error {
func UploadAvatar(ctx context.Context, repo *repo_model.Repository, data []byte) error {
m, err := avatar.Prepare(data)
if err != nil {
return err
@ -31,7 +31,7 @@ func UploadAvatar(repo *repo_model.Repository, data []byte) error {
return nil
}
ctx, committer, err := db.TxContext(db.DefaultContext)
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return err
}
@ -65,7 +65,7 @@ func UploadAvatar(repo *repo_model.Repository, data []byte) error {
}
// DeleteAvatar deletes the repos's custom avatar.
func DeleteAvatar(repo *repo_model.Repository) error {
func DeleteAvatar(ctx context.Context, repo *repo_model.Repository) error {
// Avatar not exists
if len(repo.Avatar) == 0 {
return nil
@ -74,7 +74,7 @@ func DeleteAvatar(repo *repo_model.Repository) error {
avatarPath := repo.CustomAvatarRelativePath()
log.Trace("DeleteAvatar[%d]: %s", repo.ID, avatarPath)
ctx, committer, err := db.TxContext(db.DefaultContext)
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return err
}
@ -102,7 +102,7 @@ func RemoveRandomAvatars(ctx context.Context) error {
}
stringifiedID := strconv.FormatInt(repository.ID, 10)
if repository.Avatar == stringifiedID {
return DeleteAvatar(repository)
return DeleteAvatar(ctx, repository)
}
return nil
})

View File

@ -9,6 +9,7 @@ import (
"image/png"
"testing"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/avatar"
@ -25,7 +26,7 @@ func TestUploadAvatar(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
err := UploadAvatar(repo, buff.Bytes())
err := UploadAvatar(db.DefaultContext, repo, buff.Bytes())
assert.NoError(t, err)
assert.Equal(t, avatar.HashAvatar(10, buff.Bytes()), repo.Avatar)
}
@ -39,7 +40,7 @@ func TestUploadBigAvatar(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
err := UploadAvatar(repo, buff.Bytes())
err := UploadAvatar(db.DefaultContext, repo, buff.Bytes())
assert.Error(t, err)
}
@ -52,10 +53,10 @@ func TestDeleteAvatar(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
err := UploadAvatar(repo, buff.Bytes())
err := UploadAvatar(db.DefaultContext, repo, buff.Bytes())
assert.NoError(t, err)
err = DeleteAvatar(repo)
err = DeleteAvatar(db.DefaultContext, repo)
assert.NoError(t, err)
assert.Equal(t, "", repo.Avatar)

View File

@ -10,7 +10,6 @@ import (
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
@ -106,7 +105,7 @@ func CreateNewBranchFromCommit(ctx context.Context, doer *user_model.User, repo
}
// RenameBranch rename a branch
func RenameBranch(repo *repo_model.Repository, doer *user_model.User, gitRepo *git.Repository, from, to string) (string, error) {
func RenameBranch(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, gitRepo *git.Repository, from, to string) (string, error) {
if from == to {
return "target_exist", nil
}
@ -119,7 +118,7 @@ func RenameBranch(repo *repo_model.Repository, doer *user_model.User, gitRepo *g
return "from_not_exist", nil
}
if err := git_model.RenameBranch(db.DefaultContext, repo, from, to, func(isDefault bool) error {
if err := git_model.RenameBranch(ctx, repo, from, to, func(isDefault bool) error {
err2 := gitRepo.RenameBranch(from, to)
if err2 != nil {
return err2
@ -141,8 +140,8 @@ func RenameBranch(repo *repo_model.Repository, doer *user_model.User, gitRepo *g
return "", err
}
notification.NotifyDeleteRef(db.DefaultContext, doer, repo, "branch", git.BranchPrefix+from)
notification.NotifyCreateRef(db.DefaultContext, doer, repo, "branch", git.BranchPrefix+to, refID)
notification.NotifyDeleteRef(ctx, doer, repo, "branch", git.BranchPrefix+from)
notification.NotifyCreateRef(ctx, doer, repo, "branch", git.BranchPrefix+to, refID)
return "", nil
}
@ -153,12 +152,12 @@ var (
)
// DeleteBranch delete branch
func DeleteBranch(doer *user_model.User, repo *repo_model.Repository, gitRepo *git.Repository, branchName string) error {
func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, gitRepo *git.Repository, branchName string) error {
if branchName == repo.DefaultBranch {
return ErrBranchIsDefault
}
isProtected, err := git_model.IsBranchProtected(db.DefaultContext, repo.ID, branchName)
isProtected, err := git_model.IsBranchProtected(ctx, repo.ID, branchName)
if err != nil {
return err
}
@ -195,7 +194,7 @@ func DeleteBranch(doer *user_model.User, repo *repo_model.Repository, gitRepo *g
log.Error("Update: %v", err)
}
if err := git_model.AddDeletedBranch(db.DefaultContext, repo.ID, branchName, commit.ID.String(), doer.ID); err != nil {
if err := git_model.AddDeletedBranch(ctx, repo.ID, branchName, commit.ID.String(), doer.ID); err != nil {
log.Warn("AddDeletedBranch: %v", err)
}

View File

@ -189,8 +189,8 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
}
// ConvertForkToNormalRepository convert the provided repo from a forked repo to normal repo
func ConvertForkToNormalRepository(repo *repo_model.Repository) error {
err := db.WithTx(db.DefaultContext, func(ctx context.Context) error {
func ConvertForkToNormalRepository(ctx context.Context, repo *repo_model.Repository) error {
err := db.WithTx(ctx, func(ctx context.Context) error {
repo, err := repo_model.GetRepositoryByID(ctx, repo.ID)
if err != nil {
return err

View File

@ -80,6 +80,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("PushUpdates: %s/%s", optsList[0].RepoUserName, optsList[0].RepoName))
defer finished()
ctx = cache.WithCacheContext(ctx)
repo, err := repo_model.GetRepositoryByOwnerAndName(ctx, optsList[0].RepoUserName, optsList[0].RepoName)
if err != nil {
@ -122,7 +123,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
tagName := opts.TagName()
if opts.IsDelRef() {
notification.NotifyPushCommits(
db.DefaultContext, pusher, repo,
ctx, pusher, repo,
&repo_module.PushUpdateOptions{
RefFullName: git.TagPrefix + tagName,
OldCommitID: opts.OldCommitID,
@ -130,7 +131,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
}, repo_module.NewPushCommits())
delTags = append(delTags, tagName)
notification.NotifyDeleteRef(db.DefaultContext, pusher, repo, "tag", opts.RefFullName)
notification.NotifyDeleteRef(ctx, pusher, repo, "tag", opts.RefFullName)
} else { // is new tag
newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
if err != nil {
@ -142,7 +143,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
commits.CompareURL = repo.ComposeCompareURL(git.EmptySHA, opts.NewCommitID)
notification.NotifyPushCommits(
db.DefaultContext, pusher, repo,
ctx, pusher, repo,
&repo_module.PushUpdateOptions{
RefFullName: git.TagPrefix + tagName,
OldCommitID: git.EmptySHA,
@ -150,7 +151,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
}, commits)
addTags = append(addTags, tagName)
notification.NotifyCreateRef(db.DefaultContext, pusher, repo, "tag", opts.RefFullName, opts.NewCommitID)
notification.NotifyCreateRef(ctx, pusher, repo, "tag", opts.RefFullName, opts.NewCommitID)
}
} else if opts.IsBranch() { // If is branch reference
if pusher == nil || pusher.ID != opts.PusherID {
@ -190,7 +191,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
}
}
// Update the is empty and default_branch columns
if err := repo_model.UpdateRepositoryCols(db.DefaultContext, repo, "default_branch", "is_empty"); err != nil {
if err := repo_model.UpdateRepositoryCols(ctx, repo, "default_branch", "is_empty"); err != nil {
return fmt.Errorf("UpdateRepositoryCols: %w", err)
}
}
@ -199,7 +200,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
if err != nil {
return fmt.Errorf("newCommit.CommitsBeforeLimit: %w", err)
}
notification.NotifyCreateRef(db.DefaultContext, pusher, repo, "branch", opts.RefFullName, opts.NewCommitID)
notification.NotifyCreateRef(ctx, pusher, repo, "branch", opts.RefFullName, opts.NewCommitID)
} else {
l, err = newCommit.CommitsBeforeUntil(opts.OldCommitID)
if err != nil {
@ -259,7 +260,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
commits.Commits = commits.Commits[:setting.UI.FeedMaxCommitNum]
}
notification.NotifyPushCommits(db.DefaultContext, pusher, repo, opts, commits)
notification.NotifyPushCommits(ctx, pusher, repo, opts, commits)
if err = git_model.RemoveDeletedBranchByName(ctx, repo.ID, branch); err != nil {
log.Error("models.RemoveDeletedBranch %s/%s failed: %v", repo.ID, branch, err)
@ -270,7 +271,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
log.Error("repo_module.CacheRef %s/%s failed: %v", repo.ID, branch, err)
}
} else {
notification.NotifyDeleteRef(db.DefaultContext, pusher, repo, "branch", opts.RefFullName)
notification.NotifyDeleteRef(ctx, pusher, repo, "branch", opts.RefFullName)
if err = pull_service.CloseBranchPulls(pusher, repo.ID, branch); err != nil {
// close all related pulls
log.Error("close related pull request failed: %v", err)
@ -278,14 +279,14 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
}
// Even if user delete a branch on a repository which he didn't watch, he will be watch that.
if err = repo_model.WatchIfAuto(db.DefaultContext, opts.PusherID, repo.ID, true); err != nil {
if err = repo_model.WatchIfAuto(ctx, opts.PusherID, repo.ID, true); err != nil {
log.Warn("Fail to perform auto watch on user %v for repo %v: %v", opts.PusherID, repo.ID, err)
}
} else {
log.Trace("Non-tag and non-branch commits pushed.")
}
}
if err := PushUpdateAddDeleteTags(repo, gitRepo, addTags, delTags); err != nil {
if err := PushUpdateAddDeleteTags(ctx, repo, gitRepo, addTags, delTags); err != nil {
return fmt.Errorf("PushUpdateAddDeleteTags: %w", err)
}
@ -298,8 +299,8 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
}
// PushUpdateAddDeleteTags updates a number of added and delete tags
func PushUpdateAddDeleteTags(repo *repo_model.Repository, gitRepo *git.Repository, addTags, delTags []string) error {
return db.WithTx(db.DefaultContext, func(ctx context.Context) error {
func PushUpdateAddDeleteTags(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, addTags, delTags []string) error {
return db.WithTx(ctx, func(ctx context.Context) error {
if err := repo_model.PushUpdateDeleteTagsContext(ctx, repo, delTags); err != nil {
return err
}

Some files were not shown because too many files have changed in this diff Show More