mirror of https://github.com/go-gitea/gitea
Merge branch 'main' into add-issue-planned-time
This commit is contained in:
commit
879d96f077
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
25
cmd/serv.go
25
cmd/serv.go
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
@ -1 +1,2 @@
|
|||
37991dec2c8e592043f47155ce4808d4580f9123 feaf4ba6bc635fec442f46ddd4512416ec43c2c2 silverwind <me@silverwind.io> 1563741799 +0200 push
|
||||
feaf4ba6bc635fec442f46ddd4512416ec43c2c2 ce064814f4a0d337b333e646ece456cd39fab612 silverwind <me@silverwind.io> 1668354026 +0100 push
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
37991dec2c8e592043f47155ce4808d4580f9123 feaf4ba6bc635fec442f46ddd4512416ec43c2c2 silverwind <me@silverwind.io> 1563741799 +0200 push
|
||||
feaf4ba6bc635fec442f46ddd4512416ec43c2c2 ce064814f4a0d337b333e646ece456cd39fab612 silverwind <me@silverwind.io> 1668354026 +0100 push
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1 +1 @@
|
|||
feaf4ba6bc635fec442f46ddd4512416ec43c2c2
|
||||
ce064814f4a0d337b333e646ece456cd39fab612
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
36f97d9a96457e2bab511db30fe2db03893ebc64
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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"
|
||||
|
|
|
@ -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=フォークプルリクエストのワークフローを実行するには承認が必要です。
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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=该工作流由派生仓库的合并请求所触发,需要批准方可运行。
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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+"/"))
|
||||
}
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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{}{})
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -77,7 +77,7 @@ func CreateCodeComment(ctx *context.Context) {
|
|||
signedLine,
|
||||
form.Content,
|
||||
form.TreePath,
|
||||
form.IsReview,
|
||||
!form.SingleReview,
|
||||
form.Reply,
|
||||
form.LatestCommitID,
|
||||
)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
"",
|
||||
)
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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
|
||||
})
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue