mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-21 10:14:40 +02:00
Merge branch 'main' of https://github.com/go-gitea/gitea into workflow-webhook-api
This commit is contained in:
commit
658583a584
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Gitea DevContainer",
|
||||
"image": "mcr.microsoft.com/devcontainers/go:1.23-bookworm",
|
||||
"image": "mcr.microsoft.com/devcontainers/go:1.24-bookworm",
|
||||
"features": {
|
||||
// installs nodejs into container
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
|
@ -104,7 +104,7 @@ module.exports = {
|
||||
'@vitest/no-disabled-tests': [0],
|
||||
'@vitest/no-done-callback': [0],
|
||||
'@vitest/no-duplicate-hooks': [0],
|
||||
'@vitest/no-focused-tests': [0],
|
||||
'@vitest/no-focused-tests': [2],
|
||||
'@vitest/no-hooks': [0],
|
||||
'@vitest/no-identical-title': [2],
|
||||
'@vitest/no-interpolation-in-snapshots': [0],
|
||||
@ -155,7 +155,7 @@ module.exports = {
|
||||
'eslint-plugin-vue-scoped-css',
|
||||
],
|
||||
extends: [
|
||||
'plugin:vue/vue3-recommended',
|
||||
'plugin:vue/recommended',
|
||||
'plugin:vue-scoped-css/vue3-recommended',
|
||||
],
|
||||
rules: {
|
||||
|
4
Makefile
4
Makefile
@ -26,9 +26,9 @@ COMMA := ,
|
||||
XGO_VERSION := go-1.24.x
|
||||
|
||||
AIR_PACKAGE ?= github.com/air-verse/air@v1
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.1.2
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.2.1
|
||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.5
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.7
|
||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.12
|
||||
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.6.0
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0
|
||||
|
@ -57,6 +57,8 @@ type ActionRunner struct {
|
||||
|
||||
// Store labels defined in state file (default: .runner file) of `act_runner`
|
||||
AgentLabels []string `xorm:"TEXT"`
|
||||
// Store if this is a runner that only ever get one single job assigned
|
||||
Ephemeral bool `xorm:"ephemeral NOT NULL DEFAULT false"`
|
||||
|
||||
Created timeutil.TimeStamp `xorm:"created"`
|
||||
Updated timeutil.TimeStamp `xorm:"updated"`
|
||||
|
@ -6,10 +6,12 @@ package actions
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
@ -32,26 +34,39 @@ type ActionVariable struct {
|
||||
RepoID int64 `xorm:"INDEX UNIQUE(owner_repo_name)"`
|
||||
Name string `xorm:"UNIQUE(owner_repo_name) NOT NULL"`
|
||||
Data string `xorm:"LONGTEXT NOT NULL"`
|
||||
Description string `xorm:"TEXT"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
|
||||
}
|
||||
|
||||
const (
|
||||
VariableDataMaxLength = 65536
|
||||
VariableDescriptionMaxLength = 4096
|
||||
)
|
||||
|
||||
func init() {
|
||||
db.RegisterModel(new(ActionVariable))
|
||||
}
|
||||
|
||||
func InsertVariable(ctx context.Context, ownerID, repoID int64, name, data string) (*ActionVariable, error) {
|
||||
func InsertVariable(ctx context.Context, ownerID, repoID int64, name, data, description string) (*ActionVariable, error) {
|
||||
if ownerID != 0 && repoID != 0 {
|
||||
// It's trying to create a variable that belongs to a repository, but OwnerID has been set accidentally.
|
||||
// Remove OwnerID to avoid confusion; it's not worth returning an error here.
|
||||
ownerID = 0
|
||||
}
|
||||
|
||||
if utf8.RuneCountInString(data) > VariableDataMaxLength {
|
||||
return nil, util.NewInvalidArgumentErrorf("data too long")
|
||||
}
|
||||
|
||||
description = util.TruncateRunes(description, VariableDescriptionMaxLength)
|
||||
|
||||
variable := &ActionVariable{
|
||||
OwnerID: ownerID,
|
||||
RepoID: repoID,
|
||||
Name: strings.ToUpper(name),
|
||||
Data: data,
|
||||
OwnerID: ownerID,
|
||||
RepoID: repoID,
|
||||
Name: strings.ToUpper(name),
|
||||
Data: data,
|
||||
Description: description,
|
||||
}
|
||||
return variable, db.Insert(ctx, variable)
|
||||
}
|
||||
@ -96,6 +111,12 @@ func FindVariables(ctx context.Context, opts FindVariablesOpts) ([]*ActionVariab
|
||||
}
|
||||
|
||||
func UpdateVariableCols(ctx context.Context, variable *ActionVariable, cols ...string) (bool, error) {
|
||||
if utf8.RuneCountInString(variable.Data) > VariableDataMaxLength {
|
||||
return false, util.NewInvalidArgumentErrorf("data too long")
|
||||
}
|
||||
|
||||
variable.Description = util.TruncateRunes(variable.Description, VariableDescriptionMaxLength)
|
||||
|
||||
variable.Name = strings.ToUpper(variable.Name)
|
||||
count, err := db.GetEngine(ctx).
|
||||
ID(variable.ID).
|
||||
|
@ -453,9 +453,8 @@ func NewCommitStatus(ctx context.Context, opts NewCommitStatusOptions) error {
|
||||
return fmt.Errorf("NewCommitStatus[nil, %s]: no repository specified", opts.SHA)
|
||||
}
|
||||
|
||||
repoPath := opts.Repo.RepoPath()
|
||||
if opts.Creator == nil {
|
||||
return fmt.Errorf("NewCommitStatus[%s, %s]: no user specified", repoPath, opts.SHA)
|
||||
return fmt.Errorf("NewCommitStatus[%s, %s]: no user specified", opts.Repo.FullName(), opts.SHA)
|
||||
}
|
||||
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
@ -477,13 +476,13 @@ func NewCommitStatus(ctx context.Context, opts NewCommitStatusOptions) error {
|
||||
opts.CommitStatus.CreatorID = opts.Creator.ID
|
||||
opts.CommitStatus.RepoID = opts.Repo.ID
|
||||
opts.CommitStatus.Index = idx
|
||||
log.Debug("NewCommitStatus[%s, %s]: %d", repoPath, opts.SHA, opts.CommitStatus.Index)
|
||||
log.Debug("NewCommitStatus[%s, %s]: %d", opts.Repo.FullName(), opts.SHA, opts.CommitStatus.Index)
|
||||
|
||||
opts.CommitStatus.ContextHash = hashCommitStatusContext(opts.CommitStatus.Context)
|
||||
|
||||
// Insert new CommitStatus
|
||||
if _, err = db.GetEngine(ctx).Insert(opts.CommitStatus); err != nil {
|
||||
return fmt.Errorf("insert CommitStatus[%s, %s]: %w", repoPath, opts.SHA, err)
|
||||
return fmt.Errorf("insert CommitStatus[%s, %s]: %w", opts.Repo.FullName(), opts.SHA, err)
|
||||
}
|
||||
|
||||
return committer.Commit()
|
||||
|
@ -375,6 +375,8 @@ func prepareMigrationTasks() []*migration {
|
||||
newMigration(312, "Add DeleteBranchAfterMerge to AutoMerge", v1_24.AddDeleteBranchAfterMergeForAutoMerge),
|
||||
newMigration(313, "Move PinOrder from issue table to a new table issue_pin", v1_24.MovePinOrderToTableIssuePin),
|
||||
newMigration(314, "Update OwnerID as zero for repository level action tables", v1_24.UpdateOwnerIDOfRepoLevelActionsTables),
|
||||
newMigration(315, "Add Ephemeral to ActionRunner", v1_24.AddEphemeralToActionRunner),
|
||||
newMigration(316, "Add description for secrets and variables", v1_24.AddDescriptionForSecretsAndVariables),
|
||||
}
|
||||
return preparedMigrations
|
||||
}
|
||||
|
16
models/migrations/v1_24/v315.go
Normal file
16
models/migrations/v1_24/v315.go
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_24 //nolint
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddEphemeralToActionRunner(x *xorm.Engine) error {
|
||||
type ActionRunner struct {
|
||||
Ephemeral bool `xorm:"ephemeral NOT NULL DEFAULT false"`
|
||||
}
|
||||
|
||||
return x.Sync(new(ActionRunner))
|
||||
}
|
20
models/migrations/v1_24/v316.go
Normal file
20
models/migrations/v1_24/v316.go
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_24 //nolint
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddDescriptionForSecretsAndVariables(x *xorm.Engine) error {
|
||||
type Secret struct {
|
||||
Description string `xorm:"TEXT"`
|
||||
}
|
||||
|
||||
type ActionVariable struct {
|
||||
Description string `xorm:"TEXT"`
|
||||
}
|
||||
|
||||
return x.Sync(new(Secret), new(ActionVariable))
|
||||
}
|
@ -40,9 +40,15 @@ type Secret struct {
|
||||
RepoID int64 `xorm:"INDEX UNIQUE(owner_repo_name) NOT NULL DEFAULT 0"`
|
||||
Name string `xorm:"UNIQUE(owner_repo_name) NOT NULL"`
|
||||
Data string `xorm:"LONGTEXT"` // encrypted data
|
||||
Description string `xorm:"TEXT"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
|
||||
}
|
||||
|
||||
const (
|
||||
SecretDataMaxLength = 65536
|
||||
SecretDescriptionMaxLength = 4096
|
||||
)
|
||||
|
||||
// ErrSecretNotFound represents a "secret not found" error.
|
||||
type ErrSecretNotFound struct {
|
||||
Name string
|
||||
@ -57,7 +63,7 @@ func (err ErrSecretNotFound) Unwrap() error {
|
||||
}
|
||||
|
||||
// InsertEncryptedSecret Creates, encrypts, and validates a new secret with yet unencrypted data and insert into database
|
||||
func InsertEncryptedSecret(ctx context.Context, ownerID, repoID int64, name, data string) (*Secret, error) {
|
||||
func InsertEncryptedSecret(ctx context.Context, ownerID, repoID int64, name, data, description string) (*Secret, error) {
|
||||
if ownerID != 0 && repoID != 0 {
|
||||
// It's trying to create a secret that belongs to a repository, but OwnerID has been set accidentally.
|
||||
// Remove OwnerID to avoid confusion; it's not worth returning an error here.
|
||||
@ -67,15 +73,23 @@ func InsertEncryptedSecret(ctx context.Context, ownerID, repoID int64, name, dat
|
||||
return nil, fmt.Errorf("%w: ownerID and repoID cannot be both zero, global secrets are not supported", util.ErrInvalidArgument)
|
||||
}
|
||||
|
||||
if len(data) > SecretDataMaxLength {
|
||||
return nil, util.NewInvalidArgumentErrorf("data too long")
|
||||
}
|
||||
|
||||
description = util.TruncateRunes(description, SecretDescriptionMaxLength)
|
||||
|
||||
encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
secret := &Secret{
|
||||
OwnerID: ownerID,
|
||||
RepoID: repoID,
|
||||
Name: strings.ToUpper(name),
|
||||
Data: encrypted,
|
||||
OwnerID: ownerID,
|
||||
RepoID: repoID,
|
||||
Name: strings.ToUpper(name),
|
||||
Data: encrypted,
|
||||
Description: description,
|
||||
}
|
||||
return secret, db.Insert(ctx, secret)
|
||||
}
|
||||
@ -114,16 +128,23 @@ func (opts FindSecretsOptions) ToConds() builder.Cond {
|
||||
}
|
||||
|
||||
// UpdateSecret changes org or user reop secret.
|
||||
func UpdateSecret(ctx context.Context, secretID int64, data string) error {
|
||||
func UpdateSecret(ctx context.Context, secretID int64, data, description string) error {
|
||||
if len(data) > SecretDataMaxLength {
|
||||
return util.NewInvalidArgumentErrorf("data too long")
|
||||
}
|
||||
|
||||
description = util.TruncateRunes(description, SecretDescriptionMaxLength)
|
||||
|
||||
encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s := &Secret{
|
||||
Data: encrypted,
|
||||
Data: encrypted,
|
||||
Description: description,
|
||||
}
|
||||
affected, err := db.GetEngine(ctx).ID(secretID).Cols("data").Update(s)
|
||||
affected, err := db.GetEngine(ctx).ID(secretID).Cols("data", "description").Update(s)
|
||||
if affected != 1 {
|
||||
return ErrSecretNotFound{}
|
||||
}
|
||||
|
@ -12,13 +12,17 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
type fixtureItem struct {
|
||||
tableName string
|
||||
type FixtureItem struct {
|
||||
fileFullPath string
|
||||
tableName string
|
||||
|
||||
tableNameQuoted string
|
||||
sqlInserts []string
|
||||
sqlInsertArgs [][]any
|
||||
@ -27,10 +31,11 @@ type fixtureItem struct {
|
||||
}
|
||||
|
||||
type fixturesLoaderInternal struct {
|
||||
xormEngine *xorm.Engine
|
||||
xormTableNames map[string]bool
|
||||
db *sql.DB
|
||||
dbType schemas.DBType
|
||||
files []string
|
||||
fixtures map[string]*fixtureItem
|
||||
fixtures map[string]*FixtureItem
|
||||
quoteObject func(string) string
|
||||
paramPlaceholder func(idx int) string
|
||||
}
|
||||
@ -59,29 +64,27 @@ func (f *fixturesLoaderInternal) preprocessFixtureRow(row []map[string]any) (err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fixturesLoaderInternal) prepareFixtureItem(file string) (_ *fixtureItem, err error) {
|
||||
fixture := &fixtureItem{}
|
||||
fixture.tableName, _, _ = strings.Cut(filepath.Base(file), ".")
|
||||
func (f *fixturesLoaderInternal) prepareFixtureItem(fixture *FixtureItem) (err error) {
|
||||
fixture.tableNameQuoted = f.quoteObject(fixture.tableName)
|
||||
|
||||
if f.dbType == schemas.MSSQL {
|
||||
fixture.mssqlHasIdentityColumn, err = f.mssqlTableHasIdentityColumn(f.db, fixture.tableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(file)
|
||||
data, err := os.ReadFile(fixture.fileFullPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read file %q: %w", file, err)
|
||||
return fmt.Errorf("failed to read file %q: %w", fixture.fileFullPath, err)
|
||||
}
|
||||
|
||||
var rows []map[string]any
|
||||
if err = yaml.Unmarshal(data, &rows); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal yaml data from %q: %w", file, err)
|
||||
return fmt.Errorf("failed to unmarshal yaml data from %q: %w", fixture.fileFullPath, err)
|
||||
}
|
||||
if err = f.preprocessFixtureRow(rows); err != nil {
|
||||
return nil, fmt.Errorf("failed to preprocess fixture rows from %q: %w", file, err)
|
||||
return fmt.Errorf("failed to preprocess fixture rows from %q: %w", fixture.fileFullPath, err)
|
||||
}
|
||||
|
||||
var sqlBuf []byte
|
||||
@ -107,16 +110,14 @@ func (f *fixturesLoaderInternal) prepareFixtureItem(file string) (_ *fixtureItem
|
||||
sqlBuf = sqlBuf[:0]
|
||||
sqlArguments = sqlArguments[:0]
|
||||
}
|
||||
return fixture, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fixturesLoaderInternal) loadFixtures(tx *sql.Tx, file string) (err error) {
|
||||
fixture := f.fixtures[file]
|
||||
if fixture == nil {
|
||||
if fixture, err = f.prepareFixtureItem(file); err != nil {
|
||||
func (f *fixturesLoaderInternal) loadFixtures(tx *sql.Tx, fixture *FixtureItem) (err error) {
|
||||
if fixture.tableNameQuoted == "" {
|
||||
if err = f.prepareFixtureItem(fixture); err != nil {
|
||||
return err
|
||||
}
|
||||
f.fixtures[file] = fixture
|
||||
}
|
||||
|
||||
_, err = tx.Exec(fmt.Sprintf("DELETE FROM %s", fixture.tableNameQuoted)) // sqlite3 doesn't support truncate
|
||||
@ -147,15 +148,26 @@ func (f *fixturesLoaderInternal) Load() error {
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
for _, file := range f.files {
|
||||
if err := f.loadFixtures(tx, file); err != nil {
|
||||
return fmt.Errorf("failed to load fixtures from %s: %w", file, err)
|
||||
for _, fixture := range f.fixtures {
|
||||
if !f.xormTableNames[fixture.tableName] {
|
||||
continue
|
||||
}
|
||||
if err := f.loadFixtures(tx, fixture); err != nil {
|
||||
return fmt.Errorf("failed to load fixtures from %s: %w", fixture.fileFullPath, err)
|
||||
}
|
||||
}
|
||||
return tx.Commit()
|
||||
if err = tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
for xormTableName := range f.xormTableNames {
|
||||
if f.fixtures[xormTableName] == nil {
|
||||
_, _ = f.xormEngine.Exec("DELETE FROM `" + xormTableName + "`")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func FixturesFileFullPaths(dir string, files []string) ([]string, error) {
|
||||
func FixturesFileFullPaths(dir string, files []string) (map[string]*FixtureItem, error) {
|
||||
if files != nil && len(files) == 0 {
|
||||
return nil, nil // load nothing
|
||||
}
|
||||
@ -169,20 +181,25 @@ func FixturesFileFullPaths(dir string, files []string) ([]string, error) {
|
||||
files = append(files, e.Name())
|
||||
}
|
||||
}
|
||||
for i, file := range files {
|
||||
if !filepath.IsAbs(file) {
|
||||
files[i] = filepath.Join(dir, file)
|
||||
fixtureItems := map[string]*FixtureItem{}
|
||||
for _, file := range files {
|
||||
fileFillPath := file
|
||||
if !filepath.IsAbs(fileFillPath) {
|
||||
fileFillPath = filepath.Join(dir, file)
|
||||
}
|
||||
tableName, _, _ := strings.Cut(filepath.Base(file), ".")
|
||||
fixtureItems[tableName] = &FixtureItem{fileFullPath: fileFillPath, tableName: tableName}
|
||||
}
|
||||
return files, nil
|
||||
return fixtureItems, nil
|
||||
}
|
||||
|
||||
func NewFixturesLoader(x *xorm.Engine, opts FixturesOptions) (FixturesLoader, error) {
|
||||
files, err := FixturesFileFullPaths(opts.Dir, opts.Files)
|
||||
fixtureItems, err := FixturesFileFullPaths(opts.Dir, opts.Files)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get fixtures files: %w", err)
|
||||
}
|
||||
f := &fixturesLoaderInternal{db: x.DB().DB, dbType: x.Dialect().URI().DBType, files: files, fixtures: map[string]*fixtureItem{}}
|
||||
|
||||
f := &fixturesLoaderInternal{xormEngine: x, db: x.DB().DB, dbType: x.Dialect().URI().DBType, fixtures: fixtureItems}
|
||||
switch f.dbType {
|
||||
case schemas.SQLITE:
|
||||
f.quoteObject = func(s string) string { return fmt.Sprintf(`"%s"`, s) }
|
||||
@ -197,5 +214,12 @@ func NewFixturesLoader(x *xorm.Engine, opts FixturesOptions) (FixturesLoader, er
|
||||
f.quoteObject = func(s string) string { return fmt.Sprintf("[%s]", s) }
|
||||
f.paramPlaceholder = func(idx int) string { return "?" }
|
||||
}
|
||||
|
||||
xormBeans, _ := db.NamesToBean()
|
||||
f.xormTableNames = map[string]bool{}
|
||||
for _, bean := range xormBeans {
|
||||
f.xormTableNames[db.TableName(bean)] = true
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ const (
|
||||
SettingsKeyDiffWhitespaceBehavior = "diff.whitespace_behaviour"
|
||||
// SettingsKeyShowOutdatedComments is the setting key wether or not to show outdated comments in PRs
|
||||
SettingsKeyShowOutdatedComments = "comment_code.show_outdated"
|
||||
|
||||
// UserActivityPubPrivPem is user's private key
|
||||
UserActivityPubPrivPem = "activitypub.priv_pem"
|
||||
// UserActivityPubPubPem is user's public key
|
||||
@ -18,4 +19,6 @@ const (
|
||||
SignupIP = "signup.ip"
|
||||
// SignupUserAgent is the user agent that the user signed up with
|
||||
SignupUserAgent = "signup.user_agent"
|
||||
|
||||
SettingsKeyCodeViewShowFileTree = "code_view.show_file_tree"
|
||||
)
|
||||
|
@ -62,7 +62,7 @@ func (m *MaterialIconProvider) loadData() {
|
||||
log.Debug("Loaded material icon rules and SVG images")
|
||||
}
|
||||
|
||||
func (m *MaterialIconProvider) renderFileIconSVG(ctx reqctx.RequestContext, name, svg string) template.HTML {
|
||||
func (m *MaterialIconProvider) renderFileIconSVG(ctx reqctx.RequestContext, name, svg, extraClass string) template.HTML {
|
||||
data := ctx.GetData()
|
||||
renderedSVGs, _ := data["_RenderedSVGs"].(map[string]bool)
|
||||
if renderedSVGs == nil {
|
||||
@ -75,7 +75,7 @@ func (m *MaterialIconProvider) renderFileIconSVG(ctx reqctx.RequestContext, name
|
||||
panic("Invalid SVG icon")
|
||||
}
|
||||
svgID := "svg-mfi-" + name
|
||||
svgCommonAttrs := `class="svg fileicon" width="16" height="16" aria-hidden="true"`
|
||||
svgCommonAttrs := `class="svg git-entry-icon ` + extraClass + `" width="16" height="16" aria-hidden="true"`
|
||||
posOuterBefore := strings.IndexByte(svg, '>')
|
||||
if renderedSVGs[svgID] && posOuterBefore != -1 {
|
||||
return template.HTML(`<svg ` + svgCommonAttrs + `><use xlink:href="#` + svgID + `"></use></svg>`)
|
||||
@ -92,7 +92,8 @@ func (m *MaterialIconProvider) FileIcon(ctx reqctx.RequestContext, entry *git.Tr
|
||||
|
||||
if entry.IsLink() {
|
||||
if te, err := entry.FollowLink(); err == nil && te.IsDir() {
|
||||
return svg.RenderHTML("material-folder-symlink")
|
||||
// keep the old "octicon-xxx" class name to make some "theme plugin selector" could still work
|
||||
return svg.RenderHTML("material-folder-symlink", 16, "octicon-file-directory-symlink")
|
||||
}
|
||||
return svg.RenderHTML("octicon-file-symlink-file") // TODO: find some better icons for them
|
||||
}
|
||||
@ -100,10 +101,19 @@ func (m *MaterialIconProvider) FileIcon(ctx reqctx.RequestContext, entry *git.Tr
|
||||
name := m.findIconNameByGit(entry)
|
||||
if name == "folder" {
|
||||
// the material icon pack's "folder" icon doesn't look good, so use our built-in one
|
||||
return svg.RenderHTML("material-folder-generic")
|
||||
// keep the old "octicon-xxx" class name to make some "theme plugin selector" could still work
|
||||
return svg.RenderHTML("material-folder-generic", 16, "octicon-file-directory-fill")
|
||||
}
|
||||
if iconSVG, ok := m.svgs[name]; ok && iconSVG != "" {
|
||||
return m.renderFileIconSVG(ctx, name, iconSVG)
|
||||
// keep the old "octicon-xxx" class name to make some "theme plugin selector" could still work
|
||||
extraClass := "octicon-file"
|
||||
switch {
|
||||
case entry.IsDir():
|
||||
extraClass = "octicon-file-directory-fill"
|
||||
case entry.IsSubModule():
|
||||
extraClass = "octicon-file-submodule"
|
||||
}
|
||||
return m.renderFileIconSVG(ctx, name, iconSVG, extraClass)
|
||||
}
|
||||
return svg.RenderHTML("octicon-file")
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ type Command struct {
|
||||
args []string
|
||||
globalArgsLength int
|
||||
brokenArgs []string
|
||||
cmd *exec.Cmd // for debug purpose only
|
||||
}
|
||||
|
||||
func logArgSanitize(arg string) string {
|
||||
@ -314,6 +315,7 @@ func (c *Command) run(ctx context.Context, skip int, opts *RunOpts) error {
|
||||
startTime := time.Now()
|
||||
|
||||
cmd := exec.CommandContext(ctx, c.prog, c.args...)
|
||||
c.cmd = cmd // for debug purpose only
|
||||
if opts.Env == nil {
|
||||
cmd.Env = os.Environ()
|
||||
} else {
|
||||
|
@ -19,7 +19,7 @@ func ParseTreeEntries(data []byte) ([]*TreeEntry, error) {
|
||||
return parseTreeEntries(data, nil)
|
||||
}
|
||||
|
||||
// parseTreeEntries FIXME this function's design is not right, it should make the caller read all data into memory
|
||||
// parseTreeEntries FIXME this function's design is not right, it should not make the caller read all data into memory
|
||||
func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) {
|
||||
entries := make([]*TreeEntry, 0, bytes.Count(data, []byte{'\n'})+1)
|
||||
for pos := 0; pos < len(data); {
|
||||
|
@ -9,6 +9,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
@ -102,7 +104,7 @@ type CheckAttributeReader struct {
|
||||
|
||||
stdinReader io.ReadCloser
|
||||
stdinWriter *os.File
|
||||
stdOut attributeWriter
|
||||
stdOut *nulSeparatedAttributeWriter
|
||||
cmd *Command
|
||||
env []string
|
||||
ctx context.Context
|
||||
@ -152,7 +154,6 @@ func (c *CheckAttributeReader) Init(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run run cmd
|
||||
func (c *CheckAttributeReader) Run() error {
|
||||
defer func() {
|
||||
_ = c.stdinReader.Close()
|
||||
@ -176,7 +177,7 @@ func (c *CheckAttributeReader) Run() error {
|
||||
func (c *CheckAttributeReader) CheckPath(path string) (rs map[string]string, err error) {
|
||||
defer func() {
|
||||
if err != nil && err != c.ctx.Err() {
|
||||
log.Error("Unexpected error when checking path %s in %s. Error: %v", path, c.Repo.Path, err)
|
||||
log.Error("Unexpected error when checking path %s in %s, error: %v", path, filepath.Base(c.Repo.Path), err)
|
||||
}
|
||||
}()
|
||||
|
||||
@ -191,9 +192,31 @@ func (c *CheckAttributeReader) CheckPath(path string) (rs map[string]string, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reportTimeout := func() error {
|
||||
stdOutClosed := false
|
||||
select {
|
||||
case <-c.stdOut.closed:
|
||||
stdOutClosed = true
|
||||
default:
|
||||
}
|
||||
debugMsg := fmt.Sprintf("check path %q in repo %q", path, filepath.Base(c.Repo.Path))
|
||||
debugMsg += fmt.Sprintf(", stdOut: tmp=%q, pos=%d, closed=%v", string(c.stdOut.tmp), c.stdOut.pos, stdOutClosed)
|
||||
if c.cmd.cmd != nil {
|
||||
debugMsg += fmt.Sprintf(", process state: %q", c.cmd.cmd.ProcessState.String())
|
||||
}
|
||||
_ = c.Close()
|
||||
return fmt.Errorf("CheckPath timeout: %s", debugMsg)
|
||||
}
|
||||
|
||||
rs = make(map[string]string)
|
||||
for range c.Attributes {
|
||||
select {
|
||||
case <-time.After(5 * time.Second):
|
||||
// There is a strange "hang" problem in gitdiff.GetDiff -> CheckPath
|
||||
// So add a timeout here to mitigate the problem, and output more logs for debug purpose
|
||||
// In real world, if CheckPath runs long than seconds, it blocks the end user's operation,
|
||||
// and at the moment the CheckPath result is not so important, so we can just ignore it.
|
||||
return nil, reportTimeout()
|
||||
case attr, ok := <-c.stdOut.ReadAttribute():
|
||||
if !ok {
|
||||
return nil, c.ctx.Err()
|
||||
@ -206,18 +229,12 @@ func (c *CheckAttributeReader) CheckPath(path string) (rs map[string]string, err
|
||||
return rs, nil
|
||||
}
|
||||
|
||||
// Close close pip after use
|
||||
func (c *CheckAttributeReader) Close() error {
|
||||
c.cancel()
|
||||
err := c.stdinWriter.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
type attributeWriter interface {
|
||||
io.WriteCloser
|
||||
ReadAttribute() <-chan attributeTriple
|
||||
}
|
||||
|
||||
type attributeTriple struct {
|
||||
Filename string
|
||||
Attribute string
|
||||
@ -281,7 +298,7 @@ func (wr *nulSeparatedAttributeWriter) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a check attribute reader for the current repository and provided commit ID
|
||||
// CheckAttributeReader creates a check attribute reader for the current repository and provided commit ID
|
||||
func (repo *Repository) CheckAttributeReader(commitID string) (*CheckAttributeReader, context.CancelFunc) {
|
||||
indexFilename, worktree, deleteTemporaryFile, err := repo.ReadTreeToTemporaryIndex(commitID)
|
||||
if err != nil {
|
||||
@ -303,21 +320,21 @@ func (repo *Repository) CheckAttributeReader(commitID string) (*CheckAttributeRe
|
||||
}
|
||||
ctx, cancel := context.WithCancel(repo.Ctx)
|
||||
if err := checker.Init(ctx); err != nil {
|
||||
log.Error("Unable to open checker for %s. Error: %v", commitID, err)
|
||||
log.Error("Unable to open attribute checker for commit %s, error: %v", commitID, err)
|
||||
} else {
|
||||
go func() {
|
||||
err := checker.Run()
|
||||
if err != nil && err != ctx.Err() {
|
||||
log.Error("Unable to open checker for %s. Error: %v", commitID, err)
|
||||
if err != nil && !IsErrCanceledOrKilled(err) {
|
||||
log.Error("Attribute checker for commit %s exits with error: %v", commitID, err)
|
||||
}
|
||||
cancel()
|
||||
}()
|
||||
}
|
||||
deferable := func() {
|
||||
deferrable := func() {
|
||||
_ = checker.Close()
|
||||
cancel()
|
||||
deleteTemporaryFile()
|
||||
}
|
||||
|
||||
return checker, deferable
|
||||
return checker, deferrable
|
||||
}
|
||||
|
@ -4,10 +4,16 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
mathRand "math/rand/v2"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_nulSeparatedAttributeWriter_ReadAttribute(t *testing.T) {
|
||||
@ -95,3 +101,57 @@ func Test_nulSeparatedAttributeWriter_ReadAttribute(t *testing.T) {
|
||||
Value: "unspecified",
|
||||
}, attr)
|
||||
}
|
||||
|
||||
func TestAttributeReader(t *testing.T) {
|
||||
t.Skip() // for debug purpose only, do not run in CI
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
timeout := 1 * time.Second
|
||||
repoPath := filepath.Join(testReposDir, "language_stats_repo")
|
||||
commitRef := "HEAD"
|
||||
|
||||
oneRound := func(t *testing.T, roundIdx int) {
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
_ = cancel
|
||||
gitRepo, err := OpenRepository(ctx, repoPath)
|
||||
require.NoError(t, err)
|
||||
defer gitRepo.Close()
|
||||
|
||||
commit, err := gitRepo.GetCommit(commitRef)
|
||||
require.NoError(t, err)
|
||||
|
||||
files, err := gitRepo.LsFiles()
|
||||
require.NoError(t, err)
|
||||
|
||||
randomFiles := slices.Clone(files)
|
||||
randomFiles = append(randomFiles, "any-file-1", "any-file-2")
|
||||
|
||||
t.Logf("Round %v with %d files", roundIdx, len(randomFiles))
|
||||
|
||||
attrReader, deferrable := gitRepo.CheckAttributeReader(commit.ID.String())
|
||||
defer deferrable()
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
file := randomFiles[mathRand.IntN(len(randomFiles))]
|
||||
_, err := attrReader.CheckPath(file)
|
||||
if err != nil {
|
||||
for i := 0; i < 10; i++ {
|
||||
_, _ = attrReader.CheckPath(file)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
oneRound(t, i)
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
@ -17,11 +16,6 @@ import (
|
||||
// TagPrefix tags prefix path on the repository
|
||||
const TagPrefix = "refs/tags/"
|
||||
|
||||
// IsTagExist returns true if given tag exists in the repository.
|
||||
func IsTagExist(ctx context.Context, repoPath, name string) bool {
|
||||
return IsReferenceExist(ctx, repoPath, TagPrefix+name)
|
||||
}
|
||||
|
||||
// CreateTag create one tag in the repository
|
||||
func (repo *Repository) CreateTag(name, revision string) error {
|
||||
_, _, err := NewCommand("tag").AddDashesAndList(name, revision).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
|
||||
|
@ -21,6 +21,7 @@ func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) {
|
||||
return &TreeEntry{
|
||||
ID: t.ID,
|
||||
// Type: ObjectTree,
|
||||
ptree: t,
|
||||
gogitTreeEntry: &object.TreeEntry{
|
||||
Name: "",
|
||||
Mode: filemode.Dir,
|
||||
|
@ -47,3 +47,21 @@ func GetDefaultBranch(ctx context.Context, repo Repository) (string, error) {
|
||||
func GetWikiDefaultBranch(ctx context.Context, repo Repository) (string, error) {
|
||||
return git.GetDefaultBranch(ctx, wikiPath(repo))
|
||||
}
|
||||
|
||||
// IsReferenceExist returns true if given reference exists in the repository.
|
||||
func IsReferenceExist(ctx context.Context, repo Repository, name string) bool {
|
||||
return git.IsReferenceExist(ctx, repoPath(repo), name)
|
||||
}
|
||||
|
||||
func IsWikiReferenceExist(ctx context.Context, repo Repository, name string) bool {
|
||||
return git.IsReferenceExist(ctx, wikiPath(repo), name)
|
||||
}
|
||||
|
||||
// IsBranchExist returns true if given branch exists in the repository.
|
||||
func IsBranchExist(ctx context.Context, repo Repository, name string) bool {
|
||||
return IsReferenceExist(ctx, repo, git.BranchPrefix+name)
|
||||
}
|
||||
|
||||
func IsWikiBranchExist(ctx context.Context, repo Repository, name string) bool {
|
||||
return IsWikiReferenceExist(ctx, repo, git.BranchPrefix+name)
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ package gitrepo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@ -20,8 +21,12 @@ type Repository interface {
|
||||
GetOwnerName() string
|
||||
}
|
||||
|
||||
func absPath(owner, name string) string {
|
||||
return filepath.Join(setting.RepoRootPath, strings.ToLower(owner), strings.ToLower(name)+".git")
|
||||
}
|
||||
|
||||
func repoPath(repo Repository) string {
|
||||
return filepath.Join(setting.RepoRootPath, strings.ToLower(repo.GetOwnerName()), strings.ToLower(repo.GetName())+".git")
|
||||
return absPath(repo.GetOwnerName(), repo.GetName())
|
||||
}
|
||||
|
||||
func wikiPath(repo Repository) string {
|
||||
@ -74,3 +79,17 @@ func RepositoryFromRequestContextOrOpen(ctx reqctx.RequestContext, repo Reposito
|
||||
func IsRepositoryExist(ctx context.Context, repo Repository) (bool, error) {
|
||||
return util.IsExist(repoPath(repo))
|
||||
}
|
||||
|
||||
// DeleteRepository deletes the repository directory from the disk
|
||||
func DeleteRepository(ctx context.Context, repo Repository) error {
|
||||
return util.RemoveAll(repoPath(repo))
|
||||
}
|
||||
|
||||
// RenameRepository renames a repository's name on disk
|
||||
func RenameRepository(ctx context.Context, repo Repository, newName string) error {
|
||||
newRepoPath := absPath(repo.GetOwnerName(), newName)
|
||||
if err := util.Rename(repoPath(repo), newRepoPath); err != nil {
|
||||
return fmt.Errorf("rename repository directory: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package repository
|
||||
package gitrepo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -105,10 +106,18 @@ done
|
||||
return hookNames, hookTpls, giteaHookTpls
|
||||
}
|
||||
|
||||
// CreateDelegateHooks creates all the hooks scripts for the repo
|
||||
func CreateDelegateHooks(repoPath string) (err error) {
|
||||
// CreateDelegateHooksForRepo creates all the hooks scripts for the repo
|
||||
func CreateDelegateHooksForRepo(_ context.Context, repo Repository) (err error) {
|
||||
return createDelegateHooks(filepath.Join(repoPath(repo), "hooks"))
|
||||
}
|
||||
|
||||
// CreateDelegateHooksForWiki creates all the hooks scripts for the wiki repo
|
||||
func CreateDelegateHooksForWiki(_ context.Context, repo Repository) (err error) {
|
||||
return createDelegateHooks(filepath.Join(wikiPath(repo), "hooks"))
|
||||
}
|
||||
|
||||
func createDelegateHooks(hookDir string) (err error) {
|
||||
hookNames, hookTpls, giteaHookTpls := getHookTemplates()
|
||||
hookDir := filepath.Join(repoPath, "hooks")
|
||||
|
||||
for i, hookName := range hookNames {
|
||||
oldHookPath := filepath.Join(hookDir, hookName)
|
||||
@ -169,11 +178,19 @@ func ensureExecutable(filename string) error {
|
||||
return os.Chmod(filename, mode)
|
||||
}
|
||||
|
||||
// CheckDelegateHooks checks the hooks scripts for the repo
|
||||
func CheckDelegateHooks(repoPath string) ([]string, error) {
|
||||
// CheckDelegateHooksForRepo checks the hooks scripts for the repo
|
||||
func CheckDelegateHooksForRepo(_ context.Context, repo Repository) ([]string, error) {
|
||||
return checkDelegateHooks(filepath.Join(repoPath(repo), "hooks"))
|
||||
}
|
||||
|
||||
// CheckDelegateHooksForWiki checks the hooks scripts for the repo
|
||||
func CheckDelegateHooksForWiki(_ context.Context, repo Repository) ([]string, error) {
|
||||
return checkDelegateHooks(filepath.Join(wikiPath(repo), "hooks"))
|
||||
}
|
||||
|
||||
func checkDelegateHooks(hookDir string) ([]string, error) {
|
||||
hookNames, hookTpls, giteaHookTpls := getHookTemplates()
|
||||
|
||||
hookDir := filepath.Join(repoPath, "hooks")
|
||||
results := make([]string, 0, 10)
|
||||
|
||||
for i, hookName := range hookNames {
|
15
modules/gitrepo/tag.go
Normal file
15
modules/gitrepo/tag.go
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package gitrepo
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
)
|
||||
|
||||
// IsTagExist returns true if given tag exists in the repository.
|
||||
func IsTagExist(ctx context.Context, repo Repository, name string) bool {
|
||||
return IsReferenceExist(ctx, repo, git.TagPrefix+name)
|
||||
}
|
@ -25,6 +25,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/typesniffer"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/blevesearch/bleve/v2"
|
||||
analyzer_custom "github.com/blevesearch/bleve/v2/analysis/analyzer/custom"
|
||||
@ -272,14 +273,18 @@ func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int
|
||||
pathQuery.FieldVal = "Filename"
|
||||
pathQuery.SetBoost(10)
|
||||
|
||||
if opts.SearchMode == indexer.SearchModeExact {
|
||||
searchMode := util.IfZero(opts.SearchMode, b.SupportedSearchModes()[0].ModeValue)
|
||||
if searchMode == indexer.SearchModeExact {
|
||||
// 1.21 used NewPrefixQuery, but it seems not working well, and later releases changed to NewMatchPhraseQuery
|
||||
q := bleve.NewMatchPhraseQuery(opts.Keyword)
|
||||
q.Analyzer = repoIndexerAnalyzer
|
||||
q.FieldVal = "Content"
|
||||
contentQuery = q
|
||||
} else /* words */ {
|
||||
q := bleve.NewMatchQuery(opts.Keyword)
|
||||
q.FieldVal = "Content"
|
||||
if opts.SearchMode == indexer.SearchModeFuzzy {
|
||||
q.Analyzer = repoIndexerAnalyzer
|
||||
if searchMode == indexer.SearchModeFuzzy {
|
||||
// this logic doesn't seem right, it is only used to pass the test-case `Keyword: "dESCRIPTION"`, which doesn't seem to be a real-life use-case.
|
||||
q.Fuzziness = inner_bleve.GuessFuzzinessByKeyword(opts.Keyword)
|
||||
} else {
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/typesniffer"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/go-enry/go-enry/v2"
|
||||
"github.com/olivere/elastic/v7"
|
||||
@ -365,7 +366,9 @@ func extractAggs(searchResult *elastic.SearchResult) []*internal.SearchResultLan
|
||||
// Search searches for codes and language stats by given conditions.
|
||||
func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int64, []*internal.SearchResult, []*internal.SearchResultLanguages, error) {
|
||||
var contentQuery elastic.Query
|
||||
if opts.SearchMode == indexer.SearchModeExact {
|
||||
searchMode := util.IfZero(opts.SearchMode, b.SupportedSearchModes()[0].ModeValue)
|
||||
if searchMode == indexer.SearchModeExact {
|
||||
// 1.21 used NewMultiMatchQuery().Type(esMultiMatchTypePhrasePrefix), but later releases changed to NewMatchPhraseQuery
|
||||
contentQuery = elastic.NewMatchPhraseQuery("content", opts.Keyword)
|
||||
} else /* words */ {
|
||||
contentQuery = elastic.NewMultiMatchQuery("content", opts.Keyword).Type(esMultiMatchTypeBestFields).Operator("and")
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/indexer/code/internal"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
_ "code.gitea.io/gitea/models"
|
||||
_ "code.gitea.io/gitea/models/actions"
|
||||
@ -240,7 +241,7 @@ func testIndexer(name string, t *testing.T, indexer internal.Indexer) {
|
||||
total, res, langs, err := indexer.Search(t.Context(), &internal.SearchOptions{
|
||||
RepoIDs: kw.RepoIDs,
|
||||
Keyword: kw.Keyword,
|
||||
SearchMode: kw.SearchMode,
|
||||
SearchMode: util.IfZero(kw.SearchMode, indexer_module.SearchModeWords),
|
||||
Paginator: &db.ListOptions{
|
||||
Page: 1,
|
||||
PageSize: 10,
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
indexer_internal "code.gitea.io/gitea/modules/indexer/internal"
|
||||
inner_bleve "code.gitea.io/gitea/modules/indexer/internal/bleve"
|
||||
"code.gitea.io/gitea/modules/indexer/issues/internal"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/blevesearch/bleve/v2"
|
||||
"github.com/blevesearch/bleve/v2/analysis/analyzer/custom"
|
||||
@ -162,9 +163,10 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
||||
var queries []query.Query
|
||||
|
||||
if options.Keyword != "" {
|
||||
if options.SearchMode == indexer.SearchModeWords || options.SearchMode == indexer.SearchModeFuzzy {
|
||||
searchMode := util.IfZero(options.SearchMode, b.SupportedSearchModes()[0].ModeValue)
|
||||
if searchMode == indexer.SearchModeWords || searchMode == indexer.SearchModeFuzzy {
|
||||
fuzziness := 0
|
||||
if options.SearchMode == indexer.SearchModeFuzzy {
|
||||
if searchMode == indexer.SearchModeFuzzy {
|
||||
fuzziness = inner_bleve.GuessFuzzinessByKeyword(options.Keyword)
|
||||
}
|
||||
queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
indexer_internal "code.gitea.io/gitea/modules/indexer/internal"
|
||||
inner_db "code.gitea.io/gitea/modules/indexer/internal/db"
|
||||
"code.gitea.io/gitea/modules/indexer/issues/internal"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
@ -46,7 +47,7 @@ func (i *Indexer) Delete(_ context.Context, _ ...int64) error {
|
||||
|
||||
func buildMatchQuery(mode indexer.SearchModeType, colName, keyword string) builder.Cond {
|
||||
if mode == indexer.SearchModeExact {
|
||||
return db.BuildCaseInsensitiveLike("issue.name", keyword)
|
||||
return db.BuildCaseInsensitiveLike(colName, keyword)
|
||||
}
|
||||
|
||||
// match words
|
||||
@ -84,16 +85,16 @@ func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
||||
repoCond = builder.Eq{"repo_id": options.RepoIDs[0]}
|
||||
}
|
||||
subQuery := builder.Select("id").From("issue").Where(repoCond)
|
||||
|
||||
searchMode := util.IfZero(options.SearchMode, i.SupportedSearchModes()[0].ModeValue)
|
||||
cond = builder.Or(
|
||||
buildMatchQuery(options.SearchMode, "issue.name", options.Keyword),
|
||||
buildMatchQuery(options.SearchMode, "issue.content", options.Keyword),
|
||||
buildMatchQuery(searchMode, "issue.name", options.Keyword),
|
||||
buildMatchQuery(searchMode, "issue.content", options.Keyword),
|
||||
builder.In("issue.id", builder.Select("issue_id").
|
||||
From("comment").
|
||||
Where(builder.And(
|
||||
builder.Eq{"type": issue_model.CommentTypeComment},
|
||||
builder.In("issue_id", subQuery),
|
||||
buildMatchQuery(options.SearchMode, "content", options.Keyword),
|
||||
buildMatchQuery(searchMode, "content", options.Keyword),
|
||||
)),
|
||||
),
|
||||
)
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
indexer_internal "code.gitea.io/gitea/modules/indexer/internal"
|
||||
inner_elasticsearch "code.gitea.io/gitea/modules/indexer/internal/elasticsearch"
|
||||
"code.gitea.io/gitea/modules/indexer/issues/internal"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/olivere/elastic/v7"
|
||||
)
|
||||
@ -152,7 +153,8 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
||||
query := elastic.NewBoolQuery()
|
||||
|
||||
if options.Keyword != "" {
|
||||
if options.SearchMode == indexer.SearchModeExact {
|
||||
searchMode := util.IfZero(options.SearchMode, b.SupportedSearchModes()[0].ModeValue)
|
||||
if searchMode == indexer.SearchModeExact {
|
||||
query.Must(elastic.NewMultiMatchQuery(options.Keyword, "title", "content", "comments").Type(esMultiMatchTypePhrasePrefix))
|
||||
} else /* words */ {
|
||||
query.Must(elastic.NewMultiMatchQuery(options.Keyword, "title", "content", "comments").Type(esMultiMatchTypeBestFields).Operator("and"))
|
||||
|
@ -282,7 +282,7 @@ const (
|
||||
|
||||
// SearchIssues search issues by options.
|
||||
func SearchIssues(ctx context.Context, opts *SearchOptions) ([]int64, int64, error) {
|
||||
indexer := *globalIndexer.Load()
|
||||
ix := *globalIndexer.Load()
|
||||
|
||||
if opts.Keyword == "" || opts.IsKeywordNumeric() {
|
||||
// This is a conservative shortcut.
|
||||
@ -291,10 +291,9 @@ func SearchIssues(ctx context.Context, opts *SearchOptions) ([]int64, int64, err
|
||||
// So if the user creates an issue and list issues immediately, the issue may not be listed because the indexer needs time to index the issue.
|
||||
// Even worse, the external indexer like elastic search may not be available for a while,
|
||||
// and the user may not be able to list issues completely until it is available again.
|
||||
indexer = db.NewIndexer()
|
||||
ix = db.NewIndexer()
|
||||
}
|
||||
|
||||
result, err := indexer.Search(ctx, opts)
|
||||
result, err := ix.Search(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
@ -82,9 +82,11 @@ func searchIssueWithKeyword(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
issueIDs, _, err := SearchIssues(t.Context(), &test.opts)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expectedIDs, issueIDs)
|
||||
t.Run(test.opts.Keyword, func(t *testing.T) {
|
||||
issueIDs, _, err := SearchIssues(t.Context(), &test.opts)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expectedIDs, issueIDs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,10 +49,10 @@ func (db *DBIndexer) Index(id int64) error {
|
||||
commitID, err := gitRepo.GetBranchCommitID(repo.DefaultBranch)
|
||||
if err != nil {
|
||||
if git.IsErrBranchNotExist(err) || git.IsErrNotExist(err) || setting.IsInTesting {
|
||||
log.Debug("Unable to get commit ID for default branch %s in %s ... skipping this repository", repo.DefaultBranch, repo.RepoPath())
|
||||
log.Debug("Unable to get commit ID for default branch %s in %s ... skipping this repository", repo.DefaultBranch, repo.FullName())
|
||||
return nil
|
||||
}
|
||||
log.Error("Unable to get commit ID for default branch %s in %s. Error: %v", repo.DefaultBranch, repo.RepoPath(), err)
|
||||
log.Error("Unable to get commit ID for default branch %s in %s. Error: %v", repo.DefaultBranch, repo.FullName(), err)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -65,17 +65,17 @@ func (db *DBIndexer) Index(id int64) error {
|
||||
stats, err := gitRepo.GetLanguageStats(commitID)
|
||||
if err != nil {
|
||||
if !setting.IsInTesting {
|
||||
log.Error("Unable to get language stats for ID %s for default branch %s in %s. Error: %v", commitID, repo.DefaultBranch, repo.RepoPath(), err)
|
||||
log.Error("Unable to get language stats for ID %s for default branch %s in %s. Error: %v", commitID, repo.DefaultBranch, repo.FullName(), err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
err = repo_model.UpdateLanguageStats(ctx, repo, commitID, stats)
|
||||
if err != nil {
|
||||
log.Error("Unable to update language stats for ID %s for default branch %s in %s. Error: %v", commitID, repo.DefaultBranch, repo.RepoPath(), err)
|
||||
log.Error("Unable to update language stats for ID %s for default branch %s in %s. Error: %v", commitID, repo.DefaultBranch, repo.FullName(), err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debug("DBIndexer completed language stats for ID %s for default branch %s in %s. stats count: %d", commitID, repo.DefaultBranch, repo.RepoPath(), len(stats))
|
||||
log.Debug("DBIndexer completed language stats for ID %s for default branch %s in %s. stats count: %d", commitID, repo.DefaultBranch, repo.FullName(), len(stats))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/avatars"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
@ -43,7 +44,7 @@ func NewPushCommits() *PushCommits {
|
||||
}
|
||||
|
||||
// ToAPIPayloadCommit converts a single PushCommit to an api.PayloadCommit object.
|
||||
func ToAPIPayloadCommit(ctx context.Context, emailUsers map[string]*user_model.User, repoPath, repoLink string, commit *PushCommit) (*api.PayloadCommit, error) {
|
||||
func ToAPIPayloadCommit(ctx context.Context, emailUsers map[string]*user_model.User, repo *repo_model.Repository, commit *PushCommit) (*api.PayloadCommit, error) {
|
||||
var err error
|
||||
authorUsername := ""
|
||||
author, ok := emailUsers[commit.AuthorEmail]
|
||||
@ -70,7 +71,7 @@ func ToAPIPayloadCommit(ctx context.Context, emailUsers map[string]*user_model.U
|
||||
committerUsername = committer.Name
|
||||
}
|
||||
|
||||
fileStatus, err := git.GetCommitFileStatus(ctx, repoPath, commit.Sha1)
|
||||
fileStatus, err := git.GetCommitFileStatus(ctx, repo.RepoPath(), commit.Sha1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("FileStatus [commit_sha1: %s]: %w", commit.Sha1, err)
|
||||
}
|
||||
@ -78,7 +79,7 @@ func ToAPIPayloadCommit(ctx context.Context, emailUsers map[string]*user_model.U
|
||||
return &api.PayloadCommit{
|
||||
ID: commit.Sha1,
|
||||
Message: commit.Message,
|
||||
URL: fmt.Sprintf("%s/commit/%s", repoLink, url.PathEscape(commit.Sha1)),
|
||||
URL: fmt.Sprintf("%s/commit/%s", repo.HTMLURL(), url.PathEscape(commit.Sha1)),
|
||||
Author: &api.PayloadUser{
|
||||
Name: commit.AuthorName,
|
||||
Email: commit.AuthorEmail,
|
||||
@ -98,14 +99,14 @@ func ToAPIPayloadCommit(ctx context.Context, emailUsers map[string]*user_model.U
|
||||
|
||||
// ToAPIPayloadCommits converts a PushCommits object to api.PayloadCommit format.
|
||||
// It returns all converted commits and, if provided, the head commit or an error otherwise.
|
||||
func (pc *PushCommits) ToAPIPayloadCommits(ctx context.Context, repoPath, repoLink string) ([]*api.PayloadCommit, *api.PayloadCommit, error) {
|
||||
func (pc *PushCommits) ToAPIPayloadCommits(ctx context.Context, repo *repo_model.Repository) ([]*api.PayloadCommit, *api.PayloadCommit, error) {
|
||||
commits := make([]*api.PayloadCommit, len(pc.Commits))
|
||||
var headCommit *api.PayloadCommit
|
||||
|
||||
emailUsers := make(map[string]*user_model.User)
|
||||
|
||||
for i, commit := range pc.Commits {
|
||||
apiCommit, err := ToAPIPayloadCommit(ctx, emailUsers, repoPath, repoLink, commit)
|
||||
apiCommit, err := ToAPIPayloadCommit(ctx, emailUsers, repo, commit)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -117,7 +118,7 @@ func (pc *PushCommits) ToAPIPayloadCommits(ctx context.Context, repoPath, repoLi
|
||||
}
|
||||
if pc.HeadCommit != nil && headCommit == nil {
|
||||
var err error
|
||||
headCommit, err = ToAPIPayloadCommit(ctx, emailUsers, repoPath, repoLink, pc.HeadCommit)
|
||||
headCommit, err = ToAPIPayloadCommit(ctx, emailUsers, repo, pc.HeadCommit)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -50,14 +50,14 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) {
|
||||
pushCommits.HeadCommit = &PushCommit{Sha1: "69554a6"}
|
||||
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16})
|
||||
payloadCommits, headCommit, err := pushCommits.ToAPIPayloadCommits(git.DefaultContext, repo.RepoPath(), "/user2/repo16")
|
||||
payloadCommits, headCommit, err := pushCommits.ToAPIPayloadCommits(git.DefaultContext, repo)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, payloadCommits, 3)
|
||||
assert.NotNil(t, headCommit)
|
||||
|
||||
assert.Equal(t, "69554a6", payloadCommits[0].ID)
|
||||
assert.Equal(t, "not signed commit", payloadCommits[0].Message)
|
||||
assert.Equal(t, "/user2/repo16/commit/69554a6", payloadCommits[0].URL)
|
||||
assert.Equal(t, "https://try.gitea.io/user2/repo16/commit/69554a6", payloadCommits[0].URL)
|
||||
assert.Equal(t, "User2", payloadCommits[0].Committer.Name)
|
||||
assert.Equal(t, "user2", payloadCommits[0].Committer.UserName)
|
||||
assert.Equal(t, "User2", payloadCommits[0].Author.Name)
|
||||
@ -68,7 +68,7 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) {
|
||||
|
||||
assert.Equal(t, "27566bd", payloadCommits[1].ID)
|
||||
assert.Equal(t, "good signed commit (with not yet validated email)", payloadCommits[1].Message)
|
||||
assert.Equal(t, "/user2/repo16/commit/27566bd", payloadCommits[1].URL)
|
||||
assert.Equal(t, "https://try.gitea.io/user2/repo16/commit/27566bd", payloadCommits[1].URL)
|
||||
assert.Equal(t, "User2", payloadCommits[1].Committer.Name)
|
||||
assert.Equal(t, "user2", payloadCommits[1].Committer.UserName)
|
||||
assert.Equal(t, "User2", payloadCommits[1].Author.Name)
|
||||
@ -79,7 +79,7 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) {
|
||||
|
||||
assert.Equal(t, "5099b81", payloadCommits[2].ID)
|
||||
assert.Equal(t, "good signed commit", payloadCommits[2].Message)
|
||||
assert.Equal(t, "/user2/repo16/commit/5099b81", payloadCommits[2].URL)
|
||||
assert.Equal(t, "https://try.gitea.io/user2/repo16/commit/5099b81", payloadCommits[2].URL)
|
||||
assert.Equal(t, "User2", payloadCommits[2].Committer.Name)
|
||||
assert.Equal(t, "user2", payloadCommits[2].Committer.UserName)
|
||||
assert.Equal(t, "User2", payloadCommits[2].Author.Name)
|
||||
@ -90,7 +90,7 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) {
|
||||
|
||||
assert.Equal(t, "69554a6", headCommit.ID)
|
||||
assert.Equal(t, "not signed commit", headCommit.Message)
|
||||
assert.Equal(t, "/user2/repo16/commit/69554a6", headCommit.URL)
|
||||
assert.Equal(t, "https://try.gitea.io/user2/repo16/commit/69554a6", headCommit.URL)
|
||||
assert.Equal(t, "User2", headCommit.Committer.Name)
|
||||
assert.Equal(t, "user2", headCommit.Committer.UserName)
|
||||
assert.Equal(t, "User2", headCommit.Author.Name)
|
||||
|
@ -138,7 +138,7 @@ func CheckInitRepository(ctx context.Context, repo *repo_model.Repository) (err
|
||||
// Init git bare new repository.
|
||||
if err = git.InitRepository(ctx, repo.RepoPath(), true, repo.ObjectFormatName); err != nil {
|
||||
return fmt.Errorf("git.InitRepository: %w", err)
|
||||
} else if err = CreateDelegateHooks(repo.RepoPath()); err != nil {
|
||||
} else if err = gitrepo.CreateDelegateHooksForRepo(ctx, repo); err != nil {
|
||||
return fmt.Errorf("createDelegateHooks: %w", err)
|
||||
}
|
||||
return nil
|
||||
|
@ -10,6 +10,8 @@ import "time"
|
||||
type Secret struct {
|
||||
// the secret's name
|
||||
Name string `json:"name"`
|
||||
// the secret's description
|
||||
Description string `json:"description"`
|
||||
// swagger:strfmt date-time
|
||||
Created time.Time `json:"created_at"`
|
||||
}
|
||||
@ -21,4 +23,9 @@ type CreateOrUpdateSecretOption struct {
|
||||
//
|
||||
// required: true
|
||||
Data string `json:"data" binding:"Required"`
|
||||
|
||||
// Description of the secret to update
|
||||
//
|
||||
// required: false
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
@ -10,6 +10,11 @@ type CreateVariableOption struct {
|
||||
//
|
||||
// required: true
|
||||
Value string `json:"value" binding:"Required"`
|
||||
|
||||
// Description of the variable to create
|
||||
//
|
||||
// required: false
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// UpdateVariableOption the option when updating variable
|
||||
@ -21,6 +26,11 @@ type UpdateVariableOption struct {
|
||||
//
|
||||
// required: true
|
||||
Value string `json:"value" binding:"Required"`
|
||||
|
||||
// Description of the variable to update
|
||||
//
|
||||
// required: false
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// ActionVariable return value of the query API
|
||||
@ -34,4 +44,6 @@ type ActionVariable struct {
|
||||
Name string `json:"name"`
|
||||
// the value of the variable
|
||||
Data string `json:"data"`
|
||||
// the description of the variable
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
@ -113,6 +113,7 @@ copy_type_unsupported = This file type cannot be copied
|
||||
write = Write
|
||||
preview = Preview
|
||||
loading = Loading…
|
||||
files = Files
|
||||
|
||||
error = Error
|
||||
error404 = The page you are trying to reach either <strong>does not exist</strong> or <strong>you are not authorized</strong> to view it.
|
||||
@ -3711,8 +3712,10 @@ secrets = Secrets
|
||||
description = Secrets will be passed to certain actions and cannot be read otherwise.
|
||||
none = There are no secrets yet.
|
||||
creation = Add Secret
|
||||
creation.description = Description
|
||||
creation.name_placeholder = case-insensitive, alphanumeric characters or underscores only, cannot start with GITEA_ or GITHUB_
|
||||
creation.value_placeholder = Input any content. Whitespace at the start and end will be omitted.
|
||||
creation.description_placeholder = Enter short description (optional).
|
||||
creation.success = The secret "%s" has been added.
|
||||
creation.failed = Failed to add secret.
|
||||
deletion = Remove secret
|
||||
|
@ -113,6 +113,7 @@ copy_type_unsupported=Este tipo de ficheiro não pode ser copiado
|
||||
write=Escrever
|
||||
preview=Pré-visualizar
|
||||
loading=Carregando…
|
||||
files=Ficheiros
|
||||
|
||||
error=Erro
|
||||
error404=A página que pretende aceder <strong>não existe</strong> ou <strong>não tem autorização</strong> para a ver.
|
||||
@ -169,6 +170,10 @@ search=Pesquisar...
|
||||
type_tooltip=Tipo de pesquisa
|
||||
fuzzy=Aproximada
|
||||
fuzzy_tooltip=Incluir também os resultados que estejam próximos do termo de pesquisa
|
||||
words=Palavras
|
||||
words_tooltip=Incluir apenas os resultados que correspondam às palavras do termo de pesquisa
|
||||
regexp=Regexp
|
||||
regexp_tooltip=Incluir apenas os resultados que correspondam ao termo de pesquisa com expressões regulares
|
||||
exact=Fiel
|
||||
exact_tooltip=Incluir somente os resultados que correspondam rigorosamente ao termo de pesquisa
|
||||
repo_kind=Pesquisar repositórios...
|
||||
@ -1403,6 +1408,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
|
||||
commits.view_path=Vista neste ponto do histórico
|
||||
commits.view_file_diff=Ver as modificações deste ficheiro neste cometimento
|
||||
|
||||
commit.operations=Operações
|
||||
commit.revert=Reverter
|
||||
@ -1937,6 +1943,7 @@ pulls.outdated_with_base_branch=Este ramo é obsoleto em relação ao ramo base
|
||||
pulls.close=Encerrar pedido de integração
|
||||
pulls.closed_at=`fechou este pedido de integração <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
pulls.reopened_at=`reabriu este pedido de integração <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
pulls.cmd_instruction_hint=Ver instruções para a linha de comandos
|
||||
pulls.cmd_instruction_checkout_title=Checkout
|
||||
pulls.cmd_instruction_checkout_desc=A partir do seu repositório, crie um novo ramo e teste nele as modificações.
|
||||
pulls.cmd_instruction_merge_title=Integrar
|
||||
|
@ -96,8 +96,8 @@ edit=编辑
|
||||
view=查看
|
||||
test=测试
|
||||
|
||||
enabled=启用
|
||||
disabled=禁用
|
||||
enabled=已启用
|
||||
disabled=已禁用
|
||||
locked=已锁定
|
||||
|
||||
copy=复制
|
||||
@ -148,7 +148,7 @@ name=名称
|
||||
value=值
|
||||
readme=自述文档
|
||||
|
||||
filter=过滤
|
||||
filter=筛选
|
||||
filter.clear=清除筛选器
|
||||
filter.is_archived=已归档
|
||||
filter.not_archived=非存档
|
||||
@ -159,7 +159,7 @@ filter.not_mirror=非镜像
|
||||
filter.is_template=模板
|
||||
filter.not_template=非模板
|
||||
filter.public=公开
|
||||
filter.private=私有库
|
||||
filter.private=私有
|
||||
|
||||
no_results_found=未找到结果
|
||||
internal_error_skipped=发生内部错误,但已被跳过: %s
|
||||
@ -245,6 +245,7 @@ license_desc=所有的代码都开源在 <a target="_blank" rel="noopener norefe
|
||||
|
||||
[install]
|
||||
install=安装页面
|
||||
installing_desc=正在安装,请稍候...
|
||||
title=初始配置
|
||||
docker_helper=如果您正在使用 Docker 容器运行 Gitea,请务必先仔细阅读 <a target="_blank" rel="noopener noreferrer" href="%s">官方文档</a> 后再对本页面进行填写。
|
||||
require_db_desc=Gitea 需要使用 MySQL、PostgreSQL、MSSQL、SQLite3 或 TiDB (MySQL协议) 等数据库
|
||||
@ -2870,7 +2871,7 @@ authentication=认证源
|
||||
emails=用户邮件
|
||||
config=应用配置
|
||||
config_summary=摘要
|
||||
config_settings=组织设置
|
||||
config_settings=设置
|
||||
notices=系统提示
|
||||
monitor=监控面板
|
||||
first_page=首页
|
||||
@ -3349,6 +3350,7 @@ monitor.previous=上次执行时间
|
||||
monitor.execute_times=执行次数
|
||||
monitor.process=运行中进程
|
||||
monitor.stacktrace=调用栈踪迹
|
||||
monitor.performance_logs=性能日志
|
||||
monitor.processes_count=%d 个进程
|
||||
monitor.download_diagnosis_report=下载诊断报告
|
||||
monitor.desc=进程描述
|
||||
@ -3529,12 +3531,12 @@ alpine.registry.info=从下面的列表中选择 $branch 和 $repository。
|
||||
alpine.install=要安装包,请运行以下命令:
|
||||
alpine.repository=仓库信息
|
||||
alpine.repository.branches=分支
|
||||
alpine.repository.repositories=仓库管理
|
||||
alpine.repository.repositories=仓库
|
||||
alpine.repository.architectures=架构
|
||||
arch.registry=添加具有相关仓库和架构的服务器到 <code>/etc/pacman.conf</code> 中:
|
||||
arch.install=使用 pacman 同步软件包:
|
||||
arch.repository=仓库信息
|
||||
arch.repository.repositories=仓库管理
|
||||
arch.repository.repositories=仓库
|
||||
arch.repository.architectures=架构
|
||||
cargo.registry=在 Cargo 配置文件中设置此注册中心(例如:<code>~/.cargo/config.toml</code>):
|
||||
cargo.install=要使用 Cargo 安装软件包,请运行以下命令:
|
||||
@ -3552,6 +3554,7 @@ conda.install=要使用 Conda 安装软件包,请运行以下命令:
|
||||
container.details.type=镜像类型
|
||||
container.details.platform=平台
|
||||
container.pull=从命令行拉取镜像:
|
||||
container.images=镜像
|
||||
container.multi_arch=OS / Arch
|
||||
container.layers=镜像层
|
||||
container.labels=标签
|
||||
|
2322
package-lock.json
generated
2322
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
50
package.json
50
package.json
@ -12,12 +12,12 @@
|
||||
"@github/relative-time-element": "4.4.5",
|
||||
"@github/text-expander-element": "2.9.1",
|
||||
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
|
||||
"@primer/octicons": "19.15.0",
|
||||
"@primer/octicons": "19.15.1",
|
||||
"@silverwind/vue3-calendar-heatmap": "2.0.6",
|
||||
"add-asset-webpack-plugin": "3.0.0",
|
||||
"ansi_up": "6.0.2",
|
||||
"asciinema-player": "3.9.0",
|
||||
"chart.js": "4.4.7",
|
||||
"chart.js": "4.4.8",
|
||||
"chartjs-adapter-dayjs-4": "1.0.4",
|
||||
"chartjs-plugin-zoom": "2.2.0",
|
||||
"clippie": "4.1.5",
|
||||
@ -25,34 +25,34 @@
|
||||
"css-loader": "7.1.2",
|
||||
"dayjs": "1.11.13",
|
||||
"dropzone": "6.0.0-beta.2",
|
||||
"easymde": "2.18.0",
|
||||
"easymde": "2.20.0",
|
||||
"esbuild-loader": "4.3.0",
|
||||
"escape-goat": "4.0.0",
|
||||
"fast-glob": "3.3.3",
|
||||
"htmx.org": "2.0.4",
|
||||
"idiomorph": "0.4.0",
|
||||
"idiomorph": "0.7.3",
|
||||
"jquery": "3.7.1",
|
||||
"katex": "0.16.21",
|
||||
"license-checker-webpack-plugin": "0.2.1",
|
||||
"mermaid": "11.4.1",
|
||||
"mermaid": "11.5.0",
|
||||
"mini-css-extract-plugin": "2.9.2",
|
||||
"minimatch": "10.0.1",
|
||||
"monaco-editor": "0.52.2",
|
||||
"monaco-editor-webpack-plugin": "7.1.0",
|
||||
"pdfobject": "2.3.1",
|
||||
"perfect-debounce": "1.0.0",
|
||||
"postcss": "8.5.2",
|
||||
"postcss": "8.5.3",
|
||||
"postcss-loader": "8.1.1",
|
||||
"postcss-nesting": "13.0.1",
|
||||
"sortablejs": "1.15.6",
|
||||
"swagger-ui-dist": "5.18.3",
|
||||
"swagger-ui-dist": "5.20.1",
|
||||
"tailwindcss": "3.4.17",
|
||||
"throttle-debounce": "5.0.2",
|
||||
"tinycolor2": "1.6.0",
|
||||
"tippy.js": "6.3.7",
|
||||
"toastify-js": "1.12.0",
|
||||
"tributejs": "5.1.3",
|
||||
"typescript": "5.7.3",
|
||||
"typescript": "5.8.2",
|
||||
"uint8-to-base64": "0.2.0",
|
||||
"vanilla-colorful": "0.7.2",
|
||||
"vue": "3.5.13",
|
||||
@ -66,7 +66,7 @@
|
||||
"devDependencies": {
|
||||
"@eslint-community/eslint-plugin-eslint-comments": "4.4.1",
|
||||
"@playwright/test": "1.49.1",
|
||||
"@stoplight/spectral-cli": "6.14.2",
|
||||
"@stoplight/spectral-cli": "6.14.3",
|
||||
"@stylistic/eslint-plugin-js": "3.1.0",
|
||||
"@stylistic/stylelint-plugin": "3.1.2",
|
||||
"@types/dropzone": "5.7.9",
|
||||
@ -79,41 +79,41 @@
|
||||
"@types/throttle-debounce": "5.0.2",
|
||||
"@types/tinycolor2": "1.4.6",
|
||||
"@types/toastify-js": "1.12.3",
|
||||
"@typescript-eslint/eslint-plugin": "8.24.0",
|
||||
"@typescript-eslint/parser": "8.24.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.26.1",
|
||||
"@typescript-eslint/parser": "8.26.1",
|
||||
"@vitejs/plugin-vue": "5.2.1",
|
||||
"@vitest/eslint-plugin": "1.1.31",
|
||||
"@vitest/eslint-plugin": "1.1.37",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-import-resolver-typescript": "3.8.0",
|
||||
"eslint-import-resolver-typescript": "3.9.0",
|
||||
"eslint-plugin-array-func": "4.0.0",
|
||||
"eslint-plugin-github": "5.0.2",
|
||||
"eslint-plugin-import-x": "4.6.1",
|
||||
"eslint-plugin-no-jquery": "3.1.0",
|
||||
"eslint-plugin-import-x": "4.7.2",
|
||||
"eslint-plugin-no-jquery": "3.1.1",
|
||||
"eslint-plugin-no-use-extend-native": "0.5.0",
|
||||
"eslint-plugin-playwright": "2.2.0",
|
||||
"eslint-plugin-regexp": "2.7.0",
|
||||
"eslint-plugin-sonarjs": "3.0.2",
|
||||
"eslint-plugin-unicorn": "56.0.1",
|
||||
"eslint-plugin-vue": "9.32.0",
|
||||
"eslint-plugin-vue": "10.0.0",
|
||||
"eslint-plugin-vue-scoped-css": "2.9.0",
|
||||
"eslint-plugin-wc": "2.2.0",
|
||||
"happy-dom": "17.1.0",
|
||||
"eslint-plugin-wc": "2.2.1",
|
||||
"happy-dom": "17.4.4",
|
||||
"markdownlint-cli": "0.44.0",
|
||||
"material-icon-theme": "5.20.0",
|
||||
"nolyfill": "1.0.43",
|
||||
"nolyfill": "1.0.44",
|
||||
"postcss-html": "1.8.0",
|
||||
"stylelint": "16.14.1",
|
||||
"stylelint": "16.16.0",
|
||||
"stylelint-config-recommended": "15.0.0",
|
||||
"stylelint-declaration-block-no-ignored-properties": "2.8.0",
|
||||
"stylelint-declaration-strict-value": "1.10.7",
|
||||
"stylelint-define-config": "16.14.1",
|
||||
"stylelint-declaration-strict-value": "1.10.11",
|
||||
"stylelint-define-config": "16.15.0",
|
||||
"stylelint-value-no-unknown-custom-properties": "6.0.1",
|
||||
"svgo": "3.3.2",
|
||||
"type-fest": "4.34.1",
|
||||
"type-fest": "4.37.0",
|
||||
"updates": "16.4.2",
|
||||
"vite-string-plugin": "1.4.4",
|
||||
"vitest": "3.0.5",
|
||||
"vue-tsc": "2.2.2"
|
||||
"vitest": "3.0.8",
|
||||
"vue-tsc": "2.2.8"
|
||||
},
|
||||
"browserslist": [
|
||||
"defaults"
|
||||
|
24
poetry.lock
generated
24
poetry.lock
generated
@ -1,4 +1,4 @@
|
||||
# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
@ -29,14 +29,14 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "cssbeautifier"
|
||||
version = "1.15.3"
|
||||
version = "1.15.4"
|
||||
description = "CSS unobfuscator and beautifier."
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "cssbeautifier-1.15.3-py3-none-any.whl", hash = "sha256:0dcaf5ce197743a79b3a160b84ea58fcbd9e3e767c96df1171e428125b16d410"},
|
||||
{file = "cssbeautifier-1.15.3.tar.gz", hash = "sha256:406b04d09e7d62c0be084fbfa2cba5126fe37359ea0d8d9f7b963a6354fc8303"},
|
||||
{file = "cssbeautifier-1.15.4-py3-none-any.whl", hash = "sha256:78c84d5e5378df7d08622bbd0477a1abdbd209680e95480bf22f12d5701efc98"},
|
||||
{file = "cssbeautifier-1.15.4.tar.gz", hash = "sha256:9bb08dc3f64c101a01677f128acf01905914cf406baf87434dcde05b74c0acf5"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -103,14 +103,14 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "jsbeautifier"
|
||||
version = "1.15.3"
|
||||
version = "1.15.4"
|
||||
description = "JavaScript unobfuscator and beautifier."
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "jsbeautifier-1.15.3-py3-none-any.whl", hash = "sha256:b207a15ab7529eee4a35ae7790e9ec4e32a2b5026d51e2d0386c3a65e6ecfc91"},
|
||||
{file = "jsbeautifier-1.15.3.tar.gz", hash = "sha256:5f1baf3d4ca6a615bb5417ee861b34b77609eeb12875555f8bbfabd9bf2f3457"},
|
||||
{file = "jsbeautifier-1.15.4-py3-none-any.whl", hash = "sha256:72f65de312a3f10900d7685557f84cb61a9733c50dcc27271a39f5b0051bf528"},
|
||||
{file = "jsbeautifier-1.15.4.tar.gz", hash = "sha256:5bb18d9efb9331d825735fbc5360ee8f1aac5e52780042803943aa7f854f7592"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -403,14 +403,14 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "yamllint"
|
||||
version = "1.35.1"
|
||||
version = "1.36.1"
|
||||
description = "A linter for YAML files."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "yamllint-1.35.1-py3-none-any.whl", hash = "sha256:2e16e504bb129ff515b37823b472750b36b6de07963bd74b307341ef5ad8bdc3"},
|
||||
{file = "yamllint-1.35.1.tar.gz", hash = "sha256:7a003809f88324fd2c877734f2d575ee7881dd9043360657cc8049c809eba6cd"},
|
||||
{file = "yamllint-1.36.1-py3-none-any.whl", hash = "sha256:3e2ccd47ea12449837adf6b2c56fd9e31172ce42bc1470380806be461f25df66"},
|
||||
{file = "yamllint-1.36.1.tar.gz", hash = "sha256:a287689daaafc301a80549b2d0170452ebfdcabd826e3fe3b4c66e322d4851fa"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -423,4 +423,4 @@ dev = ["doc8", "flake8", "flake8-import-order", "rstcheck[sphinx]", "sphinx"]
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "f2e8260efe6e25f77ef387daff9551e41d25027e4794b42bc7a851ed0dfafd85"
|
||||
content-hash = "d48a461813418f7b803a7f94713d93076a009a96554e0f4c707be0cc2a95b563"
|
||||
|
@ -6,7 +6,7 @@ python = "^3.10"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
djlint = "1.36.4"
|
||||
yamllint = "1.35.1"
|
||||
yamllint = "1.36.1"
|
||||
|
||||
[tool.djlint]
|
||||
profile="golang"
|
||||
|
@ -78,6 +78,7 @@ func (s *Service) Register(
|
||||
RepoID: runnerToken.RepoID,
|
||||
Version: req.Msg.Version,
|
||||
AgentLabels: labels,
|
||||
Ephemeral: req.Msg.Ephemeral,
|
||||
}
|
||||
if err := runner.GenerateToken(); err != nil {
|
||||
return nil, errors.New("can't generate token")
|
||||
@ -96,12 +97,13 @@ func (s *Service) Register(
|
||||
|
||||
res := connect.NewResponse(&runnerv1.RegisterResponse{
|
||||
Runner: &runnerv1.Runner{
|
||||
Id: runner.ID,
|
||||
Uuid: runner.UUID,
|
||||
Token: runner.Token,
|
||||
Name: runner.Name,
|
||||
Version: runner.Version,
|
||||
Labels: runner.AgentLabels,
|
||||
Id: runner.ID,
|
||||
Uuid: runner.UUID,
|
||||
Token: runner.Token,
|
||||
Name: runner.Name,
|
||||
Version: runner.Version,
|
||||
Labels: runner.AgentLabels,
|
||||
Ephemeral: runner.Ephemeral,
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -98,6 +98,11 @@ func serveMavenMetadata(ctx *context.Context, params parameters) {
|
||||
}
|
||||
pvs = append(pvsLegacy, pvs...)
|
||||
|
||||
if len(pvs) == 0 {
|
||||
apiError(ctx, http.StatusNotFound, packages_model.ErrPackageNotExist)
|
||||
return
|
||||
}
|
||||
|
||||
pds, err := packages_model.GetPackageDescriptors(ctx, pvs)
|
||||
if err != nil {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
|
@ -61,8 +61,9 @@ func (Action) ListActionsSecrets(ctx *context.APIContext) {
|
||||
apiSecrets := make([]*api.Secret, len(secrets))
|
||||
for k, v := range secrets {
|
||||
apiSecrets[k] = &api.Secret{
|
||||
Name: v.Name,
|
||||
Created: v.CreatedUnix.AsTime(),
|
||||
Name: v.Name,
|
||||
Description: v.Description,
|
||||
Created: v.CreatedUnix.AsTime(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +107,7 @@ func (Action) CreateOrUpdateSecret(ctx *context.APIContext) {
|
||||
|
||||
opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption)
|
||||
|
||||
_, created, err := secret_service.CreateOrUpdateSecret(ctx, ctx.Org.Organization.ID, 0, ctx.PathParam("secretname"), opt.Data)
|
||||
_, created, err := secret_service.CreateOrUpdateSecret(ctx, ctx.Org.Organization.ID, 0, ctx.PathParam("secretname"), opt.Data, opt.Description)
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
ctx.APIError(http.StatusBadRequest, err)
|
||||
@ -230,10 +231,11 @@ func (Action) ListVariables(ctx *context.APIContext) {
|
||||
variables := make([]*api.ActionVariable, len(vars))
|
||||
for i, v := range vars {
|
||||
variables[i] = &api.ActionVariable{
|
||||
OwnerID: v.OwnerID,
|
||||
RepoID: v.RepoID,
|
||||
Name: v.Name,
|
||||
Data: v.Data,
|
||||
OwnerID: v.OwnerID,
|
||||
RepoID: v.RepoID,
|
||||
Name: v.Name,
|
||||
Data: v.Data,
|
||||
Description: v.Description,
|
||||
}
|
||||
}
|
||||
|
||||
@ -281,10 +283,11 @@ func (Action) GetVariable(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
variable := &api.ActionVariable{
|
||||
OwnerID: v.OwnerID,
|
||||
RepoID: v.RepoID,
|
||||
Name: v.Name,
|
||||
Data: v.Data,
|
||||
OwnerID: v.OwnerID,
|
||||
RepoID: v.RepoID,
|
||||
Name: v.Name,
|
||||
Data: v.Data,
|
||||
Description: v.Description,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, variable)
|
||||
@ -386,7 +389,7 @@ func (Action) CreateVariable(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := actions_service.CreateVariable(ctx, ownerID, 0, variableName, opt.Value); err != nil {
|
||||
if _, err := actions_service.CreateVariable(ctx, ownerID, 0, variableName, opt.Value, opt.Description); err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
ctx.APIError(http.StatusBadRequest, err)
|
||||
} else {
|
||||
@ -453,6 +456,7 @@ func (Action) UpdateVariable(ctx *context.APIContext) {
|
||||
|
||||
v.Name = opt.Name
|
||||
v.Data = opt.Value
|
||||
v.Description = opt.Description
|
||||
|
||||
if _, err := actions_service.UpdateVariableNameData(ctx, v); err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
|
@ -86,8 +86,9 @@ func (Action) ListActionsSecrets(ctx *context.APIContext) {
|
||||
apiSecrets := make([]*api.Secret, len(secrets))
|
||||
for k, v := range secrets {
|
||||
apiSecrets[k] = &api.Secret{
|
||||
Name: v.Name,
|
||||
Created: v.CreatedUnix.AsTime(),
|
||||
Name: v.Name,
|
||||
Description: v.Description,
|
||||
Created: v.CreatedUnix.AsTime(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,7 +139,7 @@ func (Action) CreateOrUpdateSecret(ctx *context.APIContext) {
|
||||
|
||||
opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption)
|
||||
|
||||
_, created, err := secret_service.CreateOrUpdateSecret(ctx, 0, repo.ID, ctx.PathParam("secretname"), opt.Data)
|
||||
_, created, err := secret_service.CreateOrUpdateSecret(ctx, 0, repo.ID, ctx.PathParam("secretname"), opt.Data, opt.Description)
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
ctx.APIError(http.StatusBadRequest, err)
|
||||
@ -251,10 +252,11 @@ func (Action) GetVariable(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
variable := &api.ActionVariable{
|
||||
OwnerID: v.OwnerID,
|
||||
RepoID: v.RepoID,
|
||||
Name: v.Name,
|
||||
Data: v.Data,
|
||||
OwnerID: v.OwnerID,
|
||||
RepoID: v.RepoID,
|
||||
Name: v.Name,
|
||||
Data: v.Data,
|
||||
Description: v.Description,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, variable)
|
||||
@ -364,7 +366,7 @@ func (Action) CreateVariable(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := actions_service.CreateVariable(ctx, 0, repoID, variableName, opt.Value); err != nil {
|
||||
if _, err := actions_service.CreateVariable(ctx, 0, repoID, variableName, opt.Value, opt.Description); err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
ctx.APIError(http.StatusBadRequest, err)
|
||||
} else {
|
||||
@ -434,6 +436,7 @@ func (Action) UpdateVariable(ctx *context.APIContext) {
|
||||
|
||||
v.Name = opt.Name
|
||||
v.Data = opt.Value
|
||||
v.Description = opt.Description
|
||||
|
||||
if _, err := actions_service.UpdateVariableNameData(ctx, v); err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
@ -493,9 +496,11 @@ func (Action) ListVariables(ctx *context.APIContext) {
|
||||
variables := make([]*api.ActionVariable, len(vars))
|
||||
for i, v := range vars {
|
||||
variables[i] = &api.ActionVariable{
|
||||
OwnerID: v.OwnerID,
|
||||
RepoID: v.RepoID,
|
||||
Name: v.Name,
|
||||
OwnerID: v.OwnerID,
|
||||
RepoID: v.RepoID,
|
||||
Name: v.Name,
|
||||
Data: v.Data,
|
||||
Description: v.Description,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -227,7 +227,7 @@ func CreateBranch(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
} else if len(opt.OldBranchName) > 0 { //nolint
|
||||
if ctx.Repo.GitRepo.IsBranchExist(opt.OldBranchName) { //nolint
|
||||
if gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, opt.OldBranchName) { //nolint
|
||||
oldCommit, err = ctx.Repo.GitRepo.GetBranchCommit(opt.OldBranchName) //nolint
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
@ -1019,7 +1019,7 @@ func EditBranchProtection(ctx *context.APIContext) {
|
||||
isPlainRule := !git_model.IsRuleNameSpecial(bpName)
|
||||
var isBranchExist bool
|
||||
if isPlainRule {
|
||||
isBranchExist = git.IsBranchExist(ctx.Req.Context(), ctx.Repo.Repository.RepoPath(), bpName)
|
||||
isBranchExist = gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, bpName)
|
||||
}
|
||||
|
||||
if isBranchExist {
|
||||
|
@ -742,7 +742,7 @@ func EditPullRequest(ctx *context.APIContext) {
|
||||
|
||||
// change pull target branch
|
||||
if !pr.HasMerged && len(form.Base) != 0 && form.Base != pr.BaseBranch {
|
||||
if !ctx.Repo.GitRepo.IsBranchExist(form.Base) {
|
||||
if !gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, form.Base) {
|
||||
ctx.APIError(http.StatusNotFound, fmt.Errorf("new base '%s' not exist", form.Base))
|
||||
return
|
||||
}
|
||||
|
@ -734,7 +734,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
|
||||
|
||||
// Default branch only updated if changed and exist or the repository is empty
|
||||
updateRepoLicense := false
|
||||
if opts.DefaultBranch != nil && repo.DefaultBranch != *opts.DefaultBranch && (repo.IsEmpty || ctx.Repo.GitRepo.IsBranchExist(*opts.DefaultBranch)) {
|
||||
if opts.DefaultBranch != nil && repo.DefaultBranch != *opts.DefaultBranch && (repo.IsEmpty || gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, *opts.DefaultBranch)) {
|
||||
if !repo.IsEmpty {
|
||||
if err := gitrepo.SetDefaultBranch(ctx, ctx.Repo.Repository, *opts.DefaultBranch); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
|
@ -49,7 +49,7 @@ func CreateOrUpdateSecret(ctx *context.APIContext) {
|
||||
|
||||
opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption)
|
||||
|
||||
_, created, err := secret_service.CreateOrUpdateSecret(ctx, ctx.Doer.ID, 0, ctx.PathParam("secretname"), opt.Data)
|
||||
_, created, err := secret_service.CreateOrUpdateSecret(ctx, ctx.Doer.ID, 0, ctx.PathParam("secretname"), opt.Data, opt.Description)
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
ctx.APIError(http.StatusBadRequest, err)
|
||||
@ -153,7 +153,7 @@ func CreateVariable(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := actions_service.CreateVariable(ctx, ownerID, 0, variableName, opt.Value); err != nil {
|
||||
if _, err := actions_service.CreateVariable(ctx, ownerID, 0, variableName, opt.Value, opt.Description); err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
ctx.APIError(http.StatusBadRequest, err)
|
||||
} else {
|
||||
@ -215,6 +215,7 @@ func UpdateVariable(ctx *context.APIContext) {
|
||||
|
||||
v.Name = opt.Name
|
||||
v.Data = opt.Value
|
||||
v.Description = opt.Description
|
||||
|
||||
if _, err := actions_service.UpdateVariableNameData(ctx, v); err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
@ -300,10 +301,11 @@ func GetVariable(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
variable := &api.ActionVariable{
|
||||
OwnerID: v.OwnerID,
|
||||
RepoID: v.RepoID,
|
||||
Name: v.Name,
|
||||
Data: v.Data,
|
||||
OwnerID: v.OwnerID,
|
||||
RepoID: v.RepoID,
|
||||
Name: v.Name,
|
||||
Data: v.Data,
|
||||
Description: v.Description,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, variable)
|
||||
@ -345,10 +347,11 @@ func ListVariables(ctx *context.APIContext) {
|
||||
variables := make([]*api.ActionVariable, len(vars))
|
||||
for i, v := range vars {
|
||||
variables[i] = &api.ActionVariable{
|
||||
OwnerID: v.OwnerID,
|
||||
RepoID: v.RepoID,
|
||||
Name: v.Name,
|
||||
Data: v.Data,
|
||||
OwnerID: v.OwnerID,
|
||||
RepoID: v.RepoID,
|
||||
Name: v.Name,
|
||||
Data: v.Data,
|
||||
Description: v.Description,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
@ -447,13 +448,13 @@ func preReceiveFor(ctx *preReceiveContext, refFullName git.RefName) {
|
||||
baseBranchName := refFullName.ForBranchName()
|
||||
|
||||
baseBranchExist := false
|
||||
if ctx.Repo.GitRepo.IsBranchExist(baseBranchName) {
|
||||
if gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, baseBranchName) {
|
||||
baseBranchExist = true
|
||||
}
|
||||
|
||||
if !baseBranchExist {
|
||||
for p, v := range baseBranchName {
|
||||
if v == '/' && ctx.Repo.GitRepo.IsBranchExist(baseBranchName[:p]) && p != len(baseBranchName)-1 {
|
||||
if v == '/' && gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, baseBranchName[:p]) && p != len(baseBranchName)-1 {
|
||||
baseBranchExist = true
|
||||
break
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
"code.gitea.io/gitea/models/renderhelper"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
@ -28,12 +29,23 @@ func ShowUserFeedAtom(ctx *context.Context) {
|
||||
// showUserFeed show user activity as RSS / Atom feed
|
||||
func showUserFeed(ctx *context.Context, formatType string) {
|
||||
includePrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
|
||||
isOrganisation := ctx.ContextUser.IsOrganization()
|
||||
if ctx.IsSigned && isOrganisation && !includePrivate {
|
||||
// When feed is requested by a member of the organization,
|
||||
// include the private repo's the member has access to.
|
||||
isOrgMember, err := organization.IsOrganizationMember(ctx, ctx.ContextUser.ID, ctx.Doer.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("IsOrganizationMember", err)
|
||||
return
|
||||
}
|
||||
includePrivate = isOrgMember
|
||||
}
|
||||
|
||||
actions, _, err := feed_service.GetFeeds(ctx, activities_model.GetFeedsOptions{
|
||||
RequestedUser: ctx.ContextUser,
|
||||
Actor: ctx.Doer,
|
||||
IncludePrivate: includePrivate,
|
||||
OnlyPerformedBy: !ctx.ContextUser.IsOrganization(),
|
||||
OnlyPerformedBy: !isOrganisation,
|
||||
IncludeDeleted: false,
|
||||
Date: ctx.FormString("date"),
|
||||
})
|
||||
|
38
routers/web/feed/profile_test.go
Normal file
38
routers/web/feed/profile_test.go
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
package feed_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/routers/web/feed"
|
||||
"code.gitea.io/gitea/services/contexttest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
unittest.MainTest(m)
|
||||
}
|
||||
|
||||
func TestCheckGetOrgFeedsAsOrgMember(t *testing.T) {
|
||||
unittest.PrepareTestEnv(t)
|
||||
t.Run("OrgMember", func(t *testing.T) {
|
||||
ctx, resp := contexttest.MockContext(t, "org3.atom")
|
||||
ctx.ContextUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
|
||||
contexttest.LoadUser(t, ctx, 2)
|
||||
ctx.IsSigned = true
|
||||
feed.ShowUserFeedAtom(ctx)
|
||||
assert.Contains(t, resp.Body.String(), "<entry>") // Should contain 1 private entry
|
||||
})
|
||||
t.Run("NonOrgMember", func(t *testing.T) {
|
||||
ctx, resp := contexttest.MockContext(t, "org3.atom")
|
||||
ctx.ContextUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
|
||||
contexttest.LoadUser(t, ctx, 5)
|
||||
ctx.IsSigned = true
|
||||
feed.ShowUserFeedAtom(ctx)
|
||||
assert.NotContains(t, resp.Body.String(), "<entry>") // Should not contain any entries
|
||||
})
|
||||
}
|
@ -11,6 +11,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/charset"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
@ -40,60 +41,45 @@ type blameRow struct {
|
||||
|
||||
// RefBlame render blame page
|
||||
func RefBlame(ctx *context.Context) {
|
||||
fileName := ctx.Repo.TreePath
|
||||
if len(fileName) == 0 {
|
||||
ctx.Data["PageIsViewCode"] = true
|
||||
ctx.Data["IsBlame"] = true
|
||||
|
||||
// Get current entry user currently looking at.
|
||||
if ctx.Repo.TreePath == "" {
|
||||
ctx.NotFound(nil)
|
||||
return
|
||||
}
|
||||
|
||||
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
|
||||
treeLink := branchLink
|
||||
rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.RefTypeNameSubURL()
|
||||
|
||||
if len(ctx.Repo.TreePath) > 0 {
|
||||
treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
}
|
||||
|
||||
var treeNames []string
|
||||
paths := make([]string, 0, 5)
|
||||
if len(ctx.Repo.TreePath) > 0 {
|
||||
treeNames = strings.Split(ctx.Repo.TreePath, "/")
|
||||
for i := range treeNames {
|
||||
paths = append(paths, strings.Join(treeNames[:i+1], "/"))
|
||||
}
|
||||
|
||||
ctx.Data["HasParentPath"] = true
|
||||
if len(paths)-2 >= 0 {
|
||||
ctx.Data["ParentPath"] = "/" + paths[len(paths)-1]
|
||||
}
|
||||
}
|
||||
|
||||
// Get current entry user currently looking at.
|
||||
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
|
||||
if err != nil {
|
||||
HandleGitError(ctx, "Repo.Commit.GetTreeEntryByPath", err)
|
||||
return
|
||||
}
|
||||
|
||||
blob := entry.Blob()
|
||||
treeNames := strings.Split(ctx.Repo.TreePath, "/")
|
||||
var paths []string
|
||||
for i := range treeNames {
|
||||
paths = append(paths, strings.Join(treeNames[:i+1], "/"))
|
||||
}
|
||||
|
||||
ctx.Data["Paths"] = paths
|
||||
ctx.Data["TreeLink"] = treeLink
|
||||
ctx.Data["TreeNames"] = treeNames
|
||||
ctx.Data["BranchLink"] = branchLink
|
||||
|
||||
ctx.Data["RawFileLink"] = rawLink + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
ctx.Data["PageIsViewCode"] = true
|
||||
|
||||
ctx.Data["IsBlame"] = true
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
|
||||
ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.RefTypeNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
|
||||
blob := entry.Blob()
|
||||
fileSize := blob.Size()
|
||||
ctx.Data["FileSize"] = fileSize
|
||||
ctx.Data["FileName"] = blob.Name()
|
||||
|
||||
tplName := tplRepoViewContent
|
||||
if !ctx.FormBool("only_content") {
|
||||
prepareHomeTreeSideBarSwitch(ctx)
|
||||
tplName = tplRepoView
|
||||
}
|
||||
|
||||
if fileSize >= setting.UI.MaxDisplayFileSize {
|
||||
ctx.Data["IsFileTooLarge"] = true
|
||||
ctx.HTML(http.StatusOK, tplRepoHome)
|
||||
ctx.HTML(http.StatusOK, tplName)
|
||||
return
|
||||
}
|
||||
|
||||
@ -104,8 +90,7 @@ func RefBlame(ctx *context.Context) {
|
||||
}
|
||||
|
||||
bypassBlameIgnore, _ := strconv.ParseBool(ctx.FormString("bypass-blame-ignore"))
|
||||
|
||||
result, err := performBlame(ctx, ctx.Repo.Repository.RepoPath(), ctx.Repo.Commit, fileName, bypassBlameIgnore)
|
||||
result, err := performBlame(ctx, ctx.Repo.Repository, ctx.Repo.Commit, ctx.Repo.TreePath, bypassBlameIgnore)
|
||||
if err != nil {
|
||||
ctx.NotFound(err)
|
||||
return
|
||||
@ -121,7 +106,7 @@ func RefBlame(ctx *context.Context) {
|
||||
|
||||
renderBlame(ctx, result.Parts, commitNames)
|
||||
|
||||
ctx.HTML(http.StatusOK, tplRepoHome)
|
||||
ctx.HTML(http.StatusOK, tplName)
|
||||
}
|
||||
|
||||
type blameResult struct {
|
||||
@ -130,10 +115,10 @@ type blameResult struct {
|
||||
FaultyIgnoreRevsFile bool
|
||||
}
|
||||
|
||||
func performBlame(ctx *context.Context, repoPath string, commit *git.Commit, file string, bypassBlameIgnore bool) (*blameResult, error) {
|
||||
func performBlame(ctx *context.Context, repo *repo_model.Repository, commit *git.Commit, file string, bypassBlameIgnore bool) (*blameResult, error) {
|
||||
objectFormat := ctx.Repo.GetObjectFormat()
|
||||
|
||||
blameReader, err := git.CreateBlameReader(ctx, objectFormat, repoPath, commit, file, bypassBlameIgnore)
|
||||
blameReader, err := git.CreateBlameReader(ctx, objectFormat, repo.RepoPath(), commit, file, bypassBlameIgnore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -149,7 +134,7 @@ func performBlame(ctx *context.Context, repoPath string, commit *git.Commit, fil
|
||||
if len(r.Parts) == 0 && r.UsesIgnoreRevs {
|
||||
// try again without ignored revs
|
||||
|
||||
blameReader, err = git.CreateBlameReader(ctx, objectFormat, repoPath, commit, file, true)
|
||||
blameReader, err = git.CreateBlameReader(ctx, objectFormat, repo.RepoPath(), commit, file, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -301,8 +301,8 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
|
||||
|
||||
// Check if base branch is valid.
|
||||
baseIsCommit := ctx.Repo.GitRepo.IsCommitExist(ci.BaseBranch)
|
||||
baseIsBranch := ctx.Repo.GitRepo.IsBranchExist(ci.BaseBranch)
|
||||
baseIsTag := ctx.Repo.GitRepo.IsTagExist(ci.BaseBranch)
|
||||
baseIsBranch := gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, ci.BaseBranch)
|
||||
baseIsTag := gitrepo.IsTagExist(ctx, ctx.Repo.Repository, ci.BaseBranch)
|
||||
|
||||
if !baseIsCommit && !baseIsBranch && !baseIsTag {
|
||||
// Check if baseBranch is short sha commit hash
|
||||
@ -504,8 +504,8 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
|
||||
|
||||
// Check if head branch is valid.
|
||||
headIsCommit := ci.HeadGitRepo.IsCommitExist(ci.HeadBranch)
|
||||
headIsBranch := ci.HeadGitRepo.IsBranchExist(ci.HeadBranch)
|
||||
headIsTag := ci.HeadGitRepo.IsTagExist(ci.HeadBranch)
|
||||
headIsBranch := gitrepo.IsBranchExist(ctx, ci.HeadRepo, ci.HeadBranch)
|
||||
headIsTag := gitrepo.IsTagExist(ctx, ci.HeadRepo, ci.HeadBranch)
|
||||
if !headIsCommit && !headIsBranch && !headIsTag {
|
||||
// Check if headBranch is short sha commit hash
|
||||
if headCommit, _ := ci.HeadGitRepo.GetCommit(ci.HeadBranch); headCommit != nil {
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"code.gitea.io/gitea/models/renderhelper"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
@ -117,7 +118,7 @@ func NewComment(ctx *context.Context) {
|
||||
ctx.ServerError("Unable to load head repo", err)
|
||||
return
|
||||
}
|
||||
if ok := git.IsBranchExist(ctx, pull.HeadRepo.RepoPath(), pull.BaseBranch); !ok {
|
||||
if ok := gitrepo.IsBranchExist(ctx, pull.HeadRepo, pull.BaseBranch); !ok {
|
||||
// todo localize
|
||||
ctx.JSONError("The origin branch is delete, cannot reopen.")
|
||||
return
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/emoji"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
@ -526,7 +527,7 @@ func preparePullViewDeleteBranch(ctx *context.Context, issue *issues_model.Issue
|
||||
pull := issue.PullRequest
|
||||
isPullBranchDeletable := canDelete &&
|
||||
pull.HeadRepo != nil &&
|
||||
git.IsBranchExist(ctx, pull.HeadRepo.RepoPath(), pull.HeadBranch) &&
|
||||
gitrepo.IsBranchExist(ctx, pull.HeadRepo, pull.HeadBranch) &&
|
||||
(!pull.HasMerged || ctx.Data["HeadBranchCommitID"] == ctx.Data["PullHeadCommitID"])
|
||||
|
||||
if isPullBranchDeletable && pull.HasMerged {
|
||||
|
@ -347,7 +347,7 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
|
||||
defer baseGitRepo.Close()
|
||||
}
|
||||
|
||||
if !baseGitRepo.IsBranchExist(pull.BaseBranch) {
|
||||
if !gitrepo.IsBranchExist(ctx, pull.BaseRepo, pull.BaseBranch) {
|
||||
ctx.Data["BaseBranchNotExist"] = true
|
||||
ctx.Data["IsPullRequestBroken"] = true
|
||||
ctx.Data["BaseTarget"] = pull.BaseBranch
|
||||
@ -404,9 +404,9 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
|
||||
defer closer.Close()
|
||||
|
||||
if pull.Flow == issues_model.PullRequestFlowGithub {
|
||||
headBranchExist = headGitRepo.IsBranchExist(pull.HeadBranch)
|
||||
headBranchExist = gitrepo.IsBranchExist(ctx, pull.HeadRepo, pull.HeadBranch)
|
||||
} else {
|
||||
headBranchExist = git.IsReferenceExist(ctx, baseGitRepo.Path, pull.GetGitRefName())
|
||||
headBranchExist = gitrepo.IsReferenceExist(ctx, pull.BaseRepo, pull.GetGitRefName())
|
||||
}
|
||||
|
||||
if headBranchExist {
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@ -420,7 +421,7 @@ func NewReleasePost(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.GitRepo.IsBranchExist(form.Target) {
|
||||
if !gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, form.Target) {
|
||||
ctx.RenderWithErr(ctx.Tr("form.target_branch_not_exist"), tplReleaseNew, &form)
|
||||
return
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ func SetDefaultBranchPost(ctx *context.Context) {
|
||||
}
|
||||
|
||||
branch := ctx.FormString("branch")
|
||||
if err := repo_service.SetRepoDefaultBranch(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, branch); err != nil {
|
||||
if err := repo_service.SetRepoDefaultBranch(ctx, ctx.Repo.Repository, branch); err != nil {
|
||||
switch {
|
||||
case git_model.IsErrBranchNotExist(err):
|
||||
ctx.Status(http.StatusNotFound)
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
"code.gitea.io/gitea/services/gitdiff"
|
||||
files_service "code.gitea.io/gitea/services/repository/files"
|
||||
|
||||
"github.com/go-enry/go-enry/v2"
|
||||
)
|
||||
@ -84,3 +85,12 @@ func transformDiffTreeForUI(diffTree *gitdiff.DiffTree, filesViewedState map[str
|
||||
|
||||
return files
|
||||
}
|
||||
|
||||
func TreeViewNodes(ctx *context.Context) {
|
||||
results, err := files_service.GetTreeViewNodes(ctx, ctx.Repo.Commit, ctx.Repo.TreePath, ctx.FormString("sub_path"))
|
||||
if err != nil {
|
||||
ctx.ServerError("GetTreeViewNodes", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, map[string]any{"fileTreeNodes": results})
|
||||
}
|
||||
|
@ -47,12 +47,14 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
tplRepoEMPTY templates.TplName = "repo/empty"
|
||||
tplRepoHome templates.TplName = "repo/home"
|
||||
tplRepoViewList templates.TplName = "repo/view_list"
|
||||
tplWatchers templates.TplName = "repo/watchers"
|
||||
tplForks templates.TplName = "repo/forks"
|
||||
tplMigrating templates.TplName = "repo/migrate/migrating"
|
||||
tplRepoEMPTY templates.TplName = "repo/empty"
|
||||
tplRepoHome templates.TplName = "repo/home"
|
||||
tplRepoView templates.TplName = "repo/view"
|
||||
tplRepoViewContent templates.TplName = "repo/view_content"
|
||||
tplRepoViewList templates.TplName = "repo/view_list"
|
||||
tplWatchers templates.TplName = "repo/watchers"
|
||||
tplForks templates.TplName = "repo/forks"
|
||||
tplMigrating templates.TplName = "repo/migrate/migrating"
|
||||
)
|
||||
|
||||
type fileInfo struct {
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -17,6 +18,7 @@ import (
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
unit_model "code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
@ -328,6 +330,19 @@ func handleRepoHomeFeed(ctx *context.Context) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func prepareHomeTreeSideBarSwitch(ctx *context.Context) {
|
||||
showFileTree := true
|
||||
if ctx.Doer != nil {
|
||||
v, err := user_model.GetUserSetting(ctx, ctx.Doer.ID, user_model.SettingsKeyCodeViewShowFileTree, "true")
|
||||
if err != nil {
|
||||
log.Error("GetUserSetting: %v", err)
|
||||
} else {
|
||||
showFileTree, _ = strconv.ParseBool(v)
|
||||
}
|
||||
}
|
||||
ctx.Data["UserSettingCodeViewShowFileTree"] = showFileTree
|
||||
}
|
||||
|
||||
// Home render repository home page
|
||||
func Home(ctx *context.Context) {
|
||||
if handleRepoHomeFeed(ctx) {
|
||||
@ -341,6 +356,8 @@ func Home(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
prepareHomeTreeSideBarSwitch(ctx)
|
||||
|
||||
title := ctx.Repo.Repository.Owner.Name + "/" + ctx.Repo.Repository.Name
|
||||
if len(ctx.Repo.Repository.Description) > 0 {
|
||||
title += ": " + ctx.Repo.Repository.Description
|
||||
@ -410,7 +427,13 @@ func Home(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
ctx.HTML(http.StatusOK, tplRepoHome)
|
||||
if ctx.FormBool("only_content") {
|
||||
ctx.HTML(http.StatusOK, tplRepoViewContent)
|
||||
} else if len(treeNames) != 0 {
|
||||
ctx.HTML(http.StatusOK, tplRepoView)
|
||||
} else {
|
||||
ctx.HTML(http.StatusOK, tplRepoHome)
|
||||
}
|
||||
}
|
||||
|
||||
func RedirectRepoTreeToSrc(ctx *context.Context) {
|
||||
|
@ -106,7 +106,8 @@ func Variables(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
ctx.Data["Variables"] = variables
|
||||
|
||||
ctx.Data["DataMaxLength"] = actions_model.VariableDataMaxLength
|
||||
ctx.Data["DescriptionMaxLength"] = actions_model.VariableDescriptionMaxLength
|
||||
ctx.HTML(http.StatusOK, vCtx.VariablesTemplate)
|
||||
}
|
||||
|
||||
@ -124,7 +125,7 @@ func VariableCreate(ctx *context.Context) {
|
||||
|
||||
form := web.GetForm(ctx).(*forms.EditVariableForm)
|
||||
|
||||
v, err := actions_service.CreateVariable(ctx, vCtx.OwnerID, vCtx.RepoID, form.Name, form.Data)
|
||||
v, err := actions_service.CreateVariable(ctx, vCtx.OwnerID, vCtx.RepoID, form.Name, form.Data, form.Description)
|
||||
if err != nil {
|
||||
log.Error("CreateVariable: %v", err)
|
||||
ctx.JSONError(ctx.Tr("actions.variables.creation.failed"))
|
||||
@ -157,6 +158,7 @@ func VariableUpdate(ctx *context.Context) {
|
||||
form := web.GetForm(ctx).(*forms.EditVariableForm)
|
||||
variable.Name = form.Name
|
||||
variable.Data = form.Data
|
||||
variable.Description = form.Description
|
||||
|
||||
if ok, err := actions_service.UpdateVariableNameData(ctx, variable); err != nil || !ok {
|
||||
log.Error("UpdateVariable: %v", err)
|
||||
|
@ -22,12 +22,14 @@ func SetSecretsContext(ctx *context.Context, ownerID, repoID int64) {
|
||||
}
|
||||
|
||||
ctx.Data["Secrets"] = secrets
|
||||
ctx.Data["DataMaxLength"] = secret_model.SecretDataMaxLength
|
||||
ctx.Data["DescriptionMaxLength"] = secret_model.SecretDescriptionMaxLength
|
||||
}
|
||||
|
||||
func PerformSecretsPost(ctx *context.Context, ownerID, repoID int64, redirectURL string) {
|
||||
form := web.GetForm(ctx).(*forms.AddSecretForm)
|
||||
|
||||
s, _, err := secret_service.CreateOrUpdateSecret(ctx, ownerID, repoID, form.Name, util.ReserveLineBreakForTextarea(form.Data))
|
||||
s, _, err := secret_service.CreateOrUpdateSecret(ctx, ownerID, repoID, form.Name, util.ReserveLineBreakForTextarea(form.Data), form.Description)
|
||||
if err != nil {
|
||||
log.Error("CreateOrUpdateSecret failed: %v", err)
|
||||
ctx.JSONError(ctx.Tr("secrets.creation.failed"))
|
||||
|
26
routers/web/user/setting/settings.go
Normal file
26
routers/web/user/setting/settings.go
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package setting
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
)
|
||||
|
||||
func UpdatePreferences(ctx *context.Context) {
|
||||
type preferencesForm struct {
|
||||
CodeViewShowFileTree bool `json:"codeViewShowFileTree"`
|
||||
}
|
||||
form := &preferencesForm{}
|
||||
if err := json.NewDecoder(ctx.Req.Body).Decode(&form); err != nil {
|
||||
ctx.HTTPError(http.StatusBadRequest, "json decode failed")
|
||||
return
|
||||
}
|
||||
_ = user_model.SetUserSetting(ctx, ctx.Doer.ID, user_model.SettingsKeyCodeViewShowFileTree, strconv.FormatBool(form.CodeViewShowFileTree))
|
||||
ctx.JSONOK()
|
||||
}
|
@ -580,6 +580,7 @@ func registerRoutes(m *web.Router) {
|
||||
m.Group("/user/settings", func() {
|
||||
m.Get("", user_setting.Profile)
|
||||
m.Post("", web.Bind(forms.UpdateProfileForm{}), user_setting.ProfilePost)
|
||||
m.Post("/update_preferences", user_setting.UpdatePreferences)
|
||||
m.Get("/change_password", auth.MustChangePassword)
|
||||
m.Post("/change_password", web.Bind(forms.MustChangePasswordForm{}), auth.MustChangePasswordPost)
|
||||
m.Post("/avatar", web.Bind(forms.AvatarForm{}), user_setting.AvatarPost)
|
||||
@ -1175,6 +1176,11 @@ func registerRoutes(m *web.Router) {
|
||||
m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.TreeList)
|
||||
m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.TreeList)
|
||||
})
|
||||
m.Group("/tree-view", func() {
|
||||
m.Get("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.TreeViewNodes)
|
||||
m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.TreeViewNodes)
|
||||
m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.TreeViewNodes)
|
||||
})
|
||||
m.Get("/compare", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff)
|
||||
m.Combo("/compare/*", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists).
|
||||
Get(repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff).
|
||||
|
@ -9,14 +9,17 @@ import (
|
||||
"time"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
actions_module "code.gitea.io/gitea/modules/actions"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// Cleanup removes expired actions logs, data and artifacts
|
||||
// Cleanup removes expired actions logs, data, artifacts and used ephemeral runners
|
||||
func Cleanup(ctx context.Context) error {
|
||||
// clean up expired artifacts
|
||||
if err := CleanupArtifacts(ctx); err != nil {
|
||||
@ -28,6 +31,11 @@ func Cleanup(ctx context.Context) error {
|
||||
return fmt.Errorf("cleanup logs: %w", err)
|
||||
}
|
||||
|
||||
// clean up old ephemeral runners
|
||||
if err := CleanupEphemeralRunners(ctx); err != nil {
|
||||
return fmt.Errorf("cleanup old ephemeral runners: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -123,3 +131,20 @@ func CleanupLogs(ctx context.Context) error {
|
||||
log.Info("Removed %d logs", count)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanupEphemeralRunners removes used ephemeral runners which are no longer able to process jobs
|
||||
func CleanupEphemeralRunners(ctx context.Context) error {
|
||||
subQuery := builder.Select("`action_runner`.id").
|
||||
From(builder.Select("*").From("`action_runner`"), "`action_runner`"). // mysql needs this redundant subquery
|
||||
Join("INNER", "`action_task`", "`action_task`.`runner_id` = `action_runner`.`id`").
|
||||
Where(builder.Eq{"`action_runner`.`ephemeral`": true}).
|
||||
And(builder.NotIn("`action_task`.`status`", actions_model.StatusWaiting, actions_model.StatusRunning, actions_model.StatusBlocked))
|
||||
b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`")
|
||||
res, err := db.GetEngine(ctx).Exec(b)
|
||||
if err != nil {
|
||||
return fmt.Errorf("find runners: %w", err)
|
||||
}
|
||||
affected, _ := res.RowsAffected()
|
||||
log.Info("Removed %d runners", affected)
|
||||
return nil
|
||||
}
|
||||
|
@ -535,7 +535,7 @@ func (n *actionsNotifier) PushCommits(ctx context.Context, pusher *user_model.Us
|
||||
ctx = withMethod(ctx, "PushCommits")
|
||||
|
||||
apiPusher := convert.ToUser(ctx, pusher, nil)
|
||||
apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo.RepoPath(), repo.HTMLURL())
|
||||
apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo)
|
||||
if err != nil {
|
||||
log.Error("commits.ToAPIPayloadCommits failed: %v", err)
|
||||
return
|
||||
@ -596,7 +596,7 @@ func (n *actionsNotifier) SyncPushCommits(ctx context.Context, pusher *user_mode
|
||||
ctx = withMethod(ctx, "SyncPushCommits")
|
||||
|
||||
apiPusher := convert.ToUser(ctx, pusher, nil)
|
||||
apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo.RepoPath(), repo.HTMLURL())
|
||||
apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo)
|
||||
if err != nil {
|
||||
log.Error("commits.ToAPIPayloadCommits failed: %v", err)
|
||||
return
|
||||
|
@ -23,6 +23,26 @@ func PickTask(ctx context.Context, runner *actions_model.ActionRunner) (*runnerv
|
||||
actionTask *actions_model.ActionTask
|
||||
)
|
||||
|
||||
if runner.Ephemeral {
|
||||
var task actions_model.ActionTask
|
||||
has, err := db.GetEngine(ctx).Where("runner_id = ?", runner.ID).Get(&task)
|
||||
// Let the runner retry the request, do not allow to proceed
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if has {
|
||||
if task.Status == actions_model.StatusWaiting || task.Status == actions_model.StatusRunning || task.Status == actions_model.StatusBlocked {
|
||||
return nil, false, nil
|
||||
}
|
||||
// task has been finished, remove it
|
||||
_, err = db.DeleteByID[actions_model.ActionRunner](ctx, runner.ID)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return nil, false, fmt.Errorf("runner has been removed")
|
||||
}
|
||||
}
|
||||
|
||||
if err := db.WithTx(ctx, func(ctx context.Context) error {
|
||||
t, ok, err := actions_model.CreateTaskForRunner(ctx, runner)
|
||||
if err != nil {
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
secret_service "code.gitea.io/gitea/services/secrets"
|
||||
)
|
||||
|
||||
func CreateVariable(ctx context.Context, ownerID, repoID int64, name, data string) (*actions_model.ActionVariable, error) {
|
||||
func CreateVariable(ctx context.Context, ownerID, repoID int64, name, data, description string) (*actions_model.ActionVariable, error) {
|
||||
if err := secret_service.ValidateName(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -22,7 +22,7 @@ func CreateVariable(ctx context.Context, ownerID, repoID int64, name, data strin
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v, err := actions_model.InsertVariable(ctx, ownerID, repoID, name, util.ReserveLineBreakForTextarea(data))
|
||||
v, err := actions_model.InsertVariable(ctx, ownerID, repoID, name, util.ReserveLineBreakForTextarea(data), description)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -41,7 +41,7 @@ func UpdateVariableNameData(ctx context.Context, variable *actions_model.ActionV
|
||||
|
||||
variable.Data = util.ReserveLineBreakForTextarea(variable.Data)
|
||||
|
||||
return actions_model.UpdateVariableCols(ctx, variable, "name", "data")
|
||||
return actions_model.UpdateVariableCols(ctx, variable, "name", "data", "description")
|
||||
}
|
||||
|
||||
func DeleteVariableByID(ctx context.Context, variableID int64) error {
|
||||
|
@ -13,6 +13,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/gitrepo"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@ -56,10 +57,10 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
|
||||
|
||||
baseBranchName := opts.RefFullNames[i].ForBranchName()
|
||||
currentTopicBranch := ""
|
||||
if !gitRepo.IsBranchExist(baseBranchName) {
|
||||
if !gitrepo.IsBranchExist(ctx, repo, baseBranchName) {
|
||||
// try match refs/for/<target-branch>/<topic-branch>
|
||||
for p, v := range baseBranchName {
|
||||
if v == '/' && gitRepo.IsBranchExist(baseBranchName[:p]) && p != len(baseBranchName)-1 {
|
||||
if v == '/' && gitrepo.IsBranchExist(ctx, repo, baseBranchName[:p]) && p != len(baseBranchName)-1 {
|
||||
currentTopicBranch = baseBranchName[p+1:]
|
||||
baseBranchName = baseBranchName[:p]
|
||||
break
|
||||
|
@ -248,13 +248,13 @@ func handlePullRequestAutoMerge(pullID int64, sha string) {
|
||||
|
||||
switch pr.Flow {
|
||||
case issues_model.PullRequestFlowGithub:
|
||||
headBranchExist := headGitRepo.IsBranchExist(pr.HeadBranch)
|
||||
if pr.HeadRepo == nil || !headBranchExist {
|
||||
headBranchExist := pr.HeadRepo != nil && gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch)
|
||||
if !headBranchExist {
|
||||
log.Warn("Head branch of auto merge %-v does not exist [HeadRepoID: %d, Branch: %s]", pr, pr.HeadRepoID, pr.HeadBranch)
|
||||
return
|
||||
}
|
||||
case issues_model.PullRequestFlowAGit:
|
||||
headBranchExist := git.IsReferenceExist(ctx, baseGitRepo.Path, pr.GetGitRefName())
|
||||
headBranchExist := gitrepo.IsReferenceExist(ctx, pr.BaseRepo, pr.GetGitRefName())
|
||||
if !headBranchExist {
|
||||
log.Warn("Head branch of auto merge %-v does not exist [HeadRepoID: %d, Branch(Agit): %s]", pr, pr.HeadRepoID, pr.HeadBranch)
|
||||
return
|
||||
|
@ -304,14 +304,14 @@ func RepoRefForAPI(next http.Handler) http.Handler {
|
||||
refName, _, _ := getRefNameLegacy(ctx.Base, ctx.Repo, ctx.PathParam("*"), ctx.FormTrim("ref"))
|
||||
var err error
|
||||
|
||||
if ctx.Repo.GitRepo.IsBranchExist(refName) {
|
||||
if gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, refName) {
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
|
||||
} else if ctx.Repo.GitRepo.IsTagExist(refName) {
|
||||
} else if gitrepo.IsTagExist(ctx, ctx.Repo.Repository, refName) {
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
|
@ -814,7 +814,7 @@ func RepoRefByType(detectRefType git.RefType) func(*Context) {
|
||||
reqPath := ctx.PathParam("*")
|
||||
if reqPath == "" {
|
||||
refShortName = ctx.Repo.Repository.DefaultBranch
|
||||
if !ctx.Repo.GitRepo.IsBranchExist(refShortName) {
|
||||
if !gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, refShortName) {
|
||||
brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 1)
|
||||
if err == nil && len(brs) != 0 {
|
||||
refShortName = brs[0].Name
|
||||
@ -854,7 +854,7 @@ func RepoRefByType(detectRefType git.RefType) func(*Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if refType == git.RefTypeBranch && ctx.Repo.GitRepo.IsBranchExist(refShortName) {
|
||||
if refType == git.RefTypeBranch && gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, refShortName) {
|
||||
ctx.Repo.BranchName = refShortName
|
||||
ctx.Repo.RefFullName = git.RefNameFromBranch(refShortName)
|
||||
|
||||
@ -864,7 +864,7 @@ func RepoRefByType(detectRefType git.RefType) func(*Context) {
|
||||
return
|
||||
}
|
||||
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
|
||||
} else if refType == git.RefTypeTag && ctx.Repo.GitRepo.IsTagExist(refShortName) {
|
||||
} else if refType == git.RefTypeTag && gitrepo.IsTagExist(ctx, ctx.Repo.Repository, refShortName) {
|
||||
ctx.Repo.RefFullName = git.RefNameFromTag(refShortName)
|
||||
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refShortName)
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
git_module "code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/reqctx"
|
||||
"code.gitea.io/gitea/modules/session"
|
||||
@ -30,6 +31,7 @@ import (
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func mockRequest(t *testing.T, reqPath string) *http.Request {
|
||||
@ -85,7 +87,7 @@ func MockAPIContext(t *testing.T, reqPath string) (*context.APIContext, *httptes
|
||||
base := context.NewBaseContext(resp, req)
|
||||
base.Data = middleware.GetContextData(req.Context())
|
||||
base.Locale = &translation.MockLocale{}
|
||||
ctx := &context.APIContext{Base: base}
|
||||
ctx := &context.APIContext{Base: base, Repo: &context.Repository{}}
|
||||
chiCtx := chi.NewRouteContext()
|
||||
ctx.SetContextValue(chi.RouteCtxKey, chiCtx)
|
||||
return ctx, resp
|
||||
@ -106,13 +108,13 @@ func MockPrivateContext(t *testing.T, reqPath string) (*context.PrivateContext,
|
||||
// LoadRepo load a repo into a test context.
|
||||
func LoadRepo(t *testing.T, ctx gocontext.Context, repoID int64) {
|
||||
var doer *user_model.User
|
||||
repo := &context.Repository{}
|
||||
var repo *context.Repository
|
||||
switch ctx := ctx.(type) {
|
||||
case *context.Context:
|
||||
ctx.Repo = repo
|
||||
repo = ctx.Repo
|
||||
doer = ctx.Doer
|
||||
case *context.APIContext:
|
||||
ctx.Repo = repo
|
||||
repo = ctx.Repo
|
||||
doer = ctx.Doer
|
||||
default:
|
||||
assert.FailNow(t, "context is not *context.Context or *context.APIContext")
|
||||
@ -140,15 +142,17 @@ func LoadRepoCommit(t *testing.T, ctx gocontext.Context) {
|
||||
}
|
||||
|
||||
gitRepo, err := gitrepo.OpenRepository(ctx, repo.Repository)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
defer gitRepo.Close()
|
||||
branch, err := gitRepo.GetHEADBranch()
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, branch)
|
||||
if branch != nil {
|
||||
repo.Commit, err = gitRepo.GetBranchCommit(branch.Name)
|
||||
assert.NoError(t, err)
|
||||
|
||||
if repo.RefFullName == "" {
|
||||
repo.RefFullName = git_module.RefNameFromBranch(repo.Repository.DefaultBranch)
|
||||
}
|
||||
if repo.RefFullName.IsPull() {
|
||||
repo.BranchName = repo.RefFullName.ShortName()
|
||||
}
|
||||
repo.Commit, err = gitRepo.GetCommit(repo.RefFullName.String())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// LoadUser load a user into a test context
|
||||
|
@ -18,7 +18,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
@ -50,14 +49,14 @@ func checkScriptType(ctx context.Context, logger log.Logger, autofix bool) error
|
||||
|
||||
func checkHooks(ctx context.Context, logger log.Logger, autofix bool) error {
|
||||
if err := iterateRepositories(ctx, func(repo *repo_model.Repository) error {
|
||||
results, err := repository.CheckDelegateHooks(repo.RepoPath())
|
||||
results, err := gitrepo.CheckDelegateHooksForRepo(ctx, repo)
|
||||
if err != nil {
|
||||
logger.Critical("Unable to check delegate hooks for repo %-v. ERROR: %v", repo, err)
|
||||
return fmt.Errorf("Unable to check delegate hooks for repo %-v. ERROR: %w", repo, err)
|
||||
}
|
||||
if len(results) > 0 && autofix {
|
||||
logger.Warn("Regenerated hooks for %s", repo.FullName())
|
||||
if err := repository.CreateDelegateHooks(repo.RepoPath()); err != nil {
|
||||
if err := gitrepo.CreateDelegateHooksForRepo(ctx, repo); err != nil {
|
||||
logger.Critical("Unable to recreate delegate hooks for %-v. ERROR: %v", repo, err)
|
||||
return fmt.Errorf("Unable to recreate delegate hooks for %-v. ERROR: %w", repo, err)
|
||||
}
|
||||
|
@ -323,8 +323,9 @@ func (f *AddKeyForm) Validate(req *http.Request, errs binding.Errors) binding.Er
|
||||
|
||||
// AddSecretForm for adding secrets
|
||||
type AddSecretForm struct {
|
||||
Name string `binding:"Required;MaxSize(255)"`
|
||||
Data string `binding:"Required;MaxSize(65535)"`
|
||||
Name string `binding:"Required;MaxSize(255)"`
|
||||
Data string `binding:"Required;MaxSize(65535)"`
|
||||
Description string `binding:"MaxSize(65535)"`
|
||||
}
|
||||
|
||||
// Validate validates the fields
|
||||
@ -334,8 +335,9 @@ func (f *AddSecretForm) Validate(req *http.Request, errs binding.Errors) binding
|
||||
}
|
||||
|
||||
type EditVariableForm struct {
|
||||
Name string `binding:"Required;MaxSize(255)"`
|
||||
Data string `binding:"Required;MaxSize(65535)"`
|
||||
Name string `binding:"Required;MaxSize(255)"`
|
||||
Data string `binding:"Required;MaxSize(65535)"`
|
||||
Description string `binding:"MaxSize(65535)"`
|
||||
}
|
||||
|
||||
func (f *EditVariableForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||
|
@ -1253,6 +1253,8 @@ func GetDiffForRender(ctx context.Context, gitRepo *git.Repository, opts *DiffOp
|
||||
if language.Has() {
|
||||
diffFile.Language = language.Value()
|
||||
}
|
||||
} else {
|
||||
checker = nil // CheckPath fails, it's not impossible to "check" anymore
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
giturl "code.gitea.io/gitea/modules/git/url"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/globallock"
|
||||
"code.gitea.io/gitea/modules/lfs"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
@ -425,6 +426,10 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
|
||||
return parseRemoteUpdateOutput(output, m.GetRemoteName()), true
|
||||
}
|
||||
|
||||
func getRepoPullMirrorLockKey(repoID int64) string {
|
||||
return fmt.Sprintf("repo_pull_mirror_%d", repoID)
|
||||
}
|
||||
|
||||
// SyncPullMirror starts the sync of the pull mirror and schedules the next run.
|
||||
func SyncPullMirror(ctx context.Context, repoID int64) bool {
|
||||
log.Trace("SyncMirrors [repo_id: %v]", repoID)
|
||||
@ -437,6 +442,13 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
|
||||
log.Error("PANIC whilst SyncMirrors[repo_id: %d] Panic: %v\nStacktrace: %s", repoID, err, log.Stack(2))
|
||||
}()
|
||||
|
||||
releaser, err := globallock.Lock(ctx, getRepoPullMirrorLockKey(repoID))
|
||||
if err != nil {
|
||||
log.Error("globallock.Lock(): %v", err)
|
||||
return false
|
||||
}
|
||||
defer releaser()
|
||||
|
||||
m, err := repo_model.GetMirrorByRepoID(ctx, repoID)
|
||||
if err != nil {
|
||||
log.Error("SyncMirrors [repo_id: %v]: unable to GetMirrorByRepoID: %v", repoID, err)
|
||||
|
@ -10,7 +10,6 @@ import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
@ -131,10 +130,10 @@ func GetPullRequestCommitStatusState(ctx context.Context, pr *issues_model.PullR
|
||||
}
|
||||
defer closer.Close()
|
||||
|
||||
if pr.Flow == issues_model.PullRequestFlowGithub && !headGitRepo.IsBranchExist(pr.HeadBranch) {
|
||||
if pr.Flow == issues_model.PullRequestFlowGithub && !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) {
|
||||
return "", errors.New("Head branch does not exist, can not merge")
|
||||
}
|
||||
if pr.Flow == issues_model.PullRequestFlowAGit && !git.IsReferenceExist(ctx, headGitRepo.Path, pr.GetGitRefName()) {
|
||||
if pr.Flow == issues_model.PullRequestFlowAGit && !gitrepo.IsReferenceExist(ctx, pr.HeadRepo, pr.GetGitRefName()) {
|
||||
return "", errors.New("Head branch does not exist, can not merge")
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
)
|
||||
|
||||
func CreateOrUpdateProtectedBranch(ctx context.Context, repo *repo_model.Repository,
|
||||
@ -22,7 +22,8 @@ func CreateOrUpdateProtectedBranch(ctx context.Context, repo *repo_model.Reposit
|
||||
isPlainRule := !git_model.IsRuleNameSpecial(protectBranch.RuleName)
|
||||
var isBranchExist bool
|
||||
if isPlainRule {
|
||||
isBranchExist = git.IsBranchExist(ctx, repo.RepoPath(), protectBranch.RuleName)
|
||||
// TODO: read the database directly to check if the branch exists
|
||||
isBranchExist = gitrepo.IsBranchExist(ctx, repo, protectBranch.RuleName)
|
||||
}
|
||||
|
||||
if isBranchExist {
|
||||
|
@ -491,7 +491,7 @@ func AddTestPullRequestTask(opts TestPullRequestOptions) {
|
||||
for _, pr := range prs {
|
||||
divergence, err := GetDiverging(ctx, pr)
|
||||
if err != nil {
|
||||
if git_model.IsErrBranchNotExist(err) && !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) {
|
||||
if git_model.IsErrBranchNotExist(err) && !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) {
|
||||
log.Warn("Cannot test PR %s/%d: head_branch %s no longer exists", pr.BaseRepo.Name, pr.IssueID, pr.HeadBranch)
|
||||
} else {
|
||||
log.Error("GetDiverging: %v", err)
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
)
|
||||
@ -181,7 +182,7 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest)
|
||||
if err := git.NewCommand("fetch").AddArguments(fetchArgs...).AddDynamicArguments(remoteRepoName, headBranch+":"+trackingBranch).
|
||||
Run(ctx, prCtx.RunOpts()); err != nil {
|
||||
cancel()
|
||||
if !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) {
|
||||
if !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) {
|
||||
return nil, nil, git_model.ErrBranchNotExist{
|
||||
BranchName: pr.HeadBranch,
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Rel
|
||||
var created bool
|
||||
// Only actual create when publish.
|
||||
if !rel.IsDraft {
|
||||
if !gitRepo.IsTagExist(rel.TagName) {
|
||||
if !gitrepo.IsTagExist(ctx, rel.Repo, rel.TagName) {
|
||||
if err := rel.LoadAttributes(ctx); err != nil {
|
||||
log.Error("LoadAttributes: %v", err)
|
||||
return false, err
|
||||
|
@ -115,7 +115,7 @@ func adoptRepository(ctx context.Context, repo *repo_model.Repository, defaultBr
|
||||
return fmt.Errorf("adoptRepository: path does not already exist: %s", repo.FullName())
|
||||
}
|
||||
|
||||
if err := repo_module.CreateDelegateHooks(repo.RepoPath()); err != nil {
|
||||
if err := gitrepo.CreateDelegateHooksForRepo(ctx, repo); err != nil {
|
||||
return fmt.Errorf("createDelegateHooks: %w", err)
|
||||
}
|
||||
|
||||
|
@ -410,11 +410,11 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, doer *user_m
|
||||
return "target_exist", nil
|
||||
}
|
||||
|
||||
if gitRepo.IsBranchExist(to) {
|
||||
if gitrepo.IsBranchExist(ctx, repo, to) {
|
||||
return "target_exist", nil
|
||||
}
|
||||
|
||||
if !gitRepo.IsBranchExist(from) {
|
||||
if !gitrepo.IsBranchExist(ctx, repo, from) {
|
||||
return "from_not_exist", nil
|
||||
}
|
||||
|
||||
@ -618,12 +618,12 @@ func AddAllRepoBranchesToSyncQueue(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetRepoDefaultBranch(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, newBranchName string) error {
|
||||
func SetRepoDefaultBranch(ctx context.Context, repo *repo_model.Repository, newBranchName string) error {
|
||||
if repo.DefaultBranch == newBranchName {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !gitRepo.IsBranchExist(newBranchName) {
|
||||
if !gitrepo.IsBranchExist(ctx, repo, newBranchName) {
|
||||
return git_model.ErrBranchNotExist{
|
||||
BranchName: newBranchName,
|
||||
}
|
||||
|
@ -277,7 +277,7 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
|
||||
}
|
||||
|
||||
if err = initRepository(ctx, doer, repo, opts); err != nil {
|
||||
if err2 := util.RemoveAll(repo.RepoPath()); err2 != nil {
|
||||
if err2 := gitrepo.DeleteRepository(ctx, repo); err2 != nil {
|
||||
log.Error("initRepository: %v", err)
|
||||
return fmt.Errorf(
|
||||
"delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2)
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/models/webhook"
|
||||
actions_module "code.gitea.io/gitea/modules/actions"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/lfs"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
@ -289,8 +290,13 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID
|
||||
// we delete the file but the database rollback, the repository will be broken.
|
||||
|
||||
// Remove repository files.
|
||||
repoPath := repo.RepoPath()
|
||||
system_model.RemoveAllWithNotice(ctx, "Delete repository files", repoPath)
|
||||
if err := gitrepo.DeleteRepository(ctx, repo); err != nil {
|
||||
desc := fmt.Sprintf("Delete repository files [%s]: %v", repo.FullName(), err)
|
||||
// Note we use the db.DefaultContext here rather than passing in a context as the context may be cancelled
|
||||
if err = system_model.CreateNotice(db.DefaultContext, system_model.NoticeRepository, desc); err != nil {
|
||||
log.Error("CreateRepositoryNotice: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove wiki files
|
||||
if repo.HasWiki() {
|
||||
|
@ -7,9 +7,13 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
@ -118,3 +122,98 @@ func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git
|
||||
}
|
||||
return tree, nil
|
||||
}
|
||||
|
||||
func entryModeString(entryMode git.EntryMode) string {
|
||||
switch entryMode {
|
||||
case git.EntryModeBlob:
|
||||
return "blob"
|
||||
case git.EntryModeExec:
|
||||
return "exec"
|
||||
case git.EntryModeSymlink:
|
||||
return "symlink"
|
||||
case git.EntryModeCommit:
|
||||
return "commit" // submodule
|
||||
case git.EntryModeTree:
|
||||
return "tree"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
type TreeViewNode struct {
|
||||
EntryName string `json:"entryName"`
|
||||
EntryMode string `json:"entryMode"`
|
||||
FullPath string `json:"fullPath"`
|
||||
SubmoduleURL string `json:"submoduleUrl,omitempty"`
|
||||
Children []*TreeViewNode `json:"children,omitempty"`
|
||||
}
|
||||
|
||||
func (node *TreeViewNode) sortLevel() int {
|
||||
return util.Iif(node.EntryMode == "tree" || node.EntryMode == "commit", 0, 1)
|
||||
}
|
||||
|
||||
func newTreeViewNodeFromEntry(ctx context.Context, commit *git.Commit, parentDir string, entry *git.TreeEntry) *TreeViewNode {
|
||||
node := &TreeViewNode{
|
||||
EntryName: entry.Name(),
|
||||
EntryMode: entryModeString(entry.Mode()),
|
||||
FullPath: path.Join(parentDir, entry.Name()),
|
||||
}
|
||||
|
||||
if node.EntryMode == "commit" {
|
||||
if subModule, err := commit.GetSubModule(node.FullPath); err != nil {
|
||||
log.Error("GetSubModule: %v", err)
|
||||
} else if subModule != nil {
|
||||
submoduleFile := git.NewCommitSubmoduleFile(subModule.URL, entry.ID.String())
|
||||
webLink := submoduleFile.SubmoduleWebLink(ctx)
|
||||
node.SubmoduleURL = webLink.CommitWebLink
|
||||
}
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
// sortTreeViewNodes list directory first and with alpha sequence
|
||||
func sortTreeViewNodes(nodes []*TreeViewNode) {
|
||||
sort.Slice(nodes, func(i, j int) bool {
|
||||
a, b := nodes[i].sortLevel(), nodes[j].sortLevel()
|
||||
if a != b {
|
||||
return a < b
|
||||
}
|
||||
return nodes[i].EntryName < nodes[j].EntryName
|
||||
})
|
||||
}
|
||||
|
||||
func listTreeNodes(ctx context.Context, commit *git.Commit, tree *git.Tree, treePath, subPath string) ([]*TreeViewNode, error) {
|
||||
entries, err := tree.ListEntries()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
subPathDirName, subPathRemaining, _ := strings.Cut(subPath, "/")
|
||||
nodes := make([]*TreeViewNode, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
node := newTreeViewNodeFromEntry(ctx, commit, treePath, entry)
|
||||
nodes = append(nodes, node)
|
||||
if entry.IsDir() && subPathDirName == entry.Name() {
|
||||
subTreePath := treePath + "/" + node.EntryName
|
||||
if subTreePath[0] == '/' {
|
||||
subTreePath = subTreePath[1:]
|
||||
}
|
||||
subNodes, err := listTreeNodes(ctx, commit, entry.Tree(), subTreePath, subPathRemaining)
|
||||
if err != nil {
|
||||
log.Error("listTreeNodes: %v", err)
|
||||
} else {
|
||||
node.Children = subNodes
|
||||
}
|
||||
}
|
||||
}
|
||||
sortTreeViewNodes(nodes)
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
func GetTreeViewNodes(ctx context.Context, commit *git.Commit, treePath, subPath string) ([]*TreeViewNode, error) {
|
||||
entry, err := commit.GetTreeEntryByPath(treePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return listTreeNodes(ctx, commit, entry.Tree(), treePath, subPath)
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/services/contexttest"
|
||||
|
||||
@ -50,3 +51,51 @@ func TestGetTreeBySHA(t *testing.T) {
|
||||
|
||||
assert.EqualValues(t, expectedTree, tree)
|
||||
}
|
||||
|
||||
func TestGetTreeViewNodes(t *testing.T) {
|
||||
unittest.PrepareTestEnv(t)
|
||||
ctx, _ := contexttest.MockContext(t, "user2/repo1")
|
||||
ctx.Repo.RefFullName = git.RefNameFromBranch("sub-home-md-img-check")
|
||||
contexttest.LoadRepo(t, ctx, 1)
|
||||
contexttest.LoadRepoCommit(t, ctx)
|
||||
contexttest.LoadUser(t, ctx, 2)
|
||||
contexttest.LoadGitRepo(t, ctx)
|
||||
defer ctx.Repo.GitRepo.Close()
|
||||
|
||||
treeNodes, err := GetTreeViewNodes(ctx, ctx.Repo.Commit, "", "")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []*TreeViewNode{
|
||||
{
|
||||
EntryName: "docs",
|
||||
EntryMode: "tree",
|
||||
FullPath: "docs",
|
||||
},
|
||||
}, treeNodes)
|
||||
|
||||
treeNodes, err = GetTreeViewNodes(ctx, ctx.Repo.Commit, "", "docs/README.md")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []*TreeViewNode{
|
||||
{
|
||||
EntryName: "docs",
|
||||
EntryMode: "tree",
|
||||
FullPath: "docs",
|
||||
Children: []*TreeViewNode{
|
||||
{
|
||||
EntryName: "README.md",
|
||||
EntryMode: "blob",
|
||||
FullPath: "docs/README.md",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, treeNodes)
|
||||
|
||||
treeNodes, err = GetTreeViewNodes(ctx, ctx.Repo.Commit, "docs", "README.md")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []*TreeViewNode{
|
||||
{
|
||||
EntryName: "README.md",
|
||||
EntryMode: "blob",
|
||||
FullPath: "docs/README.md",
|
||||
},
|
||||
}, treeNodes)
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
|
||||
|
||||
// As the transaction will be failed and hence database changes will be destroyed we only need
|
||||
// to delete the related repository on the filesystem
|
||||
if errDelete := util.RemoveAll(repo.RepoPath()); errDelete != nil {
|
||||
if errDelete := gitrepo.DeleteRepository(ctx, repo); errDelete != nil {
|
||||
log.Error("Failed to remove fork repo")
|
||||
}
|
||||
}
|
||||
@ -154,8 +154,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
|
||||
if opts.SingleBranch != "" {
|
||||
cloneCmd.AddArguments("--single-branch", "--branch").AddDynamicArguments(opts.SingleBranch)
|
||||
}
|
||||
repoPath := repo_model.RepoPath(owner.Name, repo.Name)
|
||||
if stdout, _, err := cloneCmd.AddDynamicArguments(oldRepoPath, repoPath).
|
||||
if stdout, _, err := cloneCmd.AddDynamicArguments(oldRepoPath, repo.RepoPath()).
|
||||
RunStdBytes(txCtx, &git.RunOpts{Timeout: 10 * time.Minute}); err != nil {
|
||||
log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", repo, opts.BaseRepo, stdout, err)
|
||||
return fmt.Errorf("git clone: %w", err)
|
||||
@ -166,12 +165,12 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
|
||||
}
|
||||
|
||||
if stdout, _, err := git.NewCommand("update-server-info").
|
||||
RunStdString(txCtx, &git.RunOpts{Dir: repoPath}); err != nil {
|
||||
RunStdString(txCtx, &git.RunOpts{Dir: repo.RepoPath()}); err != nil {
|
||||
log.Error("Fork Repository (git update-server-info) failed for %v:\nStdout: %s\nError: %v", repo, stdout, err)
|
||||
return fmt.Errorf("git update-server-info: %w", err)
|
||||
}
|
||||
|
||||
if err = repo_module.CreateDelegateHooks(repoPath); err != nil {
|
||||
if err = gitrepo.CreateDelegateHooksForRepo(ctx, repo); err != nil {
|
||||
return fmt.Errorf("createDelegateHooks: %w", err)
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@ import (
|
||||
"code.gitea.io/gitea/models/webhook"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
@ -32,11 +31,11 @@ func SyncRepositoryHooks(ctx context.Context) error {
|
||||
default:
|
||||
}
|
||||
|
||||
if err := repo_module.CreateDelegateHooks(repo.RepoPath()); err != nil {
|
||||
if err := gitrepo.CreateDelegateHooksForRepo(ctx, repo); err != nil {
|
||||
return fmt.Errorf("SyncRepositoryHook: %w", err)
|
||||
}
|
||||
if repo.HasWiki() {
|
||||
if err := repo_module.CreateDelegateHooks(repo.WikiPath()); err != nil {
|
||||
if err := gitrepo.CreateDelegateHooksForWiki(ctx, repo); err != nil {
|
||||
return fmt.Errorf("SyncRepositoryHook: %w", err)
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,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/gitrepo"
|
||||
"code.gitea.io/gitea/modules/lfs"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/migration"
|
||||
@ -264,17 +265,16 @@ func cleanUpMigrateGitConfig(ctx context.Context, repoPath string) error {
|
||||
|
||||
// CleanUpMigrateInfo finishes migrating repository and/or wiki with things that don't need to be done for mirrors.
|
||||
func CleanUpMigrateInfo(ctx context.Context, repo *repo_model.Repository) (*repo_model.Repository, error) {
|
||||
repoPath := repo.RepoPath()
|
||||
if err := repo_module.CreateDelegateHooks(repoPath); err != nil {
|
||||
if err := gitrepo.CreateDelegateHooksForRepo(ctx, repo); err != nil {
|
||||
return repo, fmt.Errorf("createDelegateHooks: %w", err)
|
||||
}
|
||||
if repo.HasWiki() {
|
||||
if err := repo_module.CreateDelegateHooks(repo.WikiPath()); err != nil {
|
||||
if err := gitrepo.CreateDelegateHooksForWiki(ctx, repo); err != nil {
|
||||
return repo, fmt.Errorf("createDelegateHooks.(wiki): %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
_, _, err := git.NewCommand("remote", "rm", "origin").RunStdString(ctx, &git.RunOpts{Dir: repoPath})
|
||||
_, _, err := git.NewCommand("remote", "rm", "origin").RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()})
|
||||
if err != nil && !git.IsRemoteNotExistError(err) {
|
||||
return repo, fmt.Errorf("CleanUpMigrateInfo: %w", err)
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
project_model "code.gitea.io/gitea/models/project"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/globallock"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
@ -335,8 +336,7 @@ func changeRepositoryName(ctx context.Context, repo *repo_model.Repository, newR
|
||||
}
|
||||
}
|
||||
|
||||
newRepoPath := repo_model.RepoPath(repo.Owner.Name, newRepoName)
|
||||
if err = util.Rename(repo.RepoPath(), newRepoPath); err != nil {
|
||||
if err = gitrepo.RenameRepository(ctx, repo, newRepoName); err != nil {
|
||||
return fmt.Errorf("rename repository directory: %w", err)
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
secret_model "code.gitea.io/gitea/models/secret"
|
||||
)
|
||||
|
||||
func CreateOrUpdateSecret(ctx context.Context, ownerID, repoID int64, name, data string) (*secret_model.Secret, bool, error) {
|
||||
func CreateOrUpdateSecret(ctx context.Context, ownerID, repoID int64, name, data, description string) (*secret_model.Secret, bool, error) {
|
||||
if err := ValidateName(name); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@ -25,14 +25,14 @@ func CreateOrUpdateSecret(ctx context.Context, ownerID, repoID int64, name, data
|
||||
}
|
||||
|
||||
if len(s) == 0 {
|
||||
s, err := secret_model.InsertEncryptedSecret(ctx, ownerID, repoID, name, data)
|
||||
s, err := secret_model.InsertEncryptedSecret(ctx, ownerID, repoID, name, data, description)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return s, true, nil
|
||||
}
|
||||
|
||||
if err := secret_model.UpdateSecret(ctx, s[0].ID, data); err != nil {
|
||||
if err := secret_model.UpdateSecret(ctx, s[0].ID, data, description); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
|
@ -604,7 +604,7 @@ func (m *webhookNotifier) IssueChangeMilestone(ctx context.Context, doer *user_m
|
||||
|
||||
func (m *webhookNotifier) PushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
|
||||
apiPusher := convert.ToUser(ctx, pusher, nil)
|
||||
apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo.RepoPath(), repo.HTMLURL())
|
||||
apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo)
|
||||
if err != nil {
|
||||
log.Error("commits.ToAPIPayloadCommits failed: %v", err)
|
||||
return
|
||||
@ -843,7 +843,7 @@ func (m *webhookNotifier) DeleteRelease(ctx context.Context, doer *user_model.Us
|
||||
|
||||
func (m *webhookNotifier) SyncPushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
|
||||
apiPusher := convert.ToUser(ctx, pusher, nil)
|
||||
apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo.RepoPath(), repo.HTMLURL())
|
||||
apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo)
|
||||
if err != nil {
|
||||
log.Error("commits.ToAPIPayloadCommits failed: %v", err)
|
||||
return
|
||||
@ -867,7 +867,7 @@ func (m *webhookNotifier) SyncPushCommits(ctx context.Context, pusher *user_mode
|
||||
|
||||
func (m *webhookNotifier) CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, commit *repository.PushCommit, sender *user_model.User, status *git_model.CommitStatus) {
|
||||
apiSender := convert.ToUser(ctx, sender, nil)
|
||||
apiCommit, err := repository.ToAPIPayloadCommit(ctx, map[string]*user_model.User{}, repo.RepoPath(), repo.HTMLURL(), commit)
|
||||
apiCommit, err := repository.ToAPIPayloadCommit(ctx, map[string]*user_model.User{}, repo, commit)
|
||||
if err != nil {
|
||||
log.Error("commits.ToAPIPayloadCommits failed: %v", err)
|
||||
return
|
||||
|
@ -41,7 +41,7 @@ func InitWiki(ctx context.Context, repo *repo_model.Repository) error {
|
||||
|
||||
if err := git.InitRepository(ctx, repo.WikiPath(), true, repo.ObjectFormatName); err != nil {
|
||||
return fmt.Errorf("InitRepository: %w", err)
|
||||
} else if err = repo_module.CreateDelegateHooks(repo.WikiPath()); err != nil {
|
||||
} else if err = gitrepo.CreateDelegateHooksForWiki(ctx, repo); err != nil {
|
||||
return fmt.Errorf("createDelegateHooks: %w", err)
|
||||
} else if _, _, err = git.NewCommand("symbolic-ref", "HEAD").AddDynamicArguments(git.BranchPrefix+repo.DefaultWikiBranch).RunStdString(ctx, &git.RunOpts{Dir: repo.WikiPath()}); err != nil {
|
||||
return fmt.Errorf("unable to set default wiki branch to %q: %w", repo.DefaultWikiBranch, err)
|
||||
@ -100,7 +100,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
|
||||
return fmt.Errorf("InitWiki: %w", err)
|
||||
}
|
||||
|
||||
hasDefaultBranch := git.IsBranchExist(ctx, repo.WikiPath(), repo.DefaultWikiBranch)
|
||||
hasDefaultBranch := gitrepo.IsWikiBranchExist(ctx, repo, repo.DefaultWikiBranch)
|
||||
|
||||
basePath, err := repo_module.CreateTemporaryPath("update-wiki")
|
||||
if err != nil {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user