mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-09 03:27:52 +02:00
Merge branch 'main' into zzc/dev/act_runner_exec
This commit is contained in:
commit
62d3f35cb3
@ -25,10 +25,11 @@ env:
|
||||
es2022: true
|
||||
node: true
|
||||
|
||||
globals:
|
||||
__webpack_public_path__: true
|
||||
|
||||
overrides:
|
||||
- files: ["web_src/**/*"]
|
||||
globals:
|
||||
__webpack_public_path__: true
|
||||
process: false # https://github.com/webpack/webpack/issues/15833
|
||||
- files: ["web_src/**/*", "docs/**/*"]
|
||||
env:
|
||||
browser: true
|
||||
|
||||
27
.github/workflows/files-changed.yml
vendored
27
.github/workflows/files-changed.yml
vendored
@ -15,6 +15,8 @@ on:
|
||||
value: ${{ jobs.detect.outputs.templates }}
|
||||
docker:
|
||||
value: ${{ jobs.detect.outputs.docker }}
|
||||
swagger:
|
||||
value: ${{ jobs.detect.outputs.swagger }}
|
||||
|
||||
jobs:
|
||||
detect:
|
||||
@ -27,6 +29,7 @@ jobs:
|
||||
actions: ${{ steps.changes.outputs.actions }}
|
||||
templates: ${{ steps.changes.outputs.templates }}
|
||||
docker: ${{ steps.changes.outputs.docker }}
|
||||
swagger: ${{ steps.changes.outputs.swagger }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dorny/paths-filter@v2
|
||||
@ -37,6 +40,7 @@ jobs:
|
||||
backend:
|
||||
- "**/*.go"
|
||||
- "templates/**/*.tmpl"
|
||||
- "assets/emoji.json"
|
||||
- "go.mod"
|
||||
- "go.sum"
|
||||
- "Makefile"
|
||||
@ -44,6 +48,7 @@ jobs:
|
||||
frontend:
|
||||
- "**/*.js"
|
||||
- "web_src/**"
|
||||
- "assets/emoji.json"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
- "Makefile"
|
||||
@ -64,22 +69,6 @@ jobs:
|
||||
- "Dockerfile.rootless"
|
||||
- "docker/**"
|
||||
- "Makefile"
|
||||
- name: set outputs result
|
||||
id: changes
|
||||
run: |
|
||||
if [ "$ACT_EXEC" == 'true' ]; then
|
||||
# set default value for 'act_runner exec'
|
||||
{ echo "backend=true" ; \
|
||||
echo "frontend=true" ; \
|
||||
echo "docs=true" ; \
|
||||
echo "actions=true" ; \
|
||||
echo "templates=true" ; \
|
||||
echo "docker=true" ; } >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
{ echo "backend=${{ steps.changes_check.outputs.backend }}" ; \
|
||||
echo "frontend=${{ steps.changes_check.outputs.frontend }}" ; \
|
||||
echo "docs=${{ steps.changes_check.outputs.docs }}" ; \
|
||||
echo "actions=${{ steps.changes_check.outputs.actions }}" ; \
|
||||
echo "actions=${{ steps.changes_check.outputs.templates }}" ; \
|
||||
echo "actions=${{ steps.changes_check.outputs.docker }}" ; } >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
swagger:
|
||||
- "templates/swagger/v1_json.tmpl"
|
||||
|
||||
12
.github/workflows/pull-compliance.yml
vendored
12
.github/workflows/pull-compliance.yml
vendored
@ -39,6 +39,18 @@ jobs:
|
||||
- run: make deps-py
|
||||
- run: make lint-templates
|
||||
|
||||
lint-swagger:
|
||||
if: needs.files-changed.outputs.swagger == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
- run: make deps-frontend
|
||||
- run: make lint-swagger
|
||||
|
||||
lint-go-windows:
|
||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||
needs: files-changed
|
||||
|
||||
@ -50,7 +50,7 @@ rules:
|
||||
declaration-no-important: null
|
||||
declaration-property-max-values: null
|
||||
declaration-property-unit-allowed-list: null
|
||||
declaration-property-unit-disallowed-list: null
|
||||
declaration-property-unit-disallowed-list: {line-height: [em]}
|
||||
declaration-property-value-allowed-list: null
|
||||
declaration-property-value-disallowed-list: null
|
||||
declaration-property-value-no-unknown: true
|
||||
|
||||
19
Makefile
19
Makefile
@ -226,6 +226,8 @@ help:
|
||||
@echo " - test-frontend test frontend files"
|
||||
@echo " - test-backend test backend files"
|
||||
@echo " - test-e2e[\#TestSpecificName] test end to end using playwright"
|
||||
@echo " - update-js update js dependencies"
|
||||
@echo " - update-py update py dependencies"
|
||||
@echo " - webpack build webpack files"
|
||||
@echo " - svg build svg files"
|
||||
@echo " - fomantic build fomantic files"
|
||||
@ -358,10 +360,10 @@ lint: lint-frontend lint-backend
|
||||
lint-fix: lint-frontend-fix lint-backend-fix
|
||||
|
||||
.PHONY: lint-frontend
|
||||
lint-frontend: lint-js lint-css lint-md lint-swagger
|
||||
lint-frontend: lint-js lint-css
|
||||
|
||||
.PHONY: lint-frontend-fix
|
||||
lint-frontend-fix: lint-js-fix lint-css-fix lint-md lint-swagger
|
||||
lint-frontend-fix: lint-js-fix lint-css-fix
|
||||
|
||||
.PHONY: lint-backend
|
||||
lint-backend: lint-go lint-go-vet lint-editorconfig
|
||||
@ -924,13 +926,20 @@ node_modules: package-lock.json
|
||||
poetry install
|
||||
@touch .venv
|
||||
|
||||
.PHONY: npm-update
|
||||
npm-update: node-check | node_modules
|
||||
npx updates -cu
|
||||
.PHONY: update-js
|
||||
update-js: node-check | node_modules
|
||||
npx updates -u -f package.json
|
||||
rm -rf node_modules package-lock.json
|
||||
npm install --package-lock
|
||||
@touch node_modules
|
||||
|
||||
.PHONY: update-py
|
||||
update-py: node-check | node_modules
|
||||
npx updates -u -f pyproject.toml
|
||||
rm -rf .venv poetry.lock
|
||||
poetry install
|
||||
@touch .venv
|
||||
|
||||
.PHONY: fomantic
|
||||
fomantic:
|
||||
rm -rf $(FOMANTIC_WORK_DIR)/build
|
||||
|
||||
2
assets/emoji.json
generated
2
assets/emoji.json
generated
File diff suppressed because one or more lines are too long
@ -25,7 +25,7 @@ import (
|
||||
|
||||
const (
|
||||
gemojiURL = "https://raw.githubusercontent.com/github/gemoji/master/db/emoji.json"
|
||||
maxUnicodeVersion = 14
|
||||
maxUnicodeVersion = 15
|
||||
)
|
||||
|
||||
var flagOut = flag.String("o", "modules/emoji/emoji_data.go", "out")
|
||||
|
||||
18
cmd/cmd.go
18
cmd/cmd.go
@ -106,5 +106,21 @@ func setupConsoleLogger(level log.Level, colorize bool, out io.Writer) {
|
||||
WriterOption: log.WriterConsoleOption{Stderr: out == os.Stderr},
|
||||
}
|
||||
writer := log.NewEventWriterConsole("console-default", writeMode)
|
||||
log.GetManager().GetLogger(log.DEFAULT).RemoveAllWriters().AddWriters(writer)
|
||||
log.GetManager().GetLogger(log.DEFAULT).ReplaceAllWriters(writer)
|
||||
}
|
||||
|
||||
// PrepareConsoleLoggerLevel by default, use INFO level for console logger, but some sub-commands (for git/ssh protocol) shouldn't output any log to stdout.
|
||||
// Any log appears in git stdout pipe will break the git protocol, eg: client can't push and hangs forever.
|
||||
func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(*cli.Context) error {
|
||||
return func(c *cli.Context) error {
|
||||
level := defaultLevel
|
||||
if c.Bool("quiet") || c.GlobalBoolT("quiet") {
|
||||
level = log.FATAL
|
||||
}
|
||||
if c.Bool("debug") || c.GlobalBool("debug") || c.Bool("verbose") || c.GlobalBool("verbose") {
|
||||
level = log.TRACE
|
||||
}
|
||||
log.SetConsoleLogger(log.DEFAULT, "console-default", level)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,7 +151,7 @@ func setupDoctorDefaultLogger(ctx *cli.Context, colorize bool) {
|
||||
log.FallbackErrorf("unable to create file log writer: %v", err)
|
||||
return
|
||||
}
|
||||
log.GetManager().GetLogger(log.DEFAULT).RemoveAllWriters().AddWriters(writer)
|
||||
log.GetManager().GetLogger(log.DEFAULT).ReplaceAllWriters(writer)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -22,9 +22,9 @@ import (
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// Cmdembedded represents the available extract sub-command.
|
||||
// CmdEmbedded represents the available extract sub-command.
|
||||
var (
|
||||
Cmdembedded = cli.Command{
|
||||
CmdEmbedded = cli.Command{
|
||||
Name: "embedded",
|
||||
Usage: "Extract embedded resources",
|
||||
Description: "A command for extracting embedded resources, like templates and images",
|
||||
|
||||
@ -15,6 +15,7 @@ import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@ -32,6 +33,7 @@ var (
|
||||
Name: "hook",
|
||||
Usage: "Delegate commands to corresponding Git hooks",
|
||||
Description: "This should only be called by Git",
|
||||
Before: PrepareConsoleLoggerLevel(log.FATAL),
|
||||
Subcommands: []cli.Command{
|
||||
subcmdHookPreReceive,
|
||||
subcmdHookUpdate,
|
||||
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
@ -17,6 +18,7 @@ import (
|
||||
var CmdKeys = cli.Command{
|
||||
Name: "keys",
|
||||
Usage: "This command queries the Gitea database to get the authorized command for a given ssh key fingerprint",
|
||||
Before: PrepareConsoleLoggerLevel(log.FATAL),
|
||||
Action: runKeys,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
|
||||
@ -44,6 +44,7 @@ var CmdServ = cli.Command{
|
||||
Name: "serv",
|
||||
Usage: "This command should only be called by SSH shell",
|
||||
Description: "Serv provides access auth for repositories",
|
||||
Before: PrepareConsoleLoggerLevel(log.FATAL),
|
||||
Action: runServ,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
|
||||
@ -35,6 +35,7 @@ var CmdWeb = cli.Command{
|
||||
Usage: "Start Gitea web server",
|
||||
Description: `Gitea web server is the only thing you need to run,
|
||||
and it takes care of all the other things for you`,
|
||||
Before: PrepareConsoleLoggerLevel(log.INFO),
|
||||
Action: runWeb,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
@ -206,11 +207,6 @@ func servePprof() {
|
||||
}
|
||||
|
||||
func runWeb(ctx *cli.Context) error {
|
||||
if ctx.Bool("verbose") {
|
||||
setupConsoleLogger(log.TRACE, log.CanColorStdout, os.Stdout)
|
||||
} else if ctx.Bool("quiet") {
|
||||
setupConsoleLogger(log.FATAL, log.CanColorStdout, os.Stdout)
|
||||
}
|
||||
defer func() {
|
||||
if panicked := recover(); panicked != nil {
|
||||
log.Fatal("PANIC: %v\n%s", panicked, log.Stack(2))
|
||||
|
||||
@ -164,3 +164,23 @@ Although we would like to provide more options, our limited manpower means that
|
||||
However, both Gitea and act runner are completely open source, so anyone can create a new/better implementation.
|
||||
We support your choice, no matter how you decide.
|
||||
In case you fork act runner to create your own version: Please contribute the changes back if you can and if you think your changes will help others as well.
|
||||
|
||||
## What workflow trigger events does Gitea support?
|
||||
|
||||
All events listed in this table are supported events and are compatible with GitHub.
|
||||
For events supported only by GitHub, see GitHub's [documentation](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows).
|
||||
|
||||
| trigger event | activity types |
|
||||
|-----------------------------|--------------------------------------------------------------------------------------------------------------------------|
|
||||
| create | not applicable |
|
||||
| delete | not applicable |
|
||||
| fork | not applicable |
|
||||
| gollum | not applicable |
|
||||
| push | not applicable |
|
||||
| issues | `opened`, `edited`, `closed`, `reopened`, `assigned`, `unassigned`, `milestoned`, `demilestoned`, `labeled`, `unlabeled` |
|
||||
| issue_comment | `created`, `edited`, `deleted` |
|
||||
| pull_request | `opened`, `edited`, `closed`, `reopened`, `assigned`, `unassigned`, `synchronize`, `labeled`, `unlabeled` |
|
||||
| pull_request_review | `submitted`, `edited` |
|
||||
| pull_request_review_comment | `created`, `edited` |
|
||||
| release | `published`, `edited` |
|
||||
| registry_package | `published` |
|
||||
|
||||
@ -164,3 +164,23 @@ defaults:
|
||||
然而,无论您如何决定,Gitea 和act runner都是完全开源的,所以任何人都可以创建一个新的/更好的实现。
|
||||
我们支持您的选择,无论您如何决定。
|
||||
如果您选择分支act runner来创建自己的版本,请在您认为您的更改对其他人也有帮助的情况下贡献这些更改。
|
||||
|
||||
## Gitea 支持哪些工作流触发事件?
|
||||
|
||||
表格中列出的所有事件都是支持的,并且与 GitHub 兼容。
|
||||
对于仅 GitHub 支持的事件,请参阅 GitHub 的[文档](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows)。
|
||||
|
||||
| 触发事件 | 活动类型 |
|
||||
|-----------------------------|--------------------------------------------------------------------------------------------------------------------------|
|
||||
| create | 不适用 |
|
||||
| delete | 不适用 |
|
||||
| fork | 不适用 |
|
||||
| gollum | 不适用 |
|
||||
| push | 不适用 |
|
||||
| issues | `opened`, `edited`, `closed`, `reopened`, `assigned`, `unassigned`, `milestoned`, `demilestoned`, `labeled`, `unlabeled` |
|
||||
| issue_comment | `created`, `edited`, `deleted` |
|
||||
| pull_request | `opened`, `edited`, `closed`, `reopened`, `assigned`, `unassigned`, `synchronize`, `labeled`, `unlabeled` |
|
||||
| pull_request_review | `submitted`, `edited` |
|
||||
| pull_request_review_comment | `created`, `edited` |
|
||||
| release | `published`, `edited` |
|
||||
| registry_package | `published` |
|
||||
|
||||
30
main.go
30
main.go
@ -87,28 +87,36 @@ func main() {
|
||||
app.Description = `By default, Gitea will start serving using the web-server with no argument, which can alternatively be run by running the subcommand "web".`
|
||||
app.Version = Version + formatBuiltWith()
|
||||
app.EnableBashCompletion = true
|
||||
app.Commands = []cli.Command{
|
||||
|
||||
// these sub-commands need to use config file
|
||||
subCmdWithIni := []cli.Command{
|
||||
cmd.CmdWeb,
|
||||
cmd.CmdServ,
|
||||
cmd.CmdHook,
|
||||
cmd.CmdDump,
|
||||
cmd.CmdCert,
|
||||
cmd.CmdAdmin,
|
||||
cmd.CmdGenerate,
|
||||
cmd.CmdMigrate,
|
||||
cmd.CmdKeys,
|
||||
cmd.CmdConvert,
|
||||
cmd.CmdDoctor,
|
||||
cmd.CmdManager,
|
||||
cmd.Cmdembedded,
|
||||
cmd.CmdEmbedded,
|
||||
cmd.CmdMigrateStorage,
|
||||
cmd.CmdDocs,
|
||||
cmd.CmdDumpRepository,
|
||||
cmd.CmdRestoreRepository,
|
||||
cmd.CmdActions,
|
||||
cmdHelp, // TODO: the "help" sub-command was used to show the more information for "work path" and "custom config", in the future, it should avoid doing so
|
||||
}
|
||||
// these sub-commands do not need the config file, and they do not depend on any path or environment variable.
|
||||
subCmdStandalone := []cli.Command{
|
||||
cmd.CmdCert,
|
||||
cmd.CmdGenerate,
|
||||
cmd.CmdDocs,
|
||||
}
|
||||
|
||||
// default configuration flags
|
||||
// shared configuration flags, they are for global and for each sub-command at the same time
|
||||
// eg: such command is valid: "./gitea --config /tmp/app.ini web --config /tmp/app.ini", while it's discouraged indeed
|
||||
// keep in mind that the short flags like "-C", "-c" and "-w" are globally polluted, they can't be used for sub-commands anymore.
|
||||
globalFlags := []cli.Flag{
|
||||
cli.HelpFlag,
|
||||
cli.StringFlag{
|
||||
@ -128,13 +136,15 @@ func main() {
|
||||
|
||||
// Set the default to be equivalent to cmdWeb and add the default flags
|
||||
app.Flags = append(app.Flags, globalFlags...)
|
||||
app.Flags = append(app.Flags, cmd.CmdWeb.Flags...)
|
||||
app.Flags = append(app.Flags, cmd.CmdWeb.Flags...) // TODO: the web flags polluted the global flags, they are not really global flags
|
||||
app.Action = prepareWorkPathAndCustomConf(cmd.CmdWeb.Action)
|
||||
app.HideHelp = true // use our own help action to show helps (with more information like default config)
|
||||
app.Commands = append(app.Commands, cmdHelp)
|
||||
for i := range app.Commands {
|
||||
prepareSubcommands(&app.Commands[i], globalFlags)
|
||||
app.Before = cmd.PrepareConsoleLoggerLevel(log.INFO)
|
||||
for i := range subCmdWithIni {
|
||||
prepareSubcommands(&subCmdWithIni[i], globalFlags)
|
||||
}
|
||||
app.Commands = append(app.Commands, subCmdWithIni...)
|
||||
app.Commands = append(app.Commands, subCmdStandalone...)
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
|
||||
@ -344,6 +344,9 @@ func UpdateTask(ctx context.Context, task *ActionTask, cols ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateTaskByState updates the task by the state.
|
||||
// It will always update the task if the state is not final, even there is no change.
|
||||
// So it will update ActionTask.Updated to avoid the task being judged as a zombie task.
|
||||
func UpdateTaskByState(ctx context.Context, state *runnerv1.TaskState) (*ActionTask, error) {
|
||||
stepStates := map[int64]*runnerv1.StepState{}
|
||||
for _, v := range state.Steps {
|
||||
@ -384,6 +387,12 @@ func UpdateTaskByState(ctx context.Context, state *runnerv1.TaskState) (*ActionT
|
||||
}, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Force update ActionTask.Updated to avoid the task being judged as a zombie task
|
||||
task.Updated = timeutil.TimeStampNow()
|
||||
if err := UpdateTask(ctx, task, "updated"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := task.LoadAttributes(ctx); err != nil {
|
||||
|
||||
@ -20,6 +20,10 @@ const (
|
||||
SearchOrderByNewest SearchOrderBy = "created_unix DESC"
|
||||
SearchOrderBySize SearchOrderBy = "size ASC"
|
||||
SearchOrderBySizeReverse SearchOrderBy = "size DESC"
|
||||
SearchOrderByGitSize SearchOrderBy = "git_size ASC"
|
||||
SearchOrderByGitSizeReverse SearchOrderBy = "git_size DESC"
|
||||
SearchOrderByLFSSize SearchOrderBy = "lfs_size ASC"
|
||||
SearchOrderByLFSSizeReverse SearchOrderBy = "lfs_size DESC"
|
||||
SearchOrderByID SearchOrderBy = "id ASC"
|
||||
SearchOrderByIDReverse SearchOrderBy = "id DESC"
|
||||
SearchOrderByStars SearchOrderBy = "num_stars ASC"
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@ -21,6 +22,7 @@ var defaultFileBlockSize int64 = 32 * 1024
|
||||
type File interface {
|
||||
io.ReadWriteCloser
|
||||
io.Seeker
|
||||
fs.File
|
||||
}
|
||||
|
||||
type file struct {
|
||||
@ -193,10 +195,26 @@ func (f *file) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *file) Stat() (os.FileInfo, error) {
|
||||
if f.metaID == 0 {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
fileMeta, err := findFileMetaByID(f.ctx, f.metaID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fileMeta, nil
|
||||
}
|
||||
|
||||
func timeToFileTimestamp(t time.Time) int64 {
|
||||
return t.UnixMicro()
|
||||
}
|
||||
|
||||
func fileTimestampToTime(timestamp int64) time.Time {
|
||||
return time.UnixMicro(timestamp)
|
||||
}
|
||||
|
||||
func (f *file) loadMetaByPath() (*dbfsMeta, error) {
|
||||
var fileMeta dbfsMeta
|
||||
if ok, err := db.GetEngine(f.ctx).Where("full_path = ?", f.fullPath).Get(&fileMeta); err != nil {
|
||||
|
||||
@ -5,7 +5,10 @@ package dbfs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
)
|
||||
@ -100,3 +103,29 @@ func Remove(ctx context.Context, name string) error {
|
||||
defer f.Close()
|
||||
return f.delete()
|
||||
}
|
||||
|
||||
var _ fs.FileInfo = (*dbfsMeta)(nil)
|
||||
|
||||
func (m *dbfsMeta) Name() string {
|
||||
return path.Base(m.FullPath)
|
||||
}
|
||||
|
||||
func (m *dbfsMeta) Size() int64 {
|
||||
return m.FileSize
|
||||
}
|
||||
|
||||
func (m *dbfsMeta) Mode() fs.FileMode {
|
||||
return os.ModePerm
|
||||
}
|
||||
|
||||
func (m *dbfsMeta) ModTime() time.Time {
|
||||
return fileTimestampToTime(m.ModifyTimestamp)
|
||||
}
|
||||
|
||||
func (m *dbfsMeta) IsDir() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *dbfsMeta) Sys() any {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -111,6 +111,19 @@ func TestDbfsBasic(t *testing.T) {
|
||||
|
||||
_, err = OpenFile(db.DefaultContext, "test2.txt", os.O_RDONLY)
|
||||
assert.Error(t, err)
|
||||
|
||||
// test stat
|
||||
f, err = OpenFile(db.DefaultContext, "test/test.txt", os.O_RDWR|os.O_CREATE)
|
||||
assert.NoError(t, err)
|
||||
stat, err := f.Stat()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "test.txt", stat.Name())
|
||||
assert.EqualValues(t, 0, stat.Size())
|
||||
_, err = f.Write([]byte("0123456789"))
|
||||
assert.NoError(t, err)
|
||||
stat, err = f.Stat()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 10, stat.Size())
|
||||
}
|
||||
|
||||
func TestDbfsReadWrite(t *testing.T) {
|
||||
|
||||
@ -318,90 +318,6 @@ func (err ErrFilePathProtected) Unwrap() error {
|
||||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// __________ .__
|
||||
// \______ \____________ ____ ____ | |__
|
||||
// | | _/\_ __ \__ \ / \_/ ___\| | \
|
||||
// | | \ | | \// __ \| | \ \___| Y \
|
||||
// |______ / |__| (____ /___| /\___ >___| /
|
||||
// \/ \/ \/ \/ \/
|
||||
|
||||
// ErrBranchDoesNotExist represents an error that branch with such name does not exist.
|
||||
type ErrBranchDoesNotExist struct {
|
||||
BranchName string
|
||||
}
|
||||
|
||||
// IsErrBranchDoesNotExist checks if an error is an ErrBranchDoesNotExist.
|
||||
func IsErrBranchDoesNotExist(err error) bool {
|
||||
_, ok := err.(ErrBranchDoesNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrBranchDoesNotExist) Error() string {
|
||||
return fmt.Sprintf("branch does not exist [name: %s]", err.BranchName)
|
||||
}
|
||||
|
||||
func (err ErrBranchDoesNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrBranchAlreadyExists represents an error that branch with such name already exists.
|
||||
type ErrBranchAlreadyExists struct {
|
||||
BranchName string
|
||||
}
|
||||
|
||||
// IsErrBranchAlreadyExists checks if an error is an ErrBranchAlreadyExists.
|
||||
func IsErrBranchAlreadyExists(err error) bool {
|
||||
_, ok := err.(ErrBranchAlreadyExists)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrBranchAlreadyExists) Error() string {
|
||||
return fmt.Sprintf("branch already exists [name: %s]", err.BranchName)
|
||||
}
|
||||
|
||||
func (err ErrBranchAlreadyExists) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrBranchNameConflict represents an error that branch name conflicts with other branch.
|
||||
type ErrBranchNameConflict struct {
|
||||
BranchName string
|
||||
}
|
||||
|
||||
// IsErrBranchNameConflict checks if an error is an ErrBranchNameConflict.
|
||||
func IsErrBranchNameConflict(err error) bool {
|
||||
_, ok := err.(ErrBranchNameConflict)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrBranchNameConflict) Error() string {
|
||||
return fmt.Sprintf("branch conflicts with existing branch [name: %s]", err.BranchName)
|
||||
}
|
||||
|
||||
func (err ErrBranchNameConflict) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrBranchesEqual represents an error that branch name conflicts with other branch.
|
||||
type ErrBranchesEqual struct {
|
||||
BaseBranchName string
|
||||
HeadBranchName string
|
||||
}
|
||||
|
||||
// IsErrBranchesEqual checks if an error is an ErrBranchesEqual.
|
||||
func IsErrBranchesEqual(err error) bool {
|
||||
_, ok := err.(ErrBranchesEqual)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrBranchesEqual) Error() string {
|
||||
return fmt.Sprintf("branches are equal [head: %sm base: %s]", err.HeadBranchName, err.BaseBranchName)
|
||||
}
|
||||
|
||||
func (err ErrBranchesEqual) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// ErrDisallowedToMerge represents an error that a branch is protected and the current user is not allowed to modify it.
|
||||
type ErrDisallowedToMerge struct {
|
||||
Reason string
|
||||
|
||||
47
models/fixtures/branch.yml
Normal file
47
models/fixtures/branch.yml
Normal file
@ -0,0 +1,47 @@
|
||||
-
|
||||
id: 1
|
||||
repo_id: 1
|
||||
name: 'foo'
|
||||
commit_id: '65f1bf27bc3bf70f64657658635e66094edbcb4d'
|
||||
commit_message: 'first commit'
|
||||
commit_time: 978307100
|
||||
pusher_id: 1
|
||||
is_deleted: true
|
||||
deleted_by_id: 1
|
||||
deleted_unix: 978307200
|
||||
|
||||
-
|
||||
id: 2
|
||||
repo_id: 1
|
||||
name: 'bar'
|
||||
commit_id: '62fb502a7172d4453f0322a2cc85bddffa57f07a'
|
||||
commit_message: 'second commit'
|
||||
commit_time: 978307100
|
||||
pusher_id: 1
|
||||
is_deleted: true
|
||||
deleted_by_id: 99
|
||||
deleted_unix: 978307200
|
||||
|
||||
-
|
||||
id: 3
|
||||
repo_id: 1
|
||||
name: 'branch2'
|
||||
commit_id: '985f0301dba5e7b34be866819cd15ad3d8f508ee'
|
||||
commit_message: 'make pull5 outdated'
|
||||
commit_time: 1579166279
|
||||
pusher_id: 1
|
||||
is_deleted: false
|
||||
deleted_by_id: 0
|
||||
deleted_unix: 0
|
||||
|
||||
-
|
||||
id: 4
|
||||
repo_id: 1
|
||||
name: 'master'
|
||||
commit_id: '65f1bf27bc3bf70f64657658635e66094edbcb4d'
|
||||
commit_message: 'Initial commit'
|
||||
commit_time: 1489927679
|
||||
pusher_id: 1
|
||||
is_deleted: false
|
||||
deleted_by_id: 0
|
||||
deleted_unix: 0
|
||||
@ -1,15 +0,0 @@
|
||||
-
|
||||
id: 1
|
||||
repo_id: 1
|
||||
name: foo
|
||||
commit: 1213212312313213213132131
|
||||
deleted_by_id: 1
|
||||
deleted_unix: 978307200
|
||||
|
||||
-
|
||||
id: 2
|
||||
repo_id: 1
|
||||
name: bar
|
||||
commit: 5655464564554545466464655
|
||||
deleted_by_id: 99
|
||||
deleted_unix: 978307200
|
||||
379
models/git/branch.go
Normal file
379
models/git/branch.go
Normal file
@ -0,0 +1,379 @@
|
||||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// ErrBranchNotExist represents an error that branch with such name does not exist.
|
||||
type ErrBranchNotExist struct {
|
||||
RepoID int64
|
||||
BranchName string
|
||||
}
|
||||
|
||||
// IsErrBranchNotExist checks if an error is an ErrBranchDoesNotExist.
|
||||
func IsErrBranchNotExist(err error) bool {
|
||||
_, ok := err.(ErrBranchNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrBranchNotExist) Error() string {
|
||||
return fmt.Sprintf("branch does not exist [repo_id: %d name: %s]", err.RepoID, err.BranchName)
|
||||
}
|
||||
|
||||
func (err ErrBranchNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrBranchAlreadyExists represents an error that branch with such name already exists.
|
||||
type ErrBranchAlreadyExists struct {
|
||||
BranchName string
|
||||
}
|
||||
|
||||
// IsErrBranchAlreadyExists checks if an error is an ErrBranchAlreadyExists.
|
||||
func IsErrBranchAlreadyExists(err error) bool {
|
||||
_, ok := err.(ErrBranchAlreadyExists)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrBranchAlreadyExists) Error() string {
|
||||
return fmt.Sprintf("branch already exists [name: %s]", err.BranchName)
|
||||
}
|
||||
|
||||
func (err ErrBranchAlreadyExists) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrBranchNameConflict represents an error that branch name conflicts with other branch.
|
||||
type ErrBranchNameConflict struct {
|
||||
BranchName string
|
||||
}
|
||||
|
||||
// IsErrBranchNameConflict checks if an error is an ErrBranchNameConflict.
|
||||
func IsErrBranchNameConflict(err error) bool {
|
||||
_, ok := err.(ErrBranchNameConflict)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrBranchNameConflict) Error() string {
|
||||
return fmt.Sprintf("branch conflicts with existing branch [name: %s]", err.BranchName)
|
||||
}
|
||||
|
||||
func (err ErrBranchNameConflict) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrBranchesEqual represents an error that base branch is equal to the head branch.
|
||||
type ErrBranchesEqual struct {
|
||||
BaseBranchName string
|
||||
HeadBranchName string
|
||||
}
|
||||
|
||||
// IsErrBranchesEqual checks if an error is an ErrBranchesEqual.
|
||||
func IsErrBranchesEqual(err error) bool {
|
||||
_, ok := err.(ErrBranchesEqual)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrBranchesEqual) Error() string {
|
||||
return fmt.Sprintf("branches are equal [head: %sm base: %s]", err.HeadBranchName, err.BaseBranchName)
|
||||
}
|
||||
|
||||
func (err ErrBranchesEqual) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// Branch represents a branch of a repository
|
||||
// For those repository who have many branches, stored into database is a good choice
|
||||
// for pagination, keyword search and filtering
|
||||
type Branch struct {
|
||||
ID int64
|
||||
RepoID int64 `xorm:"UNIQUE(s)"`
|
||||
Name string `xorm:"UNIQUE(s) NOT NULL"`
|
||||
CommitID string
|
||||
CommitMessage string `xorm:"TEXT"`
|
||||
PusherID int64
|
||||
Pusher *user_model.User `xorm:"-"`
|
||||
IsDeleted bool `xorm:"index"`
|
||||
DeletedByID int64
|
||||
DeletedBy *user_model.User `xorm:"-"`
|
||||
DeletedUnix timeutil.TimeStamp `xorm:"index"`
|
||||
CommitTime timeutil.TimeStamp // The commit
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
|
||||
}
|
||||
|
||||
func (b *Branch) LoadDeletedBy(ctx context.Context) (err error) {
|
||||
if b.DeletedBy == nil {
|
||||
b.DeletedBy, err = user_model.GetUserByID(ctx, b.DeletedByID)
|
||||
if user_model.IsErrUserNotExist(err) {
|
||||
b.DeletedBy = user_model.NewGhostUser()
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *Branch) LoadPusher(ctx context.Context) (err error) {
|
||||
if b.Pusher == nil && b.PusherID > 0 {
|
||||
b.Pusher, err = user_model.GetUserByID(ctx, b.PusherID)
|
||||
if user_model.IsErrUserNotExist(err) {
|
||||
b.Pusher = user_model.NewGhostUser()
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func init() {
|
||||
db.RegisterModel(new(Branch))
|
||||
db.RegisterModel(new(RenamedBranch))
|
||||
}
|
||||
|
||||
func GetBranch(ctx context.Context, repoID int64, branchName string) (*Branch, error) {
|
||||
var branch Branch
|
||||
has, err := db.GetEngine(ctx).Where("repo_id=?", repoID).And("name=?", branchName).Get(&branch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrBranchNotExist{
|
||||
RepoID: repoID,
|
||||
BranchName: branchName,
|
||||
}
|
||||
}
|
||||
return &branch, nil
|
||||
}
|
||||
|
||||
func AddBranches(ctx context.Context, branches []*Branch) error {
|
||||
for _, branch := range branches {
|
||||
if _, err := db.GetEngine(ctx).Insert(branch); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetDeletedBranchByID(ctx context.Context, repoID, branchID int64) (*Branch, error) {
|
||||
var branch Branch
|
||||
has, err := db.GetEngine(ctx).ID(branchID).Get(&branch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrBranchNotExist{
|
||||
RepoID: repoID,
|
||||
}
|
||||
}
|
||||
if branch.RepoID != repoID {
|
||||
return nil, ErrBranchNotExist{
|
||||
RepoID: repoID,
|
||||
}
|
||||
}
|
||||
if !branch.IsDeleted {
|
||||
return nil, ErrBranchNotExist{
|
||||
RepoID: repoID,
|
||||
}
|
||||
}
|
||||
return &branch, nil
|
||||
}
|
||||
|
||||
func DeleteBranches(ctx context.Context, repoID, doerID int64, branchIDs []int64) error {
|
||||
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||
branches := make([]*Branch, 0, len(branchIDs))
|
||||
if err := db.GetEngine(ctx).In("id", branchIDs).Find(&branches); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, branch := range branches {
|
||||
if err := AddDeletedBranch(ctx, repoID, branch.Name, doerID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateBranch updates the branch information in the database. If the branch exist, it will update latest commit of this branch information
|
||||
// If it doest not exist, insert a new record into database
|
||||
func UpdateBranch(ctx context.Context, repoID int64, branchName, commitID, commitMessage string, pusherID int64, commitTime time.Time) error {
|
||||
cnt, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repoID, branchName).
|
||||
Cols("commit_id, commit_message, pusher_id, commit_time, is_deleted, updated_unix").
|
||||
Update(&Branch{
|
||||
CommitID: commitID,
|
||||
CommitMessage: commitMessage,
|
||||
PusherID: pusherID,
|
||||
CommitTime: timeutil.TimeStamp(commitTime.Unix()),
|
||||
IsDeleted: false,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cnt > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return db.Insert(ctx, &Branch{
|
||||
RepoID: repoID,
|
||||
Name: branchName,
|
||||
CommitID: commitID,
|
||||
CommitMessage: commitMessage,
|
||||
PusherID: pusherID,
|
||||
CommitTime: timeutil.TimeStamp(commitTime.Unix()),
|
||||
})
|
||||
}
|
||||
|
||||
// AddDeletedBranch adds a deleted branch to the database
|
||||
func AddDeletedBranch(ctx context.Context, repoID int64, branchName string, deletedByID int64) error {
|
||||
branch, err := GetBranch(ctx, repoID, branchName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if branch.IsDeleted {
|
||||
return nil
|
||||
}
|
||||
|
||||
cnt, err := db.GetEngine(ctx).Where("repo_id=? AND name=? AND is_deleted=?", repoID, branchName, false).
|
||||
Cols("is_deleted, deleted_by_id, deleted_unix").
|
||||
Update(&Branch{
|
||||
IsDeleted: true,
|
||||
DeletedByID: deletedByID,
|
||||
DeletedUnix: timeutil.TimeStampNow(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cnt == 0 {
|
||||
return fmt.Errorf("branch %s not found or has been deleted", branchName)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func RemoveDeletedBranchByID(ctx context.Context, repoID, branchID int64) error {
|
||||
_, err := db.GetEngine(ctx).Where("repo_id=? AND id=? AND is_deleted = ?", repoID, branchID, true).Delete(new(Branch))
|
||||
return err
|
||||
}
|
||||
|
||||
// RemoveOldDeletedBranches removes old deleted branches
|
||||
func RemoveOldDeletedBranches(ctx context.Context, olderThan time.Duration) {
|
||||
// Nothing to do for shutdown or terminate
|
||||
log.Trace("Doing: DeletedBranchesCleanup")
|
||||
|
||||
deleteBefore := time.Now().Add(-olderThan)
|
||||
_, err := db.GetEngine(ctx).Where("is_deleted=? AND deleted_unix < ?", true, deleteBefore.Unix()).Delete(new(Branch))
|
||||
if err != nil {
|
||||
log.Error("DeletedBranchesCleanup: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// RenamedBranch provide renamed branch log
|
||||
// will check it when a branch can't be found
|
||||
type RenamedBranch struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"INDEX NOT NULL"`
|
||||
From string
|
||||
To string
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||
}
|
||||
|
||||
// FindRenamedBranch check if a branch was renamed
|
||||
func FindRenamedBranch(ctx context.Context, repoID int64, from string) (branch *RenamedBranch, exist bool, err error) {
|
||||
branch = &RenamedBranch{
|
||||
RepoID: repoID,
|
||||
From: from,
|
||||
}
|
||||
exist, err = db.GetEngine(ctx).Get(branch)
|
||||
|
||||
return branch, exist, err
|
||||
}
|
||||
|
||||
// RenameBranch rename a branch
|
||||
func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to string, gitAction func(isDefault bool) error) (err error) {
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer committer.Close()
|
||||
|
||||
sess := db.GetEngine(ctx)
|
||||
|
||||
// 1. update branch in database
|
||||
if n, err := sess.Where("repo_id=? AND name=?", repo.ID, from).Update(&Branch{
|
||||
Name: to,
|
||||
}); err != nil {
|
||||
return err
|
||||
} else if n <= 0 {
|
||||
return ErrBranchNotExist{
|
||||
RepoID: repo.ID,
|
||||
BranchName: from,
|
||||
}
|
||||
}
|
||||
|
||||
// 2. update default branch if needed
|
||||
isDefault := repo.DefaultBranch == from
|
||||
if isDefault {
|
||||
repo.DefaultBranch = to
|
||||
_, err = sess.ID(repo.ID).Cols("default_branch").Update(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Update protected branch if needed
|
||||
protectedBranch, err := GetProtectedBranchRuleByName(ctx, repo.ID, from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if protectedBranch != nil {
|
||||
// there is a protect rule for this branch
|
||||
protectedBranch.RuleName = to
|
||||
_, err = sess.ID(protectedBranch.ID).Cols("branch_name").Update(protectedBranch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// some glob protect rules may match this branch
|
||||
protected, err := IsBranchProtected(ctx, repo.ID, from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if protected {
|
||||
return ErrBranchIsProtected
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Update all not merged pull request base branch name
|
||||
_, err = sess.Table("pull_request").Where("base_repo_id=? AND base_branch=? AND has_merged=?",
|
||||
repo.ID, from, false).
|
||||
Update(map[string]interface{}{"base_branch": to})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 5. do git action
|
||||
if err = gitAction(isDefault); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 6. insert renamed branch record
|
||||
renamedBranch := &RenamedBranch{
|
||||
RepoID: repo.ID,
|
||||
From: from,
|
||||
To: to,
|
||||
}
|
||||
err = db.Insert(ctx, renamedBranch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return committer.Commit()
|
||||
}
|
||||
132
models/git/branch_list.go
Normal file
132
models/git/branch_list.go
Normal file
@ -0,0 +1,132 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
type BranchList []*Branch
|
||||
|
||||
func (branches BranchList) LoadDeletedBy(ctx context.Context) error {
|
||||
ids := container.Set[int64]{}
|
||||
for _, branch := range branches {
|
||||
if !branch.IsDeleted {
|
||||
continue
|
||||
}
|
||||
ids.Add(branch.DeletedByID)
|
||||
}
|
||||
usersMap := make(map[int64]*user_model.User, len(ids))
|
||||
if err := db.GetEngine(ctx).In("id", ids.Values()).Find(&usersMap); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, branch := range branches {
|
||||
if !branch.IsDeleted {
|
||||
continue
|
||||
}
|
||||
branch.DeletedBy = usersMap[branch.DeletedByID]
|
||||
if branch.DeletedBy == nil {
|
||||
branch.DeletedBy = user_model.NewGhostUser()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (branches BranchList) LoadPusher(ctx context.Context) error {
|
||||
ids := container.Set[int64]{}
|
||||
for _, branch := range branches {
|
||||
if branch.PusherID > 0 { // pusher_id maybe zero because some branches are sync by backend with no pusher
|
||||
ids.Add(branch.PusherID)
|
||||
}
|
||||
}
|
||||
usersMap := make(map[int64]*user_model.User, len(ids))
|
||||
if err := db.GetEngine(ctx).In("id", ids.Values()).Find(&usersMap); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, branch := range branches {
|
||||
if branch.PusherID <= 0 {
|
||||
continue
|
||||
}
|
||||
branch.Pusher = usersMap[branch.PusherID]
|
||||
if branch.Pusher == nil {
|
||||
branch.Pusher = user_model.NewGhostUser()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
BranchOrderByNameAsc = "name ASC"
|
||||
BranchOrderByCommitTimeDesc = "commit_time DESC"
|
||||
)
|
||||
|
||||
type FindBranchOptions struct {
|
||||
db.ListOptions
|
||||
RepoID int64
|
||||
ExcludeBranchNames []string
|
||||
IsDeletedBranch util.OptionalBool
|
||||
OrderBy string
|
||||
}
|
||||
|
||||
func (opts *FindBranchOptions) Cond() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
if opts.RepoID > 0 {
|
||||
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
|
||||
}
|
||||
|
||||
if len(opts.ExcludeBranchNames) > 0 {
|
||||
cond = cond.And(builder.NotIn("name", opts.ExcludeBranchNames))
|
||||
}
|
||||
if !opts.IsDeletedBranch.IsNone() {
|
||||
cond = cond.And(builder.Eq{"is_deleted": opts.IsDeletedBranch.IsTrue()})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
func CountBranches(ctx context.Context, opts FindBranchOptions) (int64, error) {
|
||||
return db.GetEngine(ctx).Where(opts.Cond()).Count(&Branch{})
|
||||
}
|
||||
|
||||
func orderByBranches(sess *xorm.Session, opts FindBranchOptions) *xorm.Session {
|
||||
if !opts.IsDeletedBranch.IsFalse() { // if deleted branch included, put them at the end
|
||||
sess = sess.OrderBy("is_deleted ASC")
|
||||
}
|
||||
|
||||
if opts.OrderBy == "" {
|
||||
opts.OrderBy = BranchOrderByCommitTimeDesc
|
||||
}
|
||||
return sess.OrderBy(opts.OrderBy)
|
||||
}
|
||||
|
||||
func FindBranches(ctx context.Context, opts FindBranchOptions) (BranchList, error) {
|
||||
sess := db.GetEngine(ctx).Where(opts.Cond())
|
||||
if opts.PageSize > 0 && !opts.IsListAll() {
|
||||
sess = db.SetSessionPagination(sess, &opts.ListOptions)
|
||||
}
|
||||
sess = orderByBranches(sess, opts)
|
||||
|
||||
var branches []*Branch
|
||||
return branches, sess.Find(&branches)
|
||||
}
|
||||
|
||||
func FindBranchNames(ctx context.Context, opts FindBranchOptions) ([]string, error) {
|
||||
sess := db.GetEngine(ctx).Select("name").Where(opts.Cond())
|
||||
if opts.PageSize > 0 && !opts.IsListAll() {
|
||||
sess = db.SetSessionPagination(sess, &opts.ListOptions)
|
||||
}
|
||||
sess = orderByBranches(sess, opts)
|
||||
var branches []string
|
||||
if err := sess.Table("branch").Find(&branches); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return branches, nil
|
||||
}
|
||||
@ -11,6 +11,7 @@ import (
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@ -18,24 +19,37 @@ import (
|
||||
func TestAddDeletedBranch(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1})
|
||||
firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 1})
|
||||
|
||||
assert.Error(t, git_model.AddDeletedBranch(db.DefaultContext, repo.ID, firstBranch.Name, firstBranch.Commit, firstBranch.DeletedByID))
|
||||
assert.NoError(t, git_model.AddDeletedBranch(db.DefaultContext, repo.ID, "test", "5655464564554545466464656", int64(1)))
|
||||
assert.True(t, firstBranch.IsDeleted)
|
||||
assert.NoError(t, git_model.AddDeletedBranch(db.DefaultContext, repo.ID, firstBranch.Name, firstBranch.DeletedByID))
|
||||
assert.NoError(t, git_model.AddDeletedBranch(db.DefaultContext, repo.ID, "branch2", int64(1)))
|
||||
|
||||
secondBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo.ID, Name: "branch2"})
|
||||
assert.True(t, secondBranch.IsDeleted)
|
||||
|
||||
err := git_model.UpdateBranch(db.DefaultContext, repo.ID, secondBranch.Name, secondBranch.CommitID, secondBranch.CommitMessage, secondBranch.PusherID, secondBranch.CommitTime.AsLocalTime())
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGetDeletedBranches(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
|
||||
branches, err := git_model.GetDeletedBranches(db.DefaultContext, repo.ID)
|
||||
branches, err := git_model.FindBranches(db.DefaultContext, git_model.FindBranchOptions{
|
||||
ListOptions: db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
RepoID: repo.ID,
|
||||
IsDeletedBranch: util.OptionalBoolTrue,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, branches, 2)
|
||||
}
|
||||
|
||||
func TestGetDeletedBranch(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1})
|
||||
firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 1})
|
||||
|
||||
assert.NotNil(t, getDeletedBranch(t, firstBranch))
|
||||
}
|
||||
@ -43,18 +57,18 @@ func TestGetDeletedBranch(t *testing.T) {
|
||||
func TestDeletedBranchLoadUser(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1})
|
||||
secondBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 2})
|
||||
firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 1})
|
||||
secondBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 2})
|
||||
|
||||
branch := getDeletedBranch(t, firstBranch)
|
||||
assert.Nil(t, branch.DeletedBy)
|
||||
branch.LoadUser(db.DefaultContext)
|
||||
branch.LoadDeletedBy(db.DefaultContext)
|
||||
assert.NotNil(t, branch.DeletedBy)
|
||||
assert.Equal(t, "user1", branch.DeletedBy.Name)
|
||||
|
||||
branch = getDeletedBranch(t, secondBranch)
|
||||
assert.Nil(t, branch.DeletedBy)
|
||||
branch.LoadUser(db.DefaultContext)
|
||||
branch.LoadDeletedBy(db.DefaultContext)
|
||||
assert.NotNil(t, branch.DeletedBy)
|
||||
assert.Equal(t, "Ghost", branch.DeletedBy.Name)
|
||||
}
|
||||
@ -63,22 +77,22 @@ func TestRemoveDeletedBranch(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
|
||||
firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1})
|
||||
firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 1})
|
||||
|
||||
err := git_model.RemoveDeletedBranchByID(db.DefaultContext, repo.ID, 1)
|
||||
assert.NoError(t, err)
|
||||
unittest.AssertNotExistsBean(t, firstBranch)
|
||||
unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 2})
|
||||
unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 2})
|
||||
}
|
||||
|
||||
func getDeletedBranch(t *testing.T, branch *git_model.DeletedBranch) *git_model.DeletedBranch {
|
||||
func getDeletedBranch(t *testing.T, branch *git_model.Branch) *git_model.Branch {
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
|
||||
deletedBranch, err := git_model.GetDeletedBranchByID(db.DefaultContext, repo.ID, branch.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, branch.ID, deletedBranch.ID)
|
||||
assert.Equal(t, branch.Name, deletedBranch.Name)
|
||||
assert.Equal(t, branch.Commit, deletedBranch.Commit)
|
||||
assert.Equal(t, branch.CommitID, deletedBranch.CommitID)
|
||||
assert.Equal(t, branch.DeletedByID, deletedBranch.DeletedByID)
|
||||
|
||||
return deletedBranch
|
||||
@ -146,8 +160,8 @@ func TestOnlyGetDeletedBranchOnCorrectRepo(t *testing.T) {
|
||||
|
||||
deletedBranch, err := git_model.GetDeletedBranchByID(db.DefaultContext, repo2.ID, 1)
|
||||
|
||||
// Expect no error, and the returned branch is nil.
|
||||
assert.NoError(t, err)
|
||||
// Expect error, and the returned branch is nil.
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, deletedBranch)
|
||||
|
||||
// Now get the deletedBranch with ID of 1 on repo with ID 1.
|
||||
@ -1,197 +0,0 @@
|
||||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
)
|
||||
|
||||
// DeletedBranch struct
|
||||
type DeletedBranch struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||
Name string `xorm:"UNIQUE(s) NOT NULL"`
|
||||
Commit string `xorm:"UNIQUE(s) NOT NULL"`
|
||||
DeletedByID int64 `xorm:"INDEX"`
|
||||
DeletedBy *user_model.User `xorm:"-"`
|
||||
DeletedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
db.RegisterModel(new(DeletedBranch))
|
||||
db.RegisterModel(new(RenamedBranch))
|
||||
}
|
||||
|
||||
// AddDeletedBranch adds a deleted branch to the database
|
||||
func AddDeletedBranch(ctx context.Context, repoID int64, branchName, commit string, deletedByID int64) error {
|
||||
deletedBranch := &DeletedBranch{
|
||||
RepoID: repoID,
|
||||
Name: branchName,
|
||||
Commit: commit,
|
||||
DeletedByID: deletedByID,
|
||||
}
|
||||
|
||||
_, err := db.GetEngine(ctx).Insert(deletedBranch)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetDeletedBranches returns all the deleted branches
|
||||
func GetDeletedBranches(ctx context.Context, repoID int64) ([]*DeletedBranch, error) {
|
||||
deletedBranches := make([]*DeletedBranch, 0)
|
||||
return deletedBranches, db.GetEngine(ctx).Where("repo_id = ?", repoID).Desc("deleted_unix").Find(&deletedBranches)
|
||||
}
|
||||
|
||||
// GetDeletedBranchByID get a deleted branch by its ID
|
||||
func GetDeletedBranchByID(ctx context.Context, repoID, id int64) (*DeletedBranch, error) {
|
||||
deletedBranch := &DeletedBranch{}
|
||||
has, err := db.GetEngine(ctx).Where("repo_id = ?", repoID).And("id = ?", id).Get(deletedBranch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !has {
|
||||
return nil, nil
|
||||
}
|
||||
return deletedBranch, nil
|
||||
}
|
||||
|
||||
// RemoveDeletedBranchByID removes a deleted branch from the database
|
||||
func RemoveDeletedBranchByID(ctx context.Context, repoID, id int64) (err error) {
|
||||
deletedBranch := &DeletedBranch{
|
||||
RepoID: repoID,
|
||||
ID: id,
|
||||
}
|
||||
|
||||
if affected, err := db.GetEngine(ctx).Delete(deletedBranch); err != nil {
|
||||
return err
|
||||
} else if affected != 1 {
|
||||
return fmt.Errorf("remove deleted branch ID(%v) failed", id)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadUser loads the user that deleted the branch
|
||||
// When there's no user found it returns a user_model.NewGhostUser
|
||||
func (deletedBranch *DeletedBranch) LoadUser(ctx context.Context) {
|
||||
user, err := user_model.GetUserByID(ctx, deletedBranch.DeletedByID)
|
||||
if err != nil {
|
||||
user = user_model.NewGhostUser()
|
||||
}
|
||||
deletedBranch.DeletedBy = user
|
||||
}
|
||||
|
||||
// RemoveDeletedBranchByName removes all deleted branches
|
||||
func RemoveDeletedBranchByName(ctx context.Context, repoID int64, branch string) error {
|
||||
_, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repoID, branch).Delete(new(DeletedBranch))
|
||||
return err
|
||||
}
|
||||
|
||||
// RemoveOldDeletedBranches removes old deleted branches
|
||||
func RemoveOldDeletedBranches(ctx context.Context, olderThan time.Duration) {
|
||||
// Nothing to do for shutdown or terminate
|
||||
log.Trace("Doing: DeletedBranchesCleanup")
|
||||
|
||||
deleteBefore := time.Now().Add(-olderThan)
|
||||
_, err := db.GetEngine(ctx).Where("deleted_unix < ?", deleteBefore.Unix()).Delete(new(DeletedBranch))
|
||||
if err != nil {
|
||||
log.Error("DeletedBranchesCleanup: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// RenamedBranch provide renamed branch log
|
||||
// will check it when a branch can't be found
|
||||
type RenamedBranch struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"INDEX NOT NULL"`
|
||||
From string
|
||||
To string
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||
}
|
||||
|
||||
// FindRenamedBranch check if a branch was renamed
|
||||
func FindRenamedBranch(ctx context.Context, repoID int64, from string) (branch *RenamedBranch, exist bool, err error) {
|
||||
branch = &RenamedBranch{
|
||||
RepoID: repoID,
|
||||
From: from,
|
||||
}
|
||||
exist, err = db.GetEngine(ctx).Get(branch)
|
||||
|
||||
return branch, exist, err
|
||||
}
|
||||
|
||||
// RenameBranch rename a branch
|
||||
func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to string, gitAction func(isDefault bool) error) (err error) {
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer committer.Close()
|
||||
|
||||
sess := db.GetEngine(ctx)
|
||||
// 1. update default branch if needed
|
||||
isDefault := repo.DefaultBranch == from
|
||||
if isDefault {
|
||||
repo.DefaultBranch = to
|
||||
_, err = sess.ID(repo.ID).Cols("default_branch").Update(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Update protected branch if needed
|
||||
protectedBranch, err := GetProtectedBranchRuleByName(ctx, repo.ID, from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if protectedBranch != nil {
|
||||
protectedBranch.RuleName = to
|
||||
_, err = sess.ID(protectedBranch.ID).Cols("branch_name").Update(protectedBranch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
protected, err := IsBranchProtected(ctx, repo.ID, from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if protected {
|
||||
return ErrBranchIsProtected
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Update all not merged pull request base branch name
|
||||
_, err = sess.Table("pull_request").Where("base_repo_id=? AND base_branch=? AND has_merged=?",
|
||||
repo.ID, from, false).
|
||||
Update(map[string]interface{}{"base_branch": to})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 4. do git action
|
||||
if err = gitAction(isDefault); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 5. insert renamed branch record
|
||||
renamedBranch := &RenamedBranch{
|
||||
RepoID: repo.ID,
|
||||
From: from,
|
||||
To: to,
|
||||
}
|
||||
err = db.Insert(ctx, renamedBranch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return committer.Commit()
|
||||
}
|
||||
@ -8,7 +8,7 @@ import (
|
||||
"sort"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/gobwas/glob"
|
||||
)
|
||||
@ -47,19 +47,32 @@ func FindRepoProtectedBranchRules(ctx context.Context, repoID int64) (ProtectedB
|
||||
}
|
||||
|
||||
// FindAllMatchedBranches find all matched branches
|
||||
func FindAllMatchedBranches(ctx context.Context, gitRepo *git.Repository, ruleName string) ([]string, error) {
|
||||
// FIXME: how many should we get?
|
||||
branches, _, err := gitRepo.GetBranchNames(0, 9999999)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rule := glob.MustCompile(ruleName)
|
||||
results := make([]string, 0, len(branches))
|
||||
for _, branch := range branches {
|
||||
if rule.Match(branch) {
|
||||
results = append(results, branch)
|
||||
func FindAllMatchedBranches(ctx context.Context, repoID int64, ruleName string) ([]string, error) {
|
||||
results := make([]string, 0, 10)
|
||||
for page := 1; ; page++ {
|
||||
brancheNames, err := FindBranchNames(ctx, FindBranchOptions{
|
||||
ListOptions: db.ListOptions{
|
||||
PageSize: 100,
|
||||
Page: page,
|
||||
},
|
||||
RepoID: repoID,
|
||||
IsDeletedBranch: util.OptionalBoolFalse,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rule := glob.MustCompile(ruleName)
|
||||
|
||||
for _, branch := range brancheNames {
|
||||
if rule.Match(branch) {
|
||||
results = append(results, branch)
|
||||
}
|
||||
}
|
||||
if len(brancheNames) < 100 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
|
||||
@ -507,6 +507,10 @@ var migrations = []Migration{
|
||||
NewMigration("Add variable table", v1_21.CreateVariableTable),
|
||||
// v262 -> v263
|
||||
NewMigration("Add TriggerEvent to action_run table", v1_21.AddTriggerEventToActionRun),
|
||||
// v263 -> v264
|
||||
NewMigration("Add git_size and lfs_size columns to repository table", v1_21.AddGitSizeAndLFSSizeToRepositoryTable),
|
||||
// v264 -> v265
|
||||
NewMigration("Add branch table", v1_21.AddBranchTable),
|
||||
}
|
||||
|
||||
// GetCurrentDBVersion returns the current db version
|
||||
|
||||
41
models/migrations/v1_21/v263.go
Normal file
41
models/migrations/v1_21/v263.go
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_21 //nolint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// AddGitSizeAndLFSSizeToRepositoryTable: add GitSize and LFSSize columns to Repository
|
||||
func AddGitSizeAndLFSSizeToRepositoryTable(x *xorm.Engine) error {
|
||||
type Repository struct {
|
||||
GitSize int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
LFSSize int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sess.Sync2(new(Repository)); err != nil {
|
||||
return fmt.Errorf("Sync2: %w", err)
|
||||
}
|
||||
|
||||
_, err := sess.Exec(`UPDATE repository SET lfs_size=(SELECT SUM(size) FROM lfs_meta_object WHERE lfs_meta_object.repository_id=repository.ID) WHERE EXISTS (SELECT 1 FROM lfs_meta_object WHERE lfs_meta_object.repository_id=repository.ID)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = sess.Exec(`UPDATE repository SET git_size = size - lfs_size`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
93
models/migrations/v1_21/v264.go
Normal file
93
models/migrations/v1_21/v264.go
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_21 //nolint
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddBranchTable(x *xorm.Engine) error {
|
||||
type Branch struct {
|
||||
ID int64
|
||||
RepoID int64 `xorm:"UNIQUE(s)"`
|
||||
Name string `xorm:"UNIQUE(s) NOT NULL"`
|
||||
CommitID string
|
||||
CommitMessage string `xorm:"TEXT"`
|
||||
PusherID int64
|
||||
IsDeleted bool `xorm:"index"`
|
||||
DeletedByID int64
|
||||
DeletedUnix timeutil.TimeStamp `xorm:"index"`
|
||||
CommitTime timeutil.TimeStamp // The commit
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
|
||||
}
|
||||
|
||||
if err := x.Sync(new(Branch)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if exist, err := x.IsTableExist("deleted_branches"); err != nil {
|
||||
return err
|
||||
} else if !exist {
|
||||
return nil
|
||||
}
|
||||
|
||||
type DeletedBranch struct {
|
||||
ID int64
|
||||
RepoID int64 `xorm:"index UNIQUE(s)"`
|
||||
Name string `xorm:"UNIQUE(s) NOT NULL"`
|
||||
Commit string
|
||||
DeletedByID int64
|
||||
DeletedUnix timeutil.TimeStamp
|
||||
}
|
||||
|
||||
var adminUserID int64
|
||||
has, err := x.Table("user").
|
||||
Select("id").
|
||||
Where("is_admin=?", true).
|
||||
Asc("id"). // Reliably get the admin with the lowest ID.
|
||||
Get(&adminUserID)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return fmt.Errorf("no admin user found")
|
||||
}
|
||||
|
||||
branches := make([]Branch, 0, 100)
|
||||
if err := db.Iterate(context.Background(), nil, func(ctx context.Context, deletedBranch *DeletedBranch) error {
|
||||
branches = append(branches, Branch{
|
||||
RepoID: deletedBranch.RepoID,
|
||||
Name: deletedBranch.Name,
|
||||
CommitID: deletedBranch.Commit,
|
||||
PusherID: adminUserID,
|
||||
IsDeleted: true,
|
||||
DeletedByID: deletedBranch.DeletedByID,
|
||||
DeletedUnix: deletedBranch.DeletedUnix,
|
||||
})
|
||||
if len(branches) >= 100 {
|
||||
_, err := x.Insert(&branches)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
branches = branches[:0]
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(branches) > 0 {
|
||||
if _, err := x.Insert(&branches); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return x.DropTables("deleted_branches")
|
||||
}
|
||||
@ -147,7 +147,7 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
|
||||
&repo_model.Collaboration{RepoID: repoID},
|
||||
&issues_model.Comment{RefRepoID: repoID},
|
||||
&git_model.CommitStatus{RepoID: repoID},
|
||||
&git_model.DeletedBranch{RepoID: repoID},
|
||||
&git_model.Branch{RepoID: repoID},
|
||||
&git_model.LFSLock{RepoID: repoID},
|
||||
&repo_model.LanguageStat{RepoID: repoID},
|
||||
&issues_model.Milestone{RepoID: repoID},
|
||||
|
||||
@ -16,6 +16,7 @@ import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@ -163,6 +164,8 @@ type Repository struct {
|
||||
IsTemplate bool `xorm:"INDEX NOT NULL DEFAULT false"`
|
||||
TemplateID int64 `xorm:"INDEX"`
|
||||
Size int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
GitSize int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
LFSSize int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
CodeIndexerStatus *RepoIndexerStatus `xorm:"-"`
|
||||
StatsIndexerStatus *RepoIndexerStatus `xorm:"-"`
|
||||
IsFsckEnabled bool `xorm:"NOT NULL DEFAULT true"`
|
||||
@ -196,6 +199,42 @@ func (repo *Repository) SanitizedOriginalURL() string {
|
||||
return u.String()
|
||||
}
|
||||
|
||||
// text representations to be returned in SizeDetail.Name
|
||||
const (
|
||||
SizeDetailNameGit = "git"
|
||||
SizeDetailNameLFS = "lfs"
|
||||
)
|
||||
|
||||
type SizeDetail struct {
|
||||
Name string
|
||||
Size int64
|
||||
}
|
||||
|
||||
// SizeDetails forms a struct with various size details about repository
|
||||
func (repo *Repository) SizeDetails() []SizeDetail {
|
||||
sizeDetails := []SizeDetail{
|
||||
{
|
||||
Name: SizeDetailNameGit,
|
||||
Size: repo.GitSize,
|
||||
},
|
||||
{
|
||||
Name: SizeDetailNameLFS,
|
||||
Size: repo.LFSSize,
|
||||
},
|
||||
}
|
||||
return sizeDetails
|
||||
}
|
||||
|
||||
// SizeDetailsString returns a concatenation of all repository size details as a string
|
||||
func (repo *Repository) SizeDetailsString() string {
|
||||
var str strings.Builder
|
||||
sizeDetails := repo.SizeDetails()
|
||||
for _, detail := range sizeDetails {
|
||||
str.WriteString(fmt.Sprintf("%s: %s, ", detail.Name, base.FileSize(detail.Size)))
|
||||
}
|
||||
return strings.TrimSuffix(str.String(), ", ")
|
||||
}
|
||||
|
||||
func (repo *Repository) LogString() string {
|
||||
if repo == nil {
|
||||
return "<Repository nil>"
|
||||
|
||||
@ -185,9 +185,11 @@ func ChangeRepositoryName(doer *user_model.User, repo *Repository, newRepoName s
|
||||
}
|
||||
|
||||
// UpdateRepoSize updates the repository size, calculating it using getDirectorySize
|
||||
func UpdateRepoSize(ctx context.Context, repoID, size int64) error {
|
||||
_, err := db.GetEngine(ctx).ID(repoID).Cols("size").NoAutoTime().Update(&Repository{
|
||||
Size: size,
|
||||
func UpdateRepoSize(ctx context.Context, repoID, gitSize, lfsSize int64) error {
|
||||
_, err := db.GetEngine(ctx).ID(repoID).Cols("size", "git_size", "lfs_size").NoAutoTime().Update(&Repository{
|
||||
Size: gitSize + lfsSize,
|
||||
GitSize: gitSize,
|
||||
LFSSize: lfsSize,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
@ -1171,9 +1171,9 @@ func GetUserByOpenID(uri string) (*User, error) {
|
||||
}
|
||||
|
||||
// GetAdminUser returns the first administrator
|
||||
func GetAdminUser() (*User, error) {
|
||||
func GetAdminUser(ctx context.Context) (*User, error) {
|
||||
var admin User
|
||||
has, err := db.GetEngine(db.DefaultContext).
|
||||
has, err := db.GetEngine(ctx).
|
||||
Where("is_admin=?", true).
|
||||
Asc("id"). // Reliably get the admin with the lowest ID.
|
||||
Get(&admin)
|
||||
|
||||
@ -29,12 +29,28 @@ const (
|
||||
)
|
||||
|
||||
func WriteLogs(ctx context.Context, filename string, offset int64, rows []*runnerv1.LogRow) ([]int, error) {
|
||||
flag := os.O_WRONLY
|
||||
if offset == 0 {
|
||||
// Create file only if offset is 0, or it could result in content holes if the file doesn't exist.
|
||||
flag |= os.O_CREATE
|
||||
}
|
||||
name := DBFSPrefix + filename
|
||||
f, err := dbfs.OpenFile(ctx, name, os.O_WRONLY|os.O_CREATE)
|
||||
f, err := dbfs.OpenFile(ctx, name, flag)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dbfs OpenFile %q: %w", name, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
stat, err := f.Stat()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dbfs Stat %q: %w", name, err)
|
||||
}
|
||||
if stat.Size() < offset {
|
||||
// If the size is less than offset, refuse to write, or it could result in content holes.
|
||||
// However, if the size is greater than offset, we can still write to overwrite the content.
|
||||
return nil, fmt.Errorf("size of %q is less than offset", name)
|
||||
}
|
||||
|
||||
if _, err := f.Seek(offset, io.SeekStart); err != nil {
|
||||
return nil, fmt.Errorf("dbfs Seek %q: %w", name, err)
|
||||
}
|
||||
@ -57,7 +73,7 @@ func WriteLogs(ctx context.Context, filename string, offset int64, rows []*runne
|
||||
}
|
||||
|
||||
func ReadLogs(ctx context.Context, inStorage bool, filename string, offset, limit int64) ([]*runnerv1.LogRow, error) {
|
||||
f, err := openLogs(ctx, inStorage, filename)
|
||||
f, err := OpenLogs(ctx, inStorage, filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -125,7 +141,7 @@ func RemoveLogs(ctx context.Context, inStorage bool, filename string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func openLogs(ctx context.Context, inStorage bool, filename string) (io.ReadSeekCloser, error) {
|
||||
func OpenLogs(ctx context.Context, inStorage bool, filename string) (io.ReadSeekCloser, error) {
|
||||
if !inStorage {
|
||||
name := DBFSPrefix + filename
|
||||
f, err := dbfs.Open(ctx, name)
|
||||
|
||||
@ -667,13 +667,38 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
|
||||
}
|
||||
ctx.Data["Tags"] = tags
|
||||
|
||||
brs, _, err := ctx.Repo.GitRepo.GetBranchNames(0, 0)
|
||||
branchOpts := git_model.FindBranchOptions{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
IsDeletedBranch: util.OptionalBoolFalse,
|
||||
ListOptions: db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
}
|
||||
branchesTotal, err := git_model.CountBranches(ctx, branchOpts)
|
||||
if err != nil {
|
||||
ctx.ServerError("CountBranches", err)
|
||||
return
|
||||
}
|
||||
|
||||
// non empty repo should have at least 1 branch, so this repository's branches haven't been synced yet
|
||||
if branchesTotal == 0 { // fallback to do a sync immediately
|
||||
branchesTotal, err = repo_module.SyncRepoBranches(ctx, ctx.Repo.Repository.ID, 0)
|
||||
if err != nil {
|
||||
ctx.ServerError("SyncRepoBranches", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: use paganation and async loading
|
||||
branchOpts.ExcludeBranchNames = []string{ctx.Repo.Repository.DefaultBranch}
|
||||
brs, err := git_model.FindBranchNames(ctx, branchOpts)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetBranches", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["Branches"] = brs
|
||||
ctx.Data["BranchesCount"] = len(brs)
|
||||
// always put default branch on the top
|
||||
ctx.Data["Branches"] = append(branchOpts.ExcludeBranchNames, brs...)
|
||||
ctx.Data["BranchesCount"] = branchesTotal
|
||||
|
||||
// If not branch selected, try default one.
|
||||
// If default branch doesn't exist, fall back to some other branch.
|
||||
@ -897,9 +922,9 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
|
||||
if len(ctx.Params("*")) == 0 {
|
||||
refName = ctx.Repo.Repository.DefaultBranch
|
||||
if !ctx.Repo.GitRepo.IsBranchExist(refName) {
|
||||
brs, _, err := ctx.Repo.GitRepo.GetBranchNames(0, 0)
|
||||
brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 1)
|
||||
if err == nil && len(brs) != 0 {
|
||||
refName = brs[0]
|
||||
refName = brs[0].Name
|
||||
} else if len(brs) == 0 {
|
||||
log.Error("No branches in non-empty repository %s", ctx.Repo.GitRepo.Path)
|
||||
ctx.Repo.Repository.MarkAsBrokenEmpty()
|
||||
|
||||
@ -230,6 +230,7 @@ var GemojiData = Gemoji{
|
||||
{"\U0001f382", "birthday cake", []string{"birthday"}, "6.0", false},
|
||||
{"\U0001f9ac", "bison", []string{"bison"}, "13.0", false},
|
||||
{"\U0001fae6", "biting lip", []string{"biting_lip"}, "14.0", false},
|
||||
{"\U0001f426\u200d\u2b1b", "black bird", []string{"black_bird"}, "15.0", false},
|
||||
{"\U0001f408\u200d\u2b1b", "black cat", []string{"black_cat"}, "13.0", false},
|
||||
{"\u26ab", "black circle", []string{"black_circle"}, "4.1", false},
|
||||
{"\U0001f3f4", "black flag", []string{"black_flag"}, "7.0", false},
|
||||
@ -748,6 +749,7 @@ var GemojiData = Gemoji{
|
||||
{"\U0001f42c", "dolphin", []string{"dolphin", "flipper"}, "6.0", false},
|
||||
{"\U0001f1e9\U0001f1f2", "flag: Dominica", []string{"dominica"}, "6.0", false},
|
||||
{"\U0001f1e9\U0001f1f4", "flag: Dominican Republic", []string{"dominican_republic"}, "6.0", false},
|
||||
{"\U0001facf", "donkey", []string{"donkey"}, "15.0", false},
|
||||
{"\U0001f6aa", "door", []string{"door"}, "6.0", false},
|
||||
{"\U0001fae5", "dotted line face", []string{"dotted_line_face"}, "14.0", false},
|
||||
{"\U0001f369", "doughnut", []string{"doughnut"}, "6.0", false},
|
||||
@ -982,11 +984,13 @@ var GemojiData = Gemoji{
|
||||
{"\U0001f4be", "floppy disk", []string{"floppy_disk"}, "6.0", false},
|
||||
{"\U0001f3b4", "flower playing cards", []string{"flower_playing_cards"}, "6.0", false},
|
||||
{"\U0001f633", "flushed face", []string{"flushed"}, "6.0", false},
|
||||
{"\U0001fa88", "flute", []string{"flute"}, "15.0", false},
|
||||
{"\U0001fab0", "fly", []string{"fly"}, "13.0", false},
|
||||
{"\U0001f94f", "flying disc", []string{"flying_disc"}, "11.0", false},
|
||||
{"\U0001f6f8", "flying saucer", []string{"flying_saucer"}, "11.0", false},
|
||||
{"\U0001f32b\ufe0f", "fog", []string{"fog"}, "7.0", false},
|
||||
{"\U0001f301", "foggy", []string{"foggy"}, "6.0", false},
|
||||
{"\U0001faad", "folding hand fan", []string{"folding_hand_fan"}, "15.0", false},
|
||||
{"\U0001fad5", "fondue", []string{"fondue"}, "13.0", false},
|
||||
{"\U0001f9b6", "foot", []string{"foot"}, "11.0", true},
|
||||
{"\U0001f9b6\U0001f3ff", "foot: Dark Skin Tone", []string{"foot_Dark_Skin_Tone"}, "12.0", false},
|
||||
@ -1054,6 +1058,7 @@ var GemojiData = Gemoji{
|
||||
{"\U0001f1ec\U0001f1ee", "flag: Gibraltar", []string{"gibraltar"}, "6.0", false},
|
||||
{"\U0001f381", "wrapped gift", []string{"gift"}, "6.0", false},
|
||||
{"\U0001f49d", "heart with ribbon", []string{"gift_heart"}, "6.0", false},
|
||||
{"\U0001fada", "ginger root", []string{"ginger_root"}, "15.0", false},
|
||||
{"\U0001f992", "giraffe", []string{"giraffe"}, "11.0", false},
|
||||
{"\U0001f467", "girl", []string{"girl"}, "6.0", true},
|
||||
{"\U0001f467\U0001f3ff", "girl: Dark Skin Tone", []string{"girl_Dark_Skin_Tone"}, "12.0", false},
|
||||
@ -1085,6 +1090,7 @@ var GemojiData = Gemoji{
|
||||
{"\U0001f3cc\U0001f3fe\ufe0f\u200d\u2640\ufe0f", "woman golfing: Medium-Dark Skin Tone", []string{"golfing_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001f3cc\U0001f3fc\ufe0f\u200d\u2640\ufe0f", "woman golfing: Medium-Light Skin Tone", []string{"golfing_woman_Medium-Light_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001f3cc\U0001f3fd\ufe0f\u200d\u2640\ufe0f", "woman golfing: Medium Skin Tone", []string{"golfing_woman_Medium_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001fabf", "goose", []string{"goose"}, "15.0", false},
|
||||
{"\U0001f98d", "gorilla", []string{"gorilla"}, "9.0", false},
|
||||
{"\U0001f347", "grapes", []string{"grapes"}, "6.0", false},
|
||||
{"\U0001f1ec\U0001f1f7", "flag: Greece", []string{"greece"}, "6.0", false},
|
||||
@ -1097,6 +1103,7 @@ var GemojiData = Gemoji{
|
||||
{"\U0001f1ec\U0001f1f1", "flag: Greenland", []string{"greenland"}, "6.0", false},
|
||||
{"\U0001f1ec\U0001f1e9", "flag: Grenada", []string{"grenada"}, "6.0", false},
|
||||
{"\u2755", "white exclamation mark", []string{"grey_exclamation"}, "6.0", false},
|
||||
{"\U0001fa76", "grey heart", []string{"grey_heart"}, "15.0", false},
|
||||
{"\u2754", "white question mark", []string{"grey_question"}, "6.0", false},
|
||||
{"\U0001f62c", "grimacing face", []string{"grimacing"}, "6.1", false},
|
||||
{"\U0001f601", "beaming face with smiling eyes", []string{"grin"}, "6.0", false},
|
||||
@ -1129,6 +1136,7 @@ var GemojiData = Gemoji{
|
||||
{"\U0001f3b8", "guitar", []string{"guitar"}, "6.0", false},
|
||||
{"\U0001f52b", "water pistol", []string{"gun"}, "6.0", false},
|
||||
{"\U0001f1ec\U0001f1fe", "flag: Guyana", []string{"guyana"}, "6.0", false},
|
||||
{"\U0001faae", "hair pick", []string{"hair_pick"}, "15.0", false},
|
||||
{"\U0001f487", "person getting haircut", []string{"haircut"}, "6.0", true},
|
||||
{"\U0001f487\U0001f3ff", "person getting haircut: Dark Skin Tone", []string{"haircut_Dark_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001f487\U0001f3fb", "person getting haircut: Light Skin Tone", []string{"haircut_Light_Skin_Tone"}, "12.0", false},
|
||||
@ -1253,6 +1261,7 @@ var GemojiData = Gemoji{
|
||||
{"\U0001f1ed\U0001f1fa", "flag: Hungary", []string{"hungary"}, "6.0", false},
|
||||
{"\U0001f62f", "hushed face", []string{"hushed"}, "6.1", false},
|
||||
{"\U0001f6d6", "hut", []string{"hut"}, "13.0", false},
|
||||
{"\U0001fabb", "hyacinth", []string{"hyacinth"}, "15.0", false},
|
||||
{"\U0001f368", "ice cream", []string{"ice_cream"}, "6.0", false},
|
||||
{"\U0001f9ca", "ice", []string{"ice_cube"}, "12.0", false},
|
||||
{"\U0001f3d2", "ice hockey", []string{"ice_hockey"}, "8.0", false},
|
||||
@ -1293,6 +1302,7 @@ var GemojiData = Gemoji{
|
||||
{"\U0001f479", "ogre", []string{"japanese_ogre"}, "6.0", false},
|
||||
{"\U0001fad9", "jar", []string{"jar"}, "14.0", false},
|
||||
{"\U0001f456", "jeans", []string{"jeans"}, "6.0", false},
|
||||
{"\U0001fabc", "jellyfish", []string{"jellyfish"}, "15.0", false},
|
||||
{"\U0001f1ef\U0001f1ea", "flag: Jersey", []string{"jersey"}, "6.0", false},
|
||||
{"\U0001f9e9", "puzzle piece", []string{"jigsaw"}, "11.0", false},
|
||||
{"\U0001f1ef\U0001f1f4", "flag: Jordan", []string{"jordan"}, "6.0", false},
|
||||
@ -1319,6 +1329,7 @@ var GemojiData = Gemoji{
|
||||
{"\U0001f511", "key", []string{"key"}, "6.0", false},
|
||||
{"\u2328\ufe0f", "keyboard", []string{"keyboard"}, "", false},
|
||||
{"\U0001f51f", "keycap: 10", []string{"keycap_ten"}, "6.0", false},
|
||||
{"\U0001faaf", "khanda", []string{"khanda"}, "15.0", false},
|
||||
{"\U0001f6f4", "kick scooter", []string{"kick_scooter"}, "9.0", false},
|
||||
{"\U0001f458", "kimono", []string{"kimono"}, "6.0", false},
|
||||
{"\U0001f1f0\U0001f1ee", "flag: Kiribati", []string{"kiribati"}, "6.0", false},
|
||||
@ -1383,6 +1394,12 @@ var GemojiData = Gemoji{
|
||||
{"\U0001faf2\U0001f3fe", "leftwards hand: Medium-Dark Skin Tone", []string{"leftwards_hand_Medium-Dark_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001faf2\U0001f3fc", "leftwards hand: Medium-Light Skin Tone", []string{"leftwards_hand_Medium-Light_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001faf2\U0001f3fd", "leftwards hand: Medium Skin Tone", []string{"leftwards_hand_Medium_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001faf7", "leftwards pushing hand", []string{"leftwards_pushing_hand"}, "15.0", true},
|
||||
{"\U0001faf7\U0001f3ff", "leftwards pushing hand: Dark Skin Tone", []string{"leftwards_pushing_hand_Dark_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001faf7\U0001f3fb", "leftwards pushing hand: Light Skin Tone", []string{"leftwards_pushing_hand_Light_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001faf7\U0001f3fe", "leftwards pushing hand: Medium-Dark Skin Tone", []string{"leftwards_pushing_hand_Medium-Dark_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001faf7\U0001f3fc", "leftwards pushing hand: Medium-Light Skin Tone", []string{"leftwards_pushing_hand_Medium-Light_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001faf7\U0001f3fd", "leftwards pushing hand: Medium Skin Tone", []string{"leftwards_pushing_hand_Medium_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001f9b5", "leg", []string{"leg"}, "11.0", true},
|
||||
{"\U0001f9b5\U0001f3ff", "leg: Dark Skin Tone", []string{"leg_Dark_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001f9b5\U0001f3fb", "leg: Light Skin Tone", []string{"leg_Light_Skin_Tone"}, "12.0", false},
|
||||
@ -1398,6 +1415,7 @@ var GemojiData = Gemoji{
|
||||
{"\u264e", "Libra", []string{"libra"}, "", false},
|
||||
{"\U0001f1f1\U0001f1fe", "flag: Libya", []string{"libya"}, "6.0", false},
|
||||
{"\U0001f1f1\U0001f1ee", "flag: Liechtenstein", []string{"liechtenstein"}, "6.0", false},
|
||||
{"\U0001fa75", "light blue heart", []string{"light_blue_heart"}, "15.0", false},
|
||||
{"\U0001f688", "light rail", []string{"light_rail"}, "6.0", false},
|
||||
{"\U0001f517", "link", []string{"link"}, "6.0", false},
|
||||
{"\U0001f981", "lion", []string{"lion"}, "8.0", false},
|
||||
@ -1695,6 +1713,7 @@ var GemojiData = Gemoji{
|
||||
{"\U0001f570\ufe0f", "mantelpiece clock", []string{"mantelpiece_clock"}, "7.0", false},
|
||||
{"\U0001f9bd", "manual wheelchair", []string{"manual_wheelchair"}, "12.0", false},
|
||||
{"\U0001f341", "maple leaf", []string{"maple_leaf"}, "6.0", false},
|
||||
{"\U0001fa87", "maracas", []string{"maracas"}, "15.0", false},
|
||||
{"\U0001f1f2\U0001f1ed", "flag: Marshall Islands", []string{"marshall_islands"}, "6.0", false},
|
||||
{"\U0001f94b", "martial arts uniform", []string{"martial_arts_uniform"}, "9.0", false},
|
||||
{"\U0001f1f2\U0001f1f6", "flag: Martinique", []string{"martinique"}, "6.0", false},
|
||||
@ -1799,6 +1818,7 @@ var GemojiData = Gemoji{
|
||||
{"\U0001f1f2\U0001f1f8", "flag: Montserrat", []string{"montserrat"}, "6.0", false},
|
||||
{"\U0001f314", "waxing gibbous moon", []string{"moon", "waxing_gibbous_moon"}, "6.0", false},
|
||||
{"\U0001f96e", "moon cake", []string{"moon_cake"}, "11.0", false},
|
||||
{"\U0001face", "moose", []string{"moose"}, "15.0", false},
|
||||
{"\U0001f1f2\U0001f1e6", "flag: Morocco", []string{"morocco"}, "6.0", false},
|
||||
{"\U0001f393", "graduation cap", []string{"mortar_board"}, "6.0", false},
|
||||
{"\U0001f54c", "mosque", []string{"mosque"}, "8.0", false},
|
||||
@ -2076,6 +2096,7 @@ var GemojiData = Gemoji{
|
||||
{"\U0001f6f3\ufe0f", "passenger ship", []string{"passenger_ship"}, "7.0", false},
|
||||
{"\U0001f6c2", "passport control", []string{"passport_control"}, "6.0", false},
|
||||
{"\u23f8\ufe0f", "pause button", []string{"pause_button"}, "7.0", false},
|
||||
{"\U0001fadb", "pea pod", []string{"pea_pod"}, "15.0", false},
|
||||
{"\u262e\ufe0f", "peace symbol", []string{"peace_symbol"}, "", false},
|
||||
{"\U0001f351", "peach", []string{"peach"}, "6.0", false},
|
||||
{"\U0001f99a", "peacock", []string{"peacock"}, "11.0", false},
|
||||
@ -2085,7 +2106,12 @@ var GemojiData = Gemoji{
|
||||
{"\u270f\ufe0f", "pencil", []string{"pencil2"}, "", false},
|
||||
{"\U0001f427", "penguin", []string{"penguin"}, "6.0", false},
|
||||
{"\U0001f614", "pensive face", []string{"pensive"}, "6.0", false},
|
||||
{"\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands", []string{"people_holding_hands"}, "12.0", false},
|
||||
{"\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands", []string{"people_holding_hands"}, "12.0", true},
|
||||
{"\U0001f9d1\U0001f3ff\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands: Dark Skin Tone", []string{"people_holding_hands_Dark_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001f9d1\U0001f3fb\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands: Light Skin Tone", []string{"people_holding_hands_Light_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001f9d1\U0001f3fe\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands: Medium-Dark Skin Tone", []string{"people_holding_hands_Medium-Dark_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001f9d1\U0001f3fc\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands: Medium-Light Skin Tone", []string{"people_holding_hands_Medium-Light_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001f9d1\U0001f3fd\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands: Medium Skin Tone", []string{"people_holding_hands_Medium_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001fac2", "people hugging", []string{"people_hugging"}, "13.0", false},
|
||||
{"\U0001f3ad", "performing arts", []string{"performing_arts"}, "6.0", false},
|
||||
{"\U0001f623", "persevering face", []string{"persevere"}, "6.0", false},
|
||||
@ -2194,6 +2220,7 @@ var GemojiData = Gemoji{
|
||||
{"\U0001f90f\U0001f3fd", "pinching hand: Medium Skin Tone", []string{"pinching_hand_Medium_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001f34d", "pineapple", []string{"pineapple"}, "6.0", false},
|
||||
{"\U0001f3d3", "ping pong", []string{"ping_pong"}, "8.0", false},
|
||||
{"\U0001fa77", "pink heart", []string{"pink_heart"}, "15.0", false},
|
||||
{"\U0001f3f4\u200d\u2620\ufe0f", "pirate flag", []string{"pirate_flag"}, "11.0", false},
|
||||
{"\u2653", "Pisces", []string{"pisces"}, "", false},
|
||||
{"\U0001f1f5\U0001f1f3", "flag: Pitcairn Islands", []string{"pitcairn_islands"}, "6.0", false},
|
||||
@ -2346,7 +2373,7 @@ var GemojiData = Gemoji{
|
||||
{"\U0001f4fb", "radio", []string{"radio"}, "6.0", false},
|
||||
{"\U0001f518", "radio button", []string{"radio_button"}, "6.0", false},
|
||||
{"\u2622\ufe0f", "radioactive", []string{"radioactive"}, "", false},
|
||||
{"\U0001f621", "pouting face", []string{"rage", "pout"}, "6.0", false},
|
||||
{"\U0001f621", "enraged face", []string{"rage", "pout"}, "6.0", false},
|
||||
{"\U0001f683", "railway car", []string{"railway_car"}, "6.0", false},
|
||||
{"\U0001f6e4\ufe0f", "railway track", []string{"railway_track"}, "7.0", false},
|
||||
{"\U0001f308", "rainbow", []string{"rainbow"}, "6.0", false},
|
||||
@ -2434,6 +2461,12 @@ var GemojiData = Gemoji{
|
||||
{"\U0001faf1\U0001f3fe", "rightwards hand: Medium-Dark Skin Tone", []string{"rightwards_hand_Medium-Dark_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001faf1\U0001f3fc", "rightwards hand: Medium-Light Skin Tone", []string{"rightwards_hand_Medium-Light_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001faf1\U0001f3fd", "rightwards hand: Medium Skin Tone", []string{"rightwards_hand_Medium_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001faf8", "rightwards pushing hand", []string{"rightwards_pushing_hand"}, "15.0", true},
|
||||
{"\U0001faf8\U0001f3ff", "rightwards pushing hand: Dark Skin Tone", []string{"rightwards_pushing_hand_Dark_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001faf8\U0001f3fb", "rightwards pushing hand: Light Skin Tone", []string{"rightwards_pushing_hand_Light_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001faf8\U0001f3fe", "rightwards pushing hand: Medium-Dark Skin Tone", []string{"rightwards_pushing_hand_Medium-Dark_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001faf8\U0001f3fc", "rightwards pushing hand: Medium-Light Skin Tone", []string{"rightwards_pushing_hand_Medium-Light_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001faf8\U0001f3fd", "rightwards pushing hand: Medium Skin Tone", []string{"rightwards_pushing_hand_Medium_Skin_Tone"}, "12.0", false},
|
||||
{"\U0001f48d", "ring", []string{"ring"}, "6.0", false},
|
||||
{"\U0001f6df", "ring buoy", []string{"ring_buoy"}, "14.0", false},
|
||||
{"\U0001fa90", "ringed planet", []string{"ringed_planet"}, "12.0", false},
|
||||
@ -2566,6 +2599,7 @@ var GemojiData = Gemoji{
|
||||
{"7\ufe0f\u20e3", "keycap: 7", []string{"seven"}, "", false},
|
||||
{"\U0001faa1", "sewing needle", []string{"sewing_needle"}, "13.0", false},
|
||||
{"\U0001f1f8\U0001f1e8", "flag: Seychelles", []string{"seychelles"}, "6.0", false},
|
||||
{"\U0001fae8", "shaking face", []string{"shaking_face"}, "15.0", false},
|
||||
{"\U0001f958", "shallow pan of food", []string{"shallow_pan_of_food"}, "", false},
|
||||
{"\u2618\ufe0f", "shamrock", []string{"shamrock"}, "4.1", false},
|
||||
{"\U0001f988", "shark", []string{"shark"}, "9.0", false},
|
||||
@ -3125,7 +3159,9 @@ var GemojiData = Gemoji{
|
||||
{"\U0001f32c\ufe0f", "wind face", []string{"wind_face"}, "7.0", false},
|
||||
{"\U0001fa9f", "window", []string{"window"}, "13.0", false},
|
||||
{"\U0001f377", "wine glass", []string{"wine_glass"}, "6.0", false},
|
||||
{"\U0001fabd", "wing", []string{"wing"}, "15.0", false},
|
||||
{"\U0001f609", "winking face", []string{"wink"}, "6.0", false},
|
||||
{"\U0001f6dc", "wireless", []string{"wireless"}, "15.0", false},
|
||||
{"\U0001f43a", "wolf", []string{"wolf"}, "6.0", false},
|
||||
{"\U0001f469", "woman", []string{"woman"}, "6.0", true},
|
||||
{"\U0001f469\U0001f3ff", "woman: Dark Skin Tone", []string{"woman_Dark_Skin_Tone"}, "12.0", false},
|
||||
@ -3364,5 +3400,5 @@ var GemojiData = Gemoji{
|
||||
{"\U0001f9df", "zombie", []string{"zombie"}, "11.0", false},
|
||||
{"\U0001f9df\u200d\u2642\ufe0f", "man zombie", []string{"zombie_man"}, "11.0", false},
|
||||
{"\U0001f9df\u200d\u2640\ufe0f", "woman zombie", []string{"zombie_woman"}, "11.0", false},
|
||||
{"\U0001f4a4", "zzz", []string{"zzz"}, "6.0", false},
|
||||
{"\U0001f4a4", "ZZZ", []string{"zzz"}, "6.0", false},
|
||||
}
|
||||
|
||||
@ -79,5 +79,5 @@ func SetConsoleLogger(loggerName, writerName string, level Level) {
|
||||
Colorize: CanColorStdout,
|
||||
WriterOption: WriterConsoleOption{},
|
||||
})
|
||||
GetManager().GetLogger(loggerName).RemoveAllWriters().AddWriters(writer)
|
||||
GetManager().GetLogger(loggerName).ReplaceAllWriters(writer)
|
||||
}
|
||||
|
||||
@ -96,7 +96,10 @@ func (l *LoggerImpl) removeWriterInternal(w EventWriter) {
|
||||
func (l *LoggerImpl) AddWriters(writer ...EventWriter) {
|
||||
l.eventWriterMu.Lock()
|
||||
defer l.eventWriterMu.Unlock()
|
||||
l.addWritersInternal(writer...)
|
||||
}
|
||||
|
||||
func (l *LoggerImpl) addWritersInternal(writer ...EventWriter) {
|
||||
for _, w := range writer {
|
||||
if old, ok := l.eventWriters[w.GetWriterName()]; ok {
|
||||
l.removeWriterInternal(old)
|
||||
@ -126,8 +129,8 @@ func (l *LoggerImpl) RemoveWriter(modeName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveAllWriters removes all writers from the logger, non-shared writers are closed and flushed
|
||||
func (l *LoggerImpl) RemoveAllWriters() *LoggerImpl {
|
||||
// ReplaceAllWriters replaces all writers from the logger, non-shared writers are closed and flushed
|
||||
func (l *LoggerImpl) ReplaceAllWriters(writer ...EventWriter) {
|
||||
l.eventWriterMu.Lock()
|
||||
defer l.eventWriterMu.Unlock()
|
||||
|
||||
@ -135,8 +138,7 @@ func (l *LoggerImpl) RemoveAllWriters() *LoggerImpl {
|
||||
l.removeWriterInternal(w)
|
||||
}
|
||||
l.eventWriters = map[string]EventWriter{}
|
||||
l.syncLevelInternal()
|
||||
return l
|
||||
l.addWritersInternal(writer...)
|
||||
}
|
||||
|
||||
// DumpWriters dumps the writers as a JSON map, it's used for debugging and display purposes.
|
||||
@ -161,7 +163,7 @@ func (l *LoggerImpl) DumpWriters() map[string]any {
|
||||
|
||||
// Close closes the logger, non-shared writers are closed and flushed
|
||||
func (l *LoggerImpl) Close() {
|
||||
l.RemoveAllWriters()
|
||||
l.ReplaceAllWriters()
|
||||
l.ctxCancel()
|
||||
}
|
||||
|
||||
@ -233,7 +235,6 @@ func NewLoggerWithWriters(ctx context.Context, name string, writer ...EventWrite
|
||||
l.ctx, l.ctxCancel = newProcessTypedContext(ctx, "Logger: "+name)
|
||||
l.LevelLogger = BaseLoggerToGeneralLogger(l)
|
||||
l.eventWriters = map[string]EventWriter{}
|
||||
l.syncLevelInternal()
|
||||
l.AddWriters(writer...)
|
||||
return l
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ func TestSharedWorker(t *testing.T) {
|
||||
loggerTest := m.GetLogger("test")
|
||||
loggerTest.AddWriters(w)
|
||||
loggerTest.Info("msg-1")
|
||||
loggerTest.RemoveAllWriters() // the shared writer is not closed here
|
||||
loggerTest.ReplaceAllWriters() // the shared writer is not closed here
|
||||
loggerTest.Info("never seen")
|
||||
|
||||
// the shared writer can still be used later
|
||||
|
||||
135
modules/repository/branch.go
Normal file
135
modules/repository/branch.go
Normal file
@ -0,0 +1,135 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
)
|
||||
|
||||
// SyncRepoBranches synchronizes branch table with repository branches
|
||||
func SyncRepoBranches(ctx context.Context, repoID, doerID int64) (int64, error) {
|
||||
repo, err := repo_model.GetRepositoryByID(ctx, repoID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
log.Debug("SyncRepoBranches: in Repo[%d:%s]", repo.ID, repo.FullName())
|
||||
|
||||
gitRepo, err := git.OpenRepository(ctx, repo.RepoPath())
|
||||
if err != nil {
|
||||
log.Error("OpenRepository[%s]: %w", repo.RepoPath(), err)
|
||||
return 0, err
|
||||
}
|
||||
defer gitRepo.Close()
|
||||
|
||||
return SyncRepoBranchesWithRepo(ctx, repo, gitRepo, doerID)
|
||||
}
|
||||
|
||||
func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, doerID int64) (int64, error) {
|
||||
allBranches := container.Set[string]{}
|
||||
{
|
||||
branches, _, err := gitRepo.GetBranchNames(0, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
log.Trace("SyncRepoBranches[%s]: branches[%d]: %v", repo.FullName(), len(branches), branches)
|
||||
for _, branch := range branches {
|
||||
allBranches.Add(branch)
|
||||
}
|
||||
}
|
||||
|
||||
dbBranches := make(map[string]*git_model.Branch)
|
||||
{
|
||||
branches, err := git_model.FindBranches(ctx, git_model.FindBranchOptions{
|
||||
ListOptions: db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
RepoID: repo.ID,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, branch := range branches {
|
||||
dbBranches[branch.Name] = branch
|
||||
}
|
||||
}
|
||||
|
||||
var toAdd []*git_model.Branch
|
||||
var toUpdate []*git_model.Branch
|
||||
var toRemove []int64
|
||||
for branch := range allBranches {
|
||||
dbb := dbBranches[branch]
|
||||
commit, err := gitRepo.GetBranchCommit(branch)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if dbb == nil {
|
||||
toAdd = append(toAdd, &git_model.Branch{
|
||||
RepoID: repo.ID,
|
||||
Name: branch,
|
||||
CommitID: commit.ID.String(),
|
||||
CommitMessage: commit.CommitMessage,
|
||||
PusherID: doerID,
|
||||
CommitTime: timeutil.TimeStamp(commit.Author.When.Unix()),
|
||||
})
|
||||
} else if commit.ID.String() != dbb.CommitID {
|
||||
toUpdate = append(toUpdate, &git_model.Branch{
|
||||
ID: dbb.ID,
|
||||
RepoID: repo.ID,
|
||||
Name: branch,
|
||||
CommitID: commit.ID.String(),
|
||||
CommitMessage: commit.CommitMessage,
|
||||
PusherID: doerID,
|
||||
CommitTime: timeutil.TimeStamp(commit.Author.When.Unix()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for _, dbBranch := range dbBranches {
|
||||
if !allBranches.Contains(dbBranch.Name) && !dbBranch.IsDeleted {
|
||||
toRemove = append(toRemove, dbBranch.ID)
|
||||
}
|
||||
}
|
||||
|
||||
log.Trace("SyncRepoBranches[%s]: toAdd: %v, toUpdate: %v, toRemove: %v", repo.FullName(), toAdd, toUpdate, toRemove)
|
||||
|
||||
if len(toAdd) == 0 && len(toRemove) == 0 && len(toUpdate) == 0 {
|
||||
return int64(len(allBranches)), nil
|
||||
}
|
||||
|
||||
if err := db.WithTx(ctx, func(subCtx context.Context) error {
|
||||
if len(toAdd) > 0 {
|
||||
if err := git_model.AddBranches(subCtx, toAdd); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, b := range toUpdate {
|
||||
if _, err := db.GetEngine(subCtx).ID(b.ID).
|
||||
Cols("commit_id, commit_message, pusher_id, commit_time, is_deleted").
|
||||
Update(b); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(toRemove) > 0 {
|
||||
if err := git_model.DeleteBranches(subCtx, repo.ID, doerID, toRemove); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int64(len(allBranches)), nil
|
||||
}
|
||||
@ -330,7 +330,7 @@ func UpdateRepoSize(ctx context.Context, repo *repo_model.Repository) error {
|
||||
return fmt.Errorf("updateSize: GetLFSMetaObjects: %w", err)
|
||||
}
|
||||
|
||||
return repo_model.UpdateRepoSize(ctx, repo.ID, size+lfsSize)
|
||||
return repo_model.UpdateRepoSize(ctx, repo.ID, size, lfsSize)
|
||||
}
|
||||
|
||||
// CheckDaemonExportOK creates/removes git-daemon-export-ok for git-daemon...
|
||||
|
||||
@ -351,6 +351,12 @@ func initRepository(ctx context.Context, repoPath string, u *user_model.User, re
|
||||
if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil {
|
||||
return fmt.Errorf("setDefaultBranch: %w", err)
|
||||
}
|
||||
|
||||
if !repo.IsEmpty {
|
||||
if _, err := SyncRepoBranches(ctx, repo.ID, u.ID); err != nil {
|
||||
return fmt.Errorf("SyncRepoBranches: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err = UpdateRepository(ctx, repo, false); err != nil {
|
||||
|
||||
@ -151,6 +151,10 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := SyncRepoBranchesWithRepo(ctx, repo, gitRepo, u.ID); err != nil {
|
||||
return repo, fmt.Errorf("SyncRepoBranchesWithRepo: %v", err)
|
||||
}
|
||||
|
||||
if !opts.Releases {
|
||||
// note: this will greatly improve release (tag) sync
|
||||
// for pull-mirrors with many tags
|
||||
@ -169,7 +173,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
|
||||
}
|
||||
}
|
||||
|
||||
ctx, committer, err := db.TxContext(db.DefaultContext)
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -244,7 +244,7 @@ func initLoggerByName(manager *log.LoggerManager, rootCfg ConfigProvider, logger
|
||||
eventWriters = append(eventWriters, eventWriter)
|
||||
}
|
||||
|
||||
manager.GetLogger(loggerName).RemoveAllWriters().AddWriters(eventWriters...)
|
||||
manager.GetLogger(loggerName).ReplaceAllWriters(eventWriters...)
|
||||
}
|
||||
|
||||
func InitSQLLoggersForCli(level log.Level) {
|
||||
|
||||
@ -116,6 +116,10 @@ func loadOAuth2From(rootCfg ConfigProvider) {
|
||||
return
|
||||
}
|
||||
|
||||
if !OAuth2.Enable {
|
||||
return
|
||||
}
|
||||
|
||||
OAuth2.JWTSecretBase64 = loadSecret(rootCfg.Section("oauth2"), "JWT_SECRET_URI", "JWT_SECRET")
|
||||
|
||||
if !filepath.IsAbs(OAuth2.JWTSigningPrivateKeyFile) {
|
||||
|
||||
@ -380,3 +380,9 @@ type NewIssuePinsAllowed struct {
|
||||
Issues bool `json:"issues"`
|
||||
PullRequests bool `json:"pull_requests"`
|
||||
}
|
||||
|
||||
// UpdateRepoAvatarUserOption options when updating the repo avatar
|
||||
type UpdateRepoAvatarOption struct {
|
||||
// image must be base64 encoded
|
||||
Image string `json:"image" binding:"Required"`
|
||||
}
|
||||
|
||||
@ -102,3 +102,9 @@ type RenameUserOption struct {
|
||||
// unique: true
|
||||
NewName string `json:"new_username" binding:"Required"`
|
||||
}
|
||||
|
||||
// UpdateUserAvatarUserOption options when updating the user avatar
|
||||
type UpdateUserAvatarOption struct {
|
||||
// image must be base64 encoded
|
||||
Image string `json:"image" binding:"Required"`
|
||||
}
|
||||
|
||||
@ -129,6 +129,7 @@ concept_user_organization = Organization
|
||||
show_timestamps = Show timestamps
|
||||
show_log_seconds = Show seconds
|
||||
show_full_screen = Show full screen
|
||||
download_logs = Download logs
|
||||
|
||||
confirm_delete_selected = Confirm to delete all selected items?
|
||||
|
||||
@ -2659,6 +2660,7 @@ dashboard.delete_repo_archives.started = Delete all repository archives task sta
|
||||
dashboard.delete_missing_repos = Delete all repositories missing their Git files
|
||||
dashboard.delete_missing_repos.started = Delete all repositories missing their Git files task started.
|
||||
dashboard.delete_generated_repository_avatars = Delete generated repository avatars
|
||||
dashboard.sync_repo_branches = Sync missed branches from git data to databases
|
||||
dashboard.update_mirrors = Update Mirrors
|
||||
dashboard.repo_health_check = Health check all repositories
|
||||
dashboard.check_repo_stats = Check all repository statistics
|
||||
@ -2712,6 +2714,7 @@ dashboard.gc_lfs = Garbage collect LFS meta objects
|
||||
dashboard.stop_zombie_tasks = Stop zombie tasks
|
||||
dashboard.stop_endless_tasks = Stop endless tasks
|
||||
dashboard.cancel_abandoned_jobs = Cancel abandoned jobs
|
||||
dashboard.sync_branch.started = Branches Sync started
|
||||
|
||||
users.user_manage_panel = User Account Management
|
||||
users.new_account = Create User Account
|
||||
@ -2797,6 +2800,7 @@ repos.stars = Stars
|
||||
repos.forks = Forks
|
||||
repos.issues = Issues
|
||||
repos.size = Size
|
||||
repos.lfs_size = LFS Size
|
||||
|
||||
packages.package_manage_panel = Package Management
|
||||
packages.total_size = Total Size: %s
|
||||
|
||||
@ -3122,7 +3122,7 @@ notices.delete_success=システム通知を削除しました。
|
||||
|
||||
[action]
|
||||
create_repo=がリポジトリ <a href="%s">%s</a> を作成しました
|
||||
rename_repo=がリポジトリ名を <code>%[1]s</code> から <a href="%[2]s">[3]s</a> へ変更しました
|
||||
rename_repo=がリポジトリ名を <code>%[1]s</code> から <a href="%[2]s">%[3]s</a> へ変更しました
|
||||
commit_repo=が <a href="%[1]s">%[4]s</a> の <a href="%[2]s">%[3]s</a> にプッシュしました
|
||||
create_issue=`がイシュー <a href="%[1]s">%[3]s#%[2]s</a> をオープンしました`
|
||||
close_issue=`がイシュー <a href="%[1]s">%[3]s#%[2]s</a> をクローズしました`
|
||||
|
||||
@ -79,6 +79,8 @@ milestones=Marcos
|
||||
|
||||
ok=Ok
|
||||
cancel=Cancelar
|
||||
rerun=Reexecutar
|
||||
rerun_all=Reexecutar todas as tarefas
|
||||
save=Salvar
|
||||
add=Adicionar
|
||||
add_all=Adicionar todos
|
||||
@ -113,11 +115,18 @@ unknown=Desconhecido
|
||||
|
||||
rss_feed=Feed RSS
|
||||
|
||||
pin=Fixar
|
||||
unpin=Desfixar
|
||||
|
||||
artifacts=Artefatos
|
||||
|
||||
concept_system_global=Global
|
||||
concept_user_individual=Individual
|
||||
concept_code_repository=Repositório
|
||||
concept_user_organization=Organização
|
||||
|
||||
show_log_seconds=Mostrar segundos
|
||||
show_full_screen=Mostrar tela cheia
|
||||
|
||||
[aria]
|
||||
navbar=Barra de navegação
|
||||
@ -142,6 +151,7 @@ buttons.list.unordered.tooltip=Adicionar uma lista com marcadores
|
||||
buttons.list.ordered.tooltip=Adicionar uma lista numerada
|
||||
buttons.list.task.tooltip=Adicionar uma lista de tarefas
|
||||
buttons.mention.tooltip=Mencionar um usuário ou equipe
|
||||
buttons.ref.tooltip=Referenciar um issue ou um pull request
|
||||
buttons.switch_to_legacy.tooltip=Em vez disso, usar o editor legado
|
||||
buttons.enable_monospace_font=Habilitar fonte mono espaçada
|
||||
buttons.disable_monospace_font=Desabilitar fonte mono espaçada
|
||||
@ -247,6 +257,7 @@ openid_signup_popup=Habilitar o auto-cadastro com base no OpenID.
|
||||
enable_captcha=Habilitar CAPTCHA ao registrar
|
||||
enable_captcha_popup=Obrigar validação por CAPTCHA para auto-cadastro de usuários.
|
||||
require_sign_in_view=Exigir acesso do usuário para a visualização de páginas
|
||||
require_sign_in_view_popup=Limitar o acesso de página aos usuários autenticados. Os visitantes só verão as páginas de autenticação e cadastro.
|
||||
admin_setting_desc=Criar uma conta de administrador é opcional. O primeiro usuário cadastrado automaticamente se tornará um administrador.
|
||||
admin_title=Configurações da conta de administrador
|
||||
admin_name=Nome do usuário administrador
|
||||
@ -312,6 +323,7 @@ repos=Repositórios
|
||||
users=Usuários
|
||||
organizations=Organizações
|
||||
search=Pesquisar
|
||||
go_to=Ir para
|
||||
code=Código
|
||||
search.type.tooltip=Tipo de pesquisa
|
||||
search.fuzzy=Similar
|
||||
@ -467,6 +479,7 @@ team_invite.text_3=Nota: este convite foi destinado a %[1]s. Se você não estav
|
||||
[modal]
|
||||
yes=Sim
|
||||
no=Não
|
||||
confirm=Confirmar
|
||||
cancel=Cancelar
|
||||
modify=Atualizar
|
||||
|
||||
@ -514,6 +527,7 @@ lang_select_error=Selecione um idioma da lista.
|
||||
|
||||
username_been_taken=O nome de usuário já está sendo usado.
|
||||
username_change_not_local_user=Usuários não-locais não são autorizados a alterar nome de usuário.
|
||||
username_has_not_been_changed=Nome de usuário não foi alterado
|
||||
repo_name_been_taken=O nome de repositório já está sendo usado.
|
||||
repository_force_private=Forçar Privado está ativado: repositórios privados não podem ser tornados públicos.
|
||||
repository_files_already_exist=Arquivos já existem neste repositório. Contate o administrador.
|
||||
@ -555,11 +569,14 @@ auth_failed=Autenticação falhou: %v
|
||||
still_own_repo=Sua conta possui um ou mais repositórios, exclua ou transfira-os primeiro.
|
||||
still_has_org=Sua conta é um membro de uma ou mais organizações, deixe-as primeiro.
|
||||
still_own_packages=Sua conta possui um ou mais pacotes, exclua-os primeiro.
|
||||
org_still_own_repo=Esta organização ainda possui repositórios, exclua ou transfira-os primeiro.
|
||||
org_still_own_packages=Esta organização ainda possui pacotes, exclua-os primeiro.
|
||||
|
||||
target_branch_not_exist=O branch de destino não existe.
|
||||
|
||||
[user]
|
||||
change_avatar=Altere seu avatar...
|
||||
joined_on=Inscreveu-se em %s
|
||||
repositories=Repositórios
|
||||
activity=Atividade pública
|
||||
followers=Seguidores
|
||||
@ -684,10 +701,12 @@ add_new_email=Adicionar novo endereço de e-mail
|
||||
add_new_openid=Adicionar novo URI OpenID
|
||||
add_email=Adicionar novo endereço de e-mail
|
||||
add_openid=Adicionar URI OpenID
|
||||
add_email_confirmation_sent=Um e-mail de confirmação foi enviado para "%s". Verifique sua caixa de entrada nos próximos %s para confirmar seu endereço de e-mail.
|
||||
add_email_success=O novo endereço de e-mail foi adicionado.
|
||||
email_preference_set_success=Preferência de e-mail definida com sucesso.
|
||||
add_openid_success=O novo endereço de OpenID foi adicionado.
|
||||
keep_email_private=Ocultar endereço de e-mail
|
||||
keep_email_private_popup=Seu endereço de e-mail ficará visível apenas para você e para os administradores
|
||||
openid_desc=OpenID permite delegar autenticação para um provedor externo.
|
||||
|
||||
manage_ssh_keys=Gerenciar Chaves SSH
|
||||
@ -721,6 +740,7 @@ gpg_token_help=Você pode gerar uma assinatura usando:
|
||||
gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
|
||||
gpg_token_signature=Assinatura GPG blindada
|
||||
key_signature_gpg_placeholder=Começa com '-----BEGIN PGP SIGNATURE-----'
|
||||
verify_gpg_key_success=A chave GPG "%s" foi validada.
|
||||
ssh_key_verified=Chave validada
|
||||
ssh_key_verified_long=A chave foi validada com um token e pode ser usada para validar commits que correspondam a qualquer dos endereços de e-mail ativados deste usuário.
|
||||
ssh_key_verify=Validar
|
||||
@ -730,11 +750,14 @@ ssh_token=Token
|
||||
ssh_token_help=Você pode gerar uma assinatura usando:
|
||||
ssh_token_signature=Assinatura SSH blindada
|
||||
key_signature_ssh_placeholder=Começa com '-----BEGIN SSH SIGNATURE-----'
|
||||
verify_ssh_key_success=A chave SSH "%s" foi validada.
|
||||
subkeys=Subchaves
|
||||
key_id=ID da chave
|
||||
key_name=Nome da Chave
|
||||
key_content=Conteúdo
|
||||
principal_content=Conteúdo
|
||||
add_key_success=A chave SSH "%s" foi adicionada.
|
||||
add_gpg_key_success=A chave GPG "%s" foi adicionada.
|
||||
delete_key=Remover
|
||||
ssh_key_deletion=Remover a chave SSH
|
||||
gpg_key_deletion=Remover a chave GPG
|
||||
@ -745,6 +768,8 @@ ssh_principal_deletion_desc=A exclusão de um Nome Principal de um Certificado S
|
||||
ssh_key_deletion_success=A chave SSH foi removida.
|
||||
gpg_key_deletion_success=A chave GPG foi removida.
|
||||
ssh_principal_deletion_success=O nome principal foi removido.
|
||||
added_on=Adicionado em %s
|
||||
valid_until_date=Válido até %s
|
||||
valid_forever=Válido para sempre
|
||||
last_used=Última vez usado em
|
||||
no_activity=Nenhuma atividade recente
|
||||
@ -756,6 +781,7 @@ principal_state_desc=Este nome principal foi utilizado nos últimos 7 dias
|
||||
show_openid=Mostrar no perfil
|
||||
hide_openid=Ocultar no perfil
|
||||
ssh_disabled=SSH desabilitado
|
||||
ssh_signonly=O SSH está desativado no momento, portanto, essas chaves são usadas apenas para verificação de assinatura de confirmação.
|
||||
ssh_externally_managed=Esta chave SSH para este usuário é gerenciada externamente
|
||||
manage_social=Gerenciar contas sociais associadas
|
||||
social_desc=Essas contas sociais estão vinculadas à sua conta do Gitea. Certifique-se de reconhecer todas elas, pois elas podem ser usadas para acessar a sua conta do Gitea.
|
||||
@ -775,6 +801,11 @@ access_token_deletion_cancel_action=Cancelar
|
||||
access_token_deletion_confirm_action=Excluir
|
||||
access_token_deletion_desc=A exclusão de um token revoga o acesso à sua conta para aplicativos que o usam. Continuar?
|
||||
delete_token_success=O token foi excluído. Os aplicativos que o utilizam já não têm acesso à sua conta.
|
||||
repo_and_org_access=Acesso ao Repositório e Organização
|
||||
permissions_public_only=Apenas público
|
||||
permissions_access_all=Todos (público, privado e limitado)
|
||||
select_permissions=Selecionar permissões
|
||||
permissions_list=Permissões:
|
||||
|
||||
manage_oauth2_applications=Gerenciar aplicativos OAuth2
|
||||
edit_oauth2_application=Editar aplicativo OAuth2
|
||||
@ -859,6 +890,7 @@ visibility=Visibilidade do usuário
|
||||
visibility.public=Pública
|
||||
visibility.public_tooltip=Visível para todos
|
||||
visibility.limited=Limitada
|
||||
visibility.limited_tooltip=Visível apenas para usuários autenticados
|
||||
visibility.private=Privada
|
||||
visibility.private_tooltip=Visível apenas para membros da organização
|
||||
|
||||
@ -1012,6 +1044,7 @@ migrated_from_fake=Migrado de %[1]s
|
||||
migrate.migrate=Migrar de %s
|
||||
migrate.migrating=Migrando a partir de <b>%s</b> ...
|
||||
migrate.migrating_failed=Migração a partir de <b>%s</b> falhou.
|
||||
migrate.migrating_failed.error=Falha ao migrar: %s
|
||||
migrate.migrating_failed_no_addr=A migração falhou.
|
||||
migrate.github.description=Migrar dados de github.com ou de outras instâncias do GitHub.
|
||||
migrate.git.description=Migrar um repositório somente de qualquer serviço Git.
|
||||
@ -1028,6 +1061,8 @@ migrate.migrating_labels=Migrando Rótulos
|
||||
migrate.migrating_releases=Migrando Versões
|
||||
migrate.migrating_issues=Migrando Issues
|
||||
migrate.migrating_pulls=Migrando Pull Requests
|
||||
migrate.cancel_migrating_title=Cancelar migração
|
||||
migrate.cancel_migrating_confirm=Você quer cancelar essa migração?
|
||||
|
||||
mirror_from=espelhamento de
|
||||
forked_from=feito fork de
|
||||
@ -1112,6 +1147,7 @@ download_file=Baixar arquivo
|
||||
normal_view=Visão normal
|
||||
line=linha
|
||||
lines=linhas
|
||||
from_comment=(comentário)
|
||||
|
||||
editor.add_file=Adicionar Arquivo
|
||||
editor.new_file=Novo arquivo
|
||||
@ -1133,6 +1169,9 @@ editor.cancel_lower=Cancelar
|
||||
editor.commit_signed_changes=Commit de alteradores assinadas
|
||||
editor.commit_changes=Aplicar commit das alterações
|
||||
editor.add_tmpl=Adicionar '<filename>'
|
||||
editor.add=Adicionar %s
|
||||
editor.update=Atualizar %s
|
||||
editor.delete=Excluir %s
|
||||
editor.patch=Aplicar Correção
|
||||
editor.patching=Corrigindo:
|
||||
editor.new_patch=Nova correção
|
||||
@ -1295,6 +1334,10 @@ issues.filter_label_exclude=`Use <code>alt</code> + <code>clique/enter</code> pa
|
||||
issues.filter_label_no_select=Todas as etiquetas
|
||||
issues.filter_label_select_no_label=Sem etiqueta
|
||||
issues.filter_milestone=Marco
|
||||
issues.filter_milestone_all=Todos os marcos
|
||||
issues.filter_milestone_none=Sem marcos
|
||||
issues.filter_milestone_open=Marcos abertos
|
||||
issues.filter_milestone_closed=Marcos fechados
|
||||
issues.filter_project=Projeto
|
||||
issues.filter_project_all=Todos os projetos
|
||||
issues.filter_project_none=Sem projeto
|
||||
@ -1353,6 +1396,7 @@ issues.context.reference_issue=Referência em uma nova issue
|
||||
issues.context.edit=Editar
|
||||
issues.context.delete=Excluir
|
||||
issues.no_content=Ainda não há conteúdo.
|
||||
issues.close=Fechar issue
|
||||
issues.close_comment_issue=Comentar e fechar
|
||||
issues.reopen_issue=Reabrir
|
||||
issues.reopen_comment_issue=Comentar e reabrir
|
||||
@ -1403,6 +1447,7 @@ issues.attachment.open_tab=`Clique para ver "%s" em uma nova aba`
|
||||
issues.attachment.download=`Clique para baixar "%s"`
|
||||
issues.subscribe=Inscrever-se
|
||||
issues.unsubscribe=Desinscrever
|
||||
issues.unpin_issue=Desfixar issue
|
||||
issues.lock=Bloquear conversação
|
||||
issues.unlock=Desbloquear conversação
|
||||
issues.lock.unknown_reason=Não pode-se bloquear uma issue com um motivo desconhecido.
|
||||
@ -1630,6 +1675,7 @@ pulls.update_branch_rebase=Atualizar branch por rebase
|
||||
pulls.update_branch_success=Atualização do branch foi bem-sucedida
|
||||
pulls.update_not_allowed=Você não tem permissão para atualizar o branch
|
||||
pulls.outdated_with_base_branch=Este branch está desatualizado com o branch base
|
||||
pulls.close=Fechar pull request
|
||||
pulls.closed_at=`fechou este pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
pulls.reopened_at=`reabriu este pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
pulls.merge_instruction_hint=`Você também pode ver as <a class="show-instruction">instruções para a linha de comandos</a>.`
|
||||
@ -1666,10 +1712,12 @@ milestones.desc=Descrição
|
||||
milestones.due_date=Data limite (opcional)
|
||||
milestones.clear=Limpar
|
||||
milestones.invalid_due_date_format=Formato da data limite deve ser 'dd/mm/aaaa'.
|
||||
milestones.create_success=O marco "%s" foi criado.
|
||||
milestones.edit=Editar marco
|
||||
milestones.edit_subheader=Marcos organizam as issues e acompanham o progresso.
|
||||
milestones.cancel=Cancelar
|
||||
milestones.modify=Atualizar marco
|
||||
milestones.edit_success=O marco "%s" foi atualizado.
|
||||
milestones.deletion=Excluir marco
|
||||
milestones.deletion_desc=A exclusão deste marco irá removê-lo de todas as issues. Tem certeza que deseja continuar?
|
||||
milestones.deletion_success=O marco foi excluído.
|
||||
@ -2100,8 +2148,11 @@ settings.dismiss_stale_approvals_desc=Quando novos commits que mudam o conteúdo
|
||||
settings.require_signed_commits=Exibir commits assinados
|
||||
settings.require_signed_commits_desc=Rejeitar pushes para este branch se não estiverem assinados ou não forem validáveis.
|
||||
settings.protect_branch_name_pattern=Padrão de Nome de Branch Protegida
|
||||
settings.protect_protected_file_patterns=Padrões de arquivos protegidos (separados usando ponto e vírgula ';'):
|
||||
settings.add_protected_branch=Habilitar proteção
|
||||
settings.delete_protected_branch=Desabilitar proteção
|
||||
settings.remove_protected_branch_success=Proteção do branch "%s" foi desabilitada.
|
||||
settings.remove_protected_branch_failed=Removendo regra de proteção de branch "%s" falhou.
|
||||
settings.protected_branch_deletion=Desabilitar proteção de branch
|
||||
settings.protected_branch_deletion_desc=Desabilitar a proteção de branch permite que os usuários com permissão de escrita realizem push. Continuar?
|
||||
settings.block_rejected_reviews=Bloquear merge em revisões rejeitadas
|
||||
@ -2224,7 +2275,9 @@ diff.review.header=Enviar revisão
|
||||
diff.review.placeholder=Comentário da revisão
|
||||
diff.review.comment=Comentar
|
||||
diff.review.approve=Aprovar
|
||||
diff.review.self_reject=Os autores do pull request não podem solicitar alterações em seus próprios pull request
|
||||
diff.review.reject=Solicitar alterações
|
||||
diff.review.self_approve=Os autores do pull request não podem aprovar seu próprio pull request
|
||||
diff.committed_by=commit de
|
||||
diff.protected=Protegido
|
||||
diff.image.side_by_side=Lado a Lado
|
||||
|
||||
186
package-lock.json
generated
186
package-lock.json
generated
@ -18,7 +18,7 @@
|
||||
"@webcomponents/custom-elements": "1.6.0",
|
||||
"add-asset-webpack-plugin": "2.0.1",
|
||||
"ansi_up": "5.2.1",
|
||||
"asciinema-player": "3.4.0",
|
||||
"asciinema-player": "3.5.0",
|
||||
"clippie": "4.0.1",
|
||||
"css-loader": "6.8.1",
|
||||
"dropzone": "6.0.0-beta.2",
|
||||
@ -28,26 +28,27 @@
|
||||
"fast-glob": "3.2.12",
|
||||
"jquery": "3.7.0",
|
||||
"jquery.are-you-sure": "1.9.0",
|
||||
"katex": "0.16.7",
|
||||
"katex": "0.16.8",
|
||||
"license-checker-webpack-plugin": "0.2.1",
|
||||
"mermaid": "10.2.3",
|
||||
"mini-css-extract-plugin": "2.7.6",
|
||||
"minimatch": "9.0.1",
|
||||
"minimatch": "9.0.2",
|
||||
"monaco-editor": "0.39.0",
|
||||
"monaco-editor-webpack-plugin": "7.0.1",
|
||||
"pdfobject": "2.2.12",
|
||||
"pretty-ms": "8.0.0",
|
||||
"sortablejs": "1.15.0",
|
||||
"swagger-ui-dist": "5.0.0",
|
||||
"swagger-ui-dist": "5.1.0",
|
||||
"throttle-debounce": "5.0.0",
|
||||
"tippy.js": "6.3.7",
|
||||
"toastify-js": "1.12.0",
|
||||
"tributejs": "5.1.3",
|
||||
"uint8-to-base64": "0.2.0",
|
||||
"vue": "3.3.4",
|
||||
"vue-bar-graph": "2.0.0",
|
||||
"vue-loader": "17.2.2",
|
||||
"vue3-calendar-heatmap": "2.0.5",
|
||||
"webpack": "5.87.0",
|
||||
"webpack": "5.88.0",
|
||||
"webpack-cli": "5.1.4",
|
||||
"wrap-ansi": "8.1.0"
|
||||
},
|
||||
@ -67,17 +68,17 @@
|
||||
"eslint-plugin-regexp": "1.15.0",
|
||||
"eslint-plugin-sonarjs": "0.19.0",
|
||||
"eslint-plugin-unicorn": "47.0.0",
|
||||
"eslint-plugin-vue": "9.14.1",
|
||||
"eslint-plugin-vue": "9.15.1",
|
||||
"eslint-plugin-wc": "1.5.0",
|
||||
"jsdom": "22.1.0",
|
||||
"markdownlint-cli": "0.35.0",
|
||||
"postcss-html": "1.5.0",
|
||||
"stylelint": "15.8.0",
|
||||
"stylelint": "15.9.0",
|
||||
"stylelint-declaration-block-no-ignored-properties": "2.7.0",
|
||||
"stylelint-declaration-strict-value": "1.9.2",
|
||||
"stylelint-stylistic": "0.4.2",
|
||||
"svgo": "3.0.2",
|
||||
"updates": "14.2.4",
|
||||
"updates": "14.2.8",
|
||||
"vitest": "0.32.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -402,9 +403,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/media-query-list-parser": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.0.tgz",
|
||||
"integrity": "sha512-MXkR+TeaS2q9IkpyO6jVCdtA/bfpABJxIrfkLswThFN8EZZgI2RfAHhm6sDNDuYV25d5+b8Lj1fpTccIcSLPsQ==",
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.1.tgz",
|
||||
"integrity": "sha512-pUjtFbaKbiFNjJo8pprrIaXLvQvWIlwPiFnRI4sEnc4F0NIGTOsw8kaJSR3CmZAKEvV8QYckovgAnWQC0bgLLQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -420,7 +421,7 @@
|
||||
"node": "^14 || ^16 || >=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@csstools/css-parser-algorithms": "^2.1.1",
|
||||
"@csstools/css-parser-algorithms": "^2.2.0",
|
||||
"@csstools/css-tokenizer": "^2.1.1"
|
||||
}
|
||||
},
|
||||
@ -1858,9 +1859,9 @@
|
||||
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA=="
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz",
|
||||
"integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg=="
|
||||
"version": "20.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.2.tgz",
|
||||
"integrity": "sha512-vOBLVQeCQfIcF/2Y7eKFTqrMnizK5lRNQ7ykML/5RuwVXVWxYkgwS7xbt4B6fKCUPgbSL5FSsjHQpaGQP/dQmw=="
|
||||
},
|
||||
"node_modules/@types/normalize-package-data": {
|
||||
"version": "2.4.1",
|
||||
@ -2604,9 +2605,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/asciinema-player": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asciinema-player/-/asciinema-player-3.4.0.tgz",
|
||||
"integrity": "sha512-dX6jt5S3K6daItsVWzyY9mRDK+ivC2QgqCxFkdSiNslo0vY/ZqA4upcTzqIKZqBtxppovOZk44ltg9VnHG9QVg==",
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/asciinema-player/-/asciinema-player-3.5.0.tgz",
|
||||
"integrity": "sha512-o4B2AscBuCZo4+JB9TBGrfZ7GQL99wsbm08WwmuNJTPd1lyLQJq8wgacnBsdvb2sC0K875ScYr8T5XmfeH/6dg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.21.0",
|
||||
"solid-js": "^1.3.0"
|
||||
@ -2876,9 +2877,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001504",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001504.tgz",
|
||||
"integrity": "sha512-5uo7eoOp2mKbWyfMXnGO9rJWOGU8duvzEiYITW+wivukL7yHH4gX9yuRaobu6El4jPxo6jKZfG+N6fB621GD/Q==",
|
||||
"version": "1.0.30001508",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001508.tgz",
|
||||
"integrity": "sha512-sdQZOJdmt3GJs1UMNpCCCyeuS2IEGLXnHyAo9yIO5JJDjbjoVRij4M1qep6P6gFpptD1PqIYgzM+gwJbOi92mw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@ -4161,9 +4162,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.433",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.433.tgz",
|
||||
"integrity": "sha512-MGO1k0w1RgrfdbLVwmXcDhHHuxCn2qRgR7dYsJvWFKDttvYPx6FNzCGG0c/fBBvzK2LDh3UV7Tt9awnHnvAAUQ=="
|
||||
"version": "1.4.441",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.441.tgz",
|
||||
"integrity": "sha512-LlCgQ8zgYZPymf5H4aE9itwiIWH4YlCiv1HFLmmcBeFYi5E+3eaIFnjHzYtcFQbaKfAW+CqZ9pgxo33DZuoqPg=="
|
||||
},
|
||||
"node_modules/elkjs": {
|
||||
"version": "0.8.2",
|
||||
@ -4204,10 +4205,22 @@
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
|
||||
"integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/envinfo": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz",
|
||||
"integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==",
|
||||
"version": "7.10.0",
|
||||
"resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.10.0.tgz",
|
||||
"integrity": "sha512-ZtUjZO6l5mwTHvc1L9+1q5p/R3wTopcfqMW8r5t8SJSKqeVI/LtajORwRFEKpEFuekjD0VBjwu1HMxL4UalIRw==",
|
||||
"bin": {
|
||||
"envinfo": "dist/cli.js"
|
||||
},
|
||||
@ -4806,9 +4819,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-vue": {
|
||||
"version": "9.14.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.14.1.tgz",
|
||||
"integrity": "sha512-LQazDB1qkNEKejLe/b5a9VfEbtbczcOaui5lQ4Qw0tbRBbQYREyxxOV5BQgNDTqGPs9pxqiEpbMi9ywuIaF7vw==",
|
||||
"version": "9.15.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.15.1.tgz",
|
||||
"integrity": "sha512-CJE/oZOslvmAR9hf8SClTdQ9JLweghT6JCBQNrT2Iel1uVw0W0OLJxzvPd6CxmABKCvLrtyDnqGV37O7KQv6+A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.3.0",
|
||||
@ -5364,9 +5377,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/get-tsconfig": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.6.0.tgz",
|
||||
"integrity": "sha512-lgbo68hHTQnFddybKbbs/RDRJnJT5YyGy2kQzVwbq+g67X73i+5MVTval34QxGkOe9X5Ujf1UYpCaphLyltjEg==",
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.6.2.tgz",
|
||||
"integrity": "sha512-E5XrT4CbbXcXWy+1jChlZmrmCwd5KGx502kDCXJJ7y898TtWW9FwoG5HfOLVRKmlmDGkWN2HM9Ho+/Y8F0sJDg==",
|
||||
"dependencies": {
|
||||
"resolve-pkg-maps": "^1.0.0"
|
||||
},
|
||||
@ -6576,9 +6589,9 @@
|
||||
"integrity": "sha512-b+z6yF1d4EOyDgylzQo5IminlUmzSeqR1hs/bzjBNjuGras4FXq/6TrzjxfN0j+TmI0ltJzTNlqXUMCniciwKQ=="
|
||||
},
|
||||
"node_modules/katex": {
|
||||
"version": "0.16.7",
|
||||
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.7.tgz",
|
||||
"integrity": "sha512-Xk9C6oGKRwJTfqfIbtr0Kes9OSv6IFsuhFGc7tW4urlpMJtuh+7YhzU6YEG9n8gmWKcMAFzkp7nr+r69kV0zrA==",
|
||||
"version": "0.16.8",
|
||||
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.8.tgz",
|
||||
"integrity": "sha512-ftuDnJbcbOckGY11OO+zg3OofESlbR5DRl2cmN8HeWeeFIV7wTXvAOx8kEjZjobhA+9wh2fbKeO6cdcA9Mnovg==",
|
||||
"funding": [
|
||||
"https://opencollective.com/katex",
|
||||
"https://github.com/sponsors/katex"
|
||||
@ -6881,18 +6894,6 @@
|
||||
"markdown-it": "bin/markdown-it.js"
|
||||
}
|
||||
},
|
||||
"node_modules/markdown-it/node_modules/entities": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
|
||||
"integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/markdownlint": {
|
||||
"version": "0.29.0",
|
||||
"resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.29.0.tgz",
|
||||
@ -7655,9 +7656,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz",
|
||||
"integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==",
|
||||
"version": "9.0.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.2.tgz",
|
||||
"integrity": "sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg==",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
@ -7701,13 +7702,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mlly": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/mlly/-/mlly-1.3.0.tgz",
|
||||
"integrity": "sha512-HT5mcgIQKkOrZecOjOX3DJorTikWXwsBfpcr/MGBkhfWcjiqvnaL/9ppxvIUXfjT6xt4DVIAsN9fMUz1ev4bIw==",
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.0.tgz",
|
||||
"integrity": "sha512-ua8PAThnTwpprIaU47EPeZ/bPUVp2QYBbWMphUQpVdBI3Lgqzm5KZQ45Agm3YJedHXaIHl6pBGabaLSUPPSptg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"acorn": "^8.8.2",
|
||||
"pathe": "^1.1.0",
|
||||
"acorn": "^8.9.0",
|
||||
"pathe": "^1.1.1",
|
||||
"pkg-types": "^1.0.3",
|
||||
"ufo": "^1.1.2"
|
||||
}
|
||||
@ -9062,14 +9063,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/run-con": {
|
||||
"version": "1.2.11",
|
||||
"resolved": "https://registry.npmjs.org/run-con/-/run-con-1.2.11.tgz",
|
||||
"integrity": "sha512-NEMGsUT+cglWkzEr4IFK21P4Jca45HqiAbIIZIBdX5+UZTB24Mb/21iNGgz9xZa8tL6vbW7CXmq7MFN42+VjNQ==",
|
||||
"version": "1.2.12",
|
||||
"resolved": "https://registry.npmjs.org/run-con/-/run-con-1.2.12.tgz",
|
||||
"integrity": "sha512-5257ILMYIF4RztL9uoZ7V9Q97zHtNHn5bN3NobeAnzB1P3ASLgg8qocM2u+R18ttp+VEM78N2LK8XcNVtnSRrg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"deep-extend": "^0.6.0",
|
||||
"ini": "~3.0.0",
|
||||
"minimist": "^1.2.6",
|
||||
"minimist": "^1.2.8",
|
||||
"strip-json-comments": "~3.1.1"
|
||||
},
|
||||
"bin": {
|
||||
@ -9209,9 +9210,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.5.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz",
|
||||
"integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==",
|
||||
"version": "7.5.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz",
|
||||
"integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==",
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
@ -9414,9 +9415,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/solid-js": {
|
||||
"version": "1.7.6",
|
||||
"resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.7.6.tgz",
|
||||
"integrity": "sha512-DXVOTjUh/bIAhE0fIqu3ezGLyQaez7v8EOw3uPLIi87DmLjg+hsuCAgKyNIZ+o4jUetOk3ZORccvJmE1yZUk8g==",
|
||||
"version": "1.7.7",
|
||||
"resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.7.7.tgz",
|
||||
"integrity": "sha512-SPdYVke/Z6Za24PBTbULyQYPrhGO1ZbPany76atO2zF2dmYn2pCotbsw1JtlgWnr9dK2JbwPGnA3ODTGPLhZNw==",
|
||||
"dependencies": {
|
||||
"csstype": "^3.1.0",
|
||||
"seroval": "^0.5.0"
|
||||
@ -9712,9 +9713,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/stylelint": {
|
||||
"version": "15.8.0",
|
||||
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.8.0.tgz",
|
||||
"integrity": "sha512-x9qBk84F3MEjMEUNCE7MtWmfj9G9y5XzJ0cpQeJdy2l/IoqjC8Ih0N0ytmOTnXE4Yv0J7I1cmVRQUVNSPCxTsA==",
|
||||
"version": "15.9.0",
|
||||
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.9.0.tgz",
|
||||
"integrity": "sha512-sXtAZi64CllWr6A+8ymDWnlIaYwuAa7XRmGnJxLQXFNnLjd3Izm4HAD+loKVaZ7cpK6SLxhAUX1lwPJKGCn0mg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@csstools/css-parser-algorithms": "^2.2.0",
|
||||
@ -9826,9 +9827,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/stylis": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
|
||||
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.0.tgz",
|
||||
"integrity": "sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ=="
|
||||
},
|
||||
"node_modules/superstruct": {
|
||||
"version": "0.10.13",
|
||||
@ -9910,9 +9911,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/swagger-ui-dist": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.0.0.tgz",
|
||||
"integrity": "sha512-bwl6og9I9CAHKGSnYLKydjhBuH7d3oU6RX6uKN8oDCkLusTHXOW3sZMyBWjRtjGFnCMmN085oZoaR/4Wm9nIaQ=="
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.1.0.tgz",
|
||||
"integrity": "sha512-c1KmAjuVODxw+vwkNLALQZrgdlBAuBbr2xSPfYrJgseEi7gFKcTvShysPmyuDI4kcUa1+5rFpjWvXdusKY74mg=="
|
||||
},
|
||||
"node_modules/symbol-tree": {
|
||||
"version": "3.2.4",
|
||||
@ -9957,9 +9958,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.18.0.tgz",
|
||||
"integrity": "sha512-pdL757Ig5a0I+owA42l6tIuEycRuM7FPY4n62h44mRLRfnOxJkkOHd6i89dOpwZlpF6JXBwaAHF6yWzFrt+QyA==",
|
||||
"version": "5.18.2",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.18.2.tgz",
|
||||
"integrity": "sha512-Ah19JS86ypbJzTzvUCX7KOsEIhDaRONungA4aYBjEP3JZRf4ocuDzTg4QWZnPn9DEMiMYGJPiSOy7aykoCc70w==",
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
"acorn": "^8.8.2",
|
||||
@ -10122,6 +10123,11 @@
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/toastify-js": {
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/toastify-js/-/toastify-js-1.12.0.tgz",
|
||||
"integrity": "sha512-HeMHCO9yLPvP9k0apGSdPUWrUbLnxUKNFzgUoZp1PHCLploIX/4DSQ7V8H25ef+h4iO9n0he7ImfcndnN6nDrQ=="
|
||||
},
|
||||
"node_modules/toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
@ -10214,9 +10220,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.5.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz",
|
||||
"integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==",
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz",
|
||||
"integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
@ -10363,9 +10369,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/updates": {
|
||||
"version": "14.2.4",
|
||||
"resolved": "https://registry.npmjs.org/updates/-/updates-14.2.4.tgz",
|
||||
"integrity": "sha512-r54h4Q12lUAmQ9dENy7BnY22AnTfW4YGEZw73gv6RvNEWgcZ3qS88jPLc1ckPAzt/8TPKWwLkSVpbEpgGwglJw==",
|
||||
"version": "14.2.8",
|
||||
"resolved": "https://registry.npmjs.org/updates/-/updates-14.2.8.tgz",
|
||||
"integrity": "sha512-Ca+M1vKKBBRiQSi3yrN8OdncmP9osIf1oJM/HpEIHeDvyGLs/noTi9X2LS4zl50VXRTSCqssF5CZN0XWzSPigg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"updates": "bin/updates.js"
|
||||
@ -10528,9 +10534,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/rollup": {
|
||||
"version": "3.25.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.25.1.tgz",
|
||||
"integrity": "sha512-tywOR+rwIt5m2ZAWSe5AIJcTat8vGlnPFAv15ycCrw33t6iFsXZ6mzHVFh2psSjxQPmI+xgzMZZizUAukBI4aQ==",
|
||||
"version": "3.25.3",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.25.3.tgz",
|
||||
"integrity": "sha512-ZT279hx8gszBj9uy5FfhoG4bZx8c+0A1sbqtr7Q3KNWIizpTdDEPZbV2xcbvHsnFp4MavCQYZyzApJ+virB8Yw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
@ -10766,9 +10772,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.87.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.87.0.tgz",
|
||||
"integrity": "sha512-GOu1tNbQ7p1bDEoFRs2YPcfyGs8xq52yyPBZ3m2VGnXGtV9MxjrkABHm4V9Ia280OefsSLzvbVoXcfLxjKY/Iw==",
|
||||
"version": "5.88.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.0.tgz",
|
||||
"integrity": "sha512-O3jDhG5e44qIBSi/P6KpcCcH7HD+nYIHVBhdWFxcLOcIGN8zGo5nqF3BjyNCxIh4p1vFdNnreZv2h2KkoAw3lw==",
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
"@types/estree": "^1.0.0",
|
||||
|
||||
17
package.json
17
package.json
@ -17,7 +17,7 @@
|
||||
"@webcomponents/custom-elements": "1.6.0",
|
||||
"add-asset-webpack-plugin": "2.0.1",
|
||||
"ansi_up": "5.2.1",
|
||||
"asciinema-player": "3.4.0",
|
||||
"asciinema-player": "3.5.0",
|
||||
"clippie": "4.0.1",
|
||||
"css-loader": "6.8.1",
|
||||
"dropzone": "6.0.0-beta.2",
|
||||
@ -27,26 +27,27 @@
|
||||
"fast-glob": "3.2.12",
|
||||
"jquery": "3.7.0",
|
||||
"jquery.are-you-sure": "1.9.0",
|
||||
"katex": "0.16.7",
|
||||
"katex": "0.16.8",
|
||||
"license-checker-webpack-plugin": "0.2.1",
|
||||
"mermaid": "10.2.3",
|
||||
"mini-css-extract-plugin": "2.7.6",
|
||||
"minimatch": "9.0.1",
|
||||
"minimatch": "9.0.2",
|
||||
"monaco-editor": "0.39.0",
|
||||
"monaco-editor-webpack-plugin": "7.0.1",
|
||||
"pdfobject": "2.2.12",
|
||||
"pretty-ms": "8.0.0",
|
||||
"sortablejs": "1.15.0",
|
||||
"swagger-ui-dist": "5.0.0",
|
||||
"swagger-ui-dist": "5.1.0",
|
||||
"throttle-debounce": "5.0.0",
|
||||
"tippy.js": "6.3.7",
|
||||
"toastify-js": "1.12.0",
|
||||
"tributejs": "5.1.3",
|
||||
"uint8-to-base64": "0.2.0",
|
||||
"vue": "3.3.4",
|
||||
"vue-bar-graph": "2.0.0",
|
||||
"vue-loader": "17.2.2",
|
||||
"vue3-calendar-heatmap": "2.0.5",
|
||||
"webpack": "5.87.0",
|
||||
"webpack": "5.88.0",
|
||||
"webpack-cli": "5.1.4",
|
||||
"wrap-ansi": "8.1.0"
|
||||
},
|
||||
@ -66,17 +67,17 @@
|
||||
"eslint-plugin-regexp": "1.15.0",
|
||||
"eslint-plugin-sonarjs": "0.19.0",
|
||||
"eslint-plugin-unicorn": "47.0.0",
|
||||
"eslint-plugin-vue": "9.14.1",
|
||||
"eslint-plugin-vue": "9.15.1",
|
||||
"eslint-plugin-wc": "1.5.0",
|
||||
"jsdom": "22.1.0",
|
||||
"markdownlint-cli": "0.35.0",
|
||||
"postcss-html": "1.5.0",
|
||||
"stylelint": "15.8.0",
|
||||
"stylelint": "15.9.0",
|
||||
"stylelint-declaration-block-no-ignored-properties": "2.7.0",
|
||||
"stylelint-declaration-strict-value": "1.9.2",
|
||||
"stylelint-stylistic": "0.4.2",
|
||||
"svgo": "3.0.2",
|
||||
"updates": "14.2.4",
|
||||
"updates": "14.2.8",
|
||||
"vitest": "0.32.2"
|
||||
},
|
||||
"browserslist": [
|
||||
|
||||
8
poetry.lock
generated
8
poetry.lock
generated
@ -42,13 +42,13 @@ six = ">=1.13.0"
|
||||
|
||||
[[package]]
|
||||
name = "djlint"
|
||||
version = "1.31.0"
|
||||
version = "1.31.1"
|
||||
description = "HTML Template Linter and Formatter"
|
||||
optional = false
|
||||
python-versions = ">=3.8.0,<4.0.0"
|
||||
files = [
|
||||
{file = "djlint-1.31.0-py3-none-any.whl", hash = "sha256:2b9200c67103b79835b7547ff732e910888d1f0ef684f5b329eb64b14d09c046"},
|
||||
{file = "djlint-1.31.0.tar.gz", hash = "sha256:8acb4b751b429c5aabb1aef5b6007bdf53224eceda25c5fbe04c42cc57c0a7ba"},
|
||||
{file = "djlint-1.31.1-py3-none-any.whl", hash = "sha256:9b2e2fc3a059a8e5a62f309edea15c1aeee331a279ab2699b9fb51a31d8c0934"},
|
||||
{file = "djlint-1.31.1.tar.gz", hash = "sha256:a11739e2f919f760b3986eb13d06e00171f3bd342b8d88e9bd914a4260eaa8ce"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -328,4 +328,4 @@ telegram = ["requests"]
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.8"
|
||||
content-hash = "22c4af11eadd8784b613951d6160d67be0f33500238a450741c3d75beb218dad"
|
||||
content-hash = "f03ad8e7c4f6e797ac3c04630db8cc16438cd59642653c26fd401633cd62d696"
|
||||
|
||||
@ -8,7 +8,7 @@ authors = []
|
||||
python = "^3.8"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
djlint = "1.31.0"
|
||||
djlint = "1.31.1"
|
||||
|
||||
[tool.djlint]
|
||||
profile="golang"
|
||||
|
||||
@ -777,11 +777,11 @@ func Routes() *web.Route {
|
||||
m.Group("/notifications", func() {
|
||||
m.Combo("").
|
||||
Get(notify.ListNotifications).
|
||||
Put(notify.ReadNotifications, reqToken())
|
||||
Put(reqToken(), notify.ReadNotifications)
|
||||
m.Get("/new", notify.NewAvailable)
|
||||
m.Combo("/threads/{id}").
|
||||
Get(notify.GetThread).
|
||||
Patch(notify.ReadThread, reqToken())
|
||||
Patch(reqToken(), notify.ReadThread)
|
||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryNotification))
|
||||
|
||||
// Users (requires user scope)
|
||||
@ -899,6 +899,11 @@ func Routes() *web.Route {
|
||||
Patch(bind(api.EditHookOption{}), user.EditHook).
|
||||
Delete(user.DeleteHook)
|
||||
}, reqWebhooksEnabled())
|
||||
|
||||
m.Group("/avatar", func() {
|
||||
m.Post("", bind(api.UpdateUserAvatarOption{}), user.UpdateAvatar)
|
||||
m.Delete("", user.DeleteAvatar)
|
||||
}, reqToken())
|
||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken())
|
||||
|
||||
// Repositories (requires repo scope, org scope)
|
||||
@ -1134,6 +1139,10 @@ func Routes() *web.Route {
|
||||
m.Get("/languages", reqRepoReader(unit.TypeCode), repo.GetLanguages)
|
||||
m.Get("/activities/feeds", repo.ListRepoActivityFeeds)
|
||||
m.Get("/new_pin_allowed", repo.AreNewIssuePinsAllowed)
|
||||
m.Group("/avatar", func() {
|
||||
m.Post("", bind(api.UpdateRepoAvatarOption{}), repo.UpdateAvatar)
|
||||
m.Delete("", repo.DeleteAvatar)
|
||||
}, reqAdmin(), reqToken())
|
||||
}, repoAssignment())
|
||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository))
|
||||
|
||||
@ -1314,6 +1323,10 @@ func Routes() *web.Route {
|
||||
Patch(bind(api.EditHookOption{}), org.EditHook).
|
||||
Delete(org.DeleteHook)
|
||||
}, reqToken(), reqOrgOwnership(), reqWebhooksEnabled())
|
||||
m.Group("/avatar", func() {
|
||||
m.Post("", bind(api.UpdateUserAvatarOption{}), org.UpdateAvatar)
|
||||
m.Delete("", org.DeleteAvatar)
|
||||
}, reqToken(), reqOrgOwnership())
|
||||
m.Get("/activities/feeds", org.ListOrgActivityFeeds)
|
||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true))
|
||||
m.Group("/teams/{teamid}", func() {
|
||||
|
||||
@ -183,7 +183,7 @@ func ReadRepoNotifications(ctx *context.APIContext) {
|
||||
if len(qLastRead) > 0 {
|
||||
tmpLastRead, err := time.Parse(time.RFC3339, qLastRead)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
ctx.Error(http.StatusBadRequest, "Parse", err)
|
||||
return
|
||||
}
|
||||
if !tmpLastRead.IsZero() {
|
||||
|
||||
@ -132,7 +132,7 @@ func ReadNotifications(ctx *context.APIContext) {
|
||||
if len(qLastRead) > 0 {
|
||||
tmpLastRead, err := time.Parse(time.RFC3339, qLastRead)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
ctx.Error(http.StatusBadRequest, "Parse", err)
|
||||
return
|
||||
}
|
||||
if !tmpLastRead.IsZero() {
|
||||
|
||||
74
routers/api/v1/org/avatar.go
Normal file
74
routers/api/v1/org/avatar.go
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package org
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
user_service "code.gitea.io/gitea/services/user"
|
||||
)
|
||||
|
||||
// UpdateAvatarupdates the Avatar of an Organisation
|
||||
func UpdateAvatar(ctx *context.APIContext) {
|
||||
// swagger:operation POST /orgs/{org}/avatar organization orgUpdateAvatar
|
||||
// ---
|
||||
// summary: Update Avatar
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: org
|
||||
// in: path
|
||||
// description: name of the organization
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/UpdateUserAvatarOption"
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
form := web.GetForm(ctx).(*api.UpdateUserAvatarOption)
|
||||
|
||||
content, err := base64.StdEncoding.DecodeString(form.Image)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusBadRequest, "DecodeImage", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = user_service.UploadAvatar(ctx.Org.Organization.AsUser(), content)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "UploadAvatar", err)
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// DeleteAvatar deletes the Avatar of an Organisation
|
||||
func DeleteAvatar(ctx *context.APIContext) {
|
||||
// swagger:operation DELETE /orgs/{org}/avatar organization orgDeleteAvatar
|
||||
// ---
|
||||
// summary: Delete Avatar
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: org
|
||||
// in: path
|
||||
// description: name of the organization
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
err := user_service.DeleteAvatar(ctx.Org.Organization.AsUser())
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "DeleteAvatar", err)
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
84
routers/api/v1/repo/avatar.go
Normal file
84
routers/api/v1/repo/avatar.go
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package repo
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
)
|
||||
|
||||
// UpdateVatar updates the Avatar of an Repo
|
||||
func UpdateAvatar(ctx *context.APIContext) {
|
||||
// swagger:operation POST /repos/{owner}/{repo}/avatar repository repoUpdateAvatar
|
||||
// ---
|
||||
// summary: Update avatar
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/UpdateRepoAvatarOption"
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
form := web.GetForm(ctx).(*api.UpdateRepoAvatarOption)
|
||||
|
||||
content, err := base64.StdEncoding.DecodeString(form.Image)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusBadRequest, "DecodeImage", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = repo_service.UploadAvatar(ctx, ctx.Repo.Repository, content)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "UploadAvatar", err)
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// UpdateAvatar deletes the Avatar of an Repo
|
||||
func DeleteAvatar(ctx *context.APIContext) {
|
||||
// swagger:operation DELETE /repos/{owner}/{repo}/avatar repository repoDeleteAvatar
|
||||
// ---
|
||||
// summary: Delete avatar
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
err := repo_service.DeleteAvatar(ctx, ctx.Repo.Repository)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "DeleteAvatar", err)
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
@ -15,7 +15,9 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||
"code.gitea.io/gitea/services/convert"
|
||||
@ -76,7 +78,7 @@ func GetBranch(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
br, err := convert.ToBranch(ctx, ctx.Repo.Repository, branch, c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
|
||||
br, err := convert.ToBranch(ctx, ctx.Repo.Repository, branch.Name, c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
|
||||
return
|
||||
@ -118,6 +120,37 @@ func DeleteBranch(ctx *context.APIContext) {
|
||||
|
||||
branchName := ctx.Params("*")
|
||||
|
||||
if ctx.Repo.Repository.IsEmpty {
|
||||
ctx.Error(http.StatusForbidden, "", "Git Repository is empty.")
|
||||
return
|
||||
}
|
||||
|
||||
// check whether branches of this repository has been synced
|
||||
totalNumOfBranches, err := git_model.CountBranches(ctx, git_model.FindBranchOptions{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
IsDeletedBranch: util.OptionalBoolFalse,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "CountBranches", err)
|
||||
return
|
||||
}
|
||||
if totalNumOfBranches == 0 { // sync branches immediately because non-empty repository should have at least 1 branch
|
||||
_, err = repo_module.SyncRepoBranches(ctx, ctx.Repo.Repository.ID, 0)
|
||||
if err != nil {
|
||||
ctx.ServerError("SyncRepoBranches", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.Repo.Repository.IsArchived {
|
||||
ctx.Error(http.StatusForbidden, "IsArchived", fmt.Errorf("can not delete branch of an archived repository"))
|
||||
return
|
||||
}
|
||||
if ctx.Repo.Repository.IsMirror {
|
||||
ctx.Error(http.StatusForbidden, "IsMirrored", fmt.Errorf("can not delete branch of an mirror repository"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := repo_service.DeleteBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil {
|
||||
switch {
|
||||
case git.IsErrBranchNotExist(err):
|
||||
@ -203,14 +236,14 @@ func CreateBranch(ctx *context.APIContext) {
|
||||
|
||||
err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, oldCommit.ID.String(), opt.BranchName)
|
||||
if err != nil {
|
||||
if models.IsErrBranchDoesNotExist(err) {
|
||||
if git_model.IsErrBranchNotExist(err) {
|
||||
ctx.Error(http.StatusNotFound, "", "The old branch does not exist")
|
||||
}
|
||||
if models.IsErrTagAlreadyExists(err) {
|
||||
ctx.Error(http.StatusConflict, "", "The branch with the same tag already exists.")
|
||||
} else if models.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
|
||||
} else if git_model.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
|
||||
ctx.Error(http.StatusConflict, "", "The branch already exists.")
|
||||
} else if models.IsErrBranchNameConflict(err) {
|
||||
} else if git_model.IsErrBranchNameConflict(err) {
|
||||
ctx.Error(http.StatusConflict, "", "The branch with the same name already exists.")
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "CreateNewBranchFromCommit", err)
|
||||
@ -236,7 +269,7 @@ func CreateBranch(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
br, err := convert.ToBranch(ctx, ctx.Repo.Repository, branch, commit, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
|
||||
br, err := convert.ToBranch(ctx, ctx.Repo.Repository, branch.Name, commit, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
|
||||
return
|
||||
@ -275,20 +308,38 @@ func ListBranches(ctx *context.APIContext) {
|
||||
// "200":
|
||||
// "$ref": "#/responses/BranchList"
|
||||
|
||||
var totalNumOfBranches int
|
||||
var totalNumOfBranches int64
|
||||
var apiBranches []*api.Branch
|
||||
|
||||
listOptions := utils.GetListOptions(ctx)
|
||||
|
||||
if !ctx.Repo.Repository.IsEmpty && ctx.Repo.GitRepo != nil {
|
||||
branchOpts := git_model.FindBranchOptions{
|
||||
ListOptions: listOptions,
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
IsDeletedBranch: util.OptionalBoolFalse,
|
||||
}
|
||||
var err error
|
||||
totalNumOfBranches, err = git_model.CountBranches(ctx, branchOpts)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "CountBranches", err)
|
||||
return
|
||||
}
|
||||
if totalNumOfBranches == 0 { // sync branches immediately because non-empty repository should have at least 1 branch
|
||||
totalNumOfBranches, err = repo_module.SyncRepoBranches(ctx, ctx.Repo.Repository.ID, 0)
|
||||
if err != nil {
|
||||
ctx.ServerError("SyncRepoBranches", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
rules, err := git_model.FindRepoProtectedBranchRules(ctx, ctx.Repo.Repository.ID)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "FindMatchedProtectedBranchRules", err)
|
||||
return
|
||||
}
|
||||
|
||||
skip, _ := listOptions.GetStartEnd()
|
||||
branches, total, err := ctx.Repo.GitRepo.GetBranches(skip, listOptions.PageSize)
|
||||
branches, err := git_model.FindBranches(ctx, branchOpts)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetBranches", err)
|
||||
return
|
||||
@ -296,11 +347,11 @@ func ListBranches(ctx *context.APIContext) {
|
||||
|
||||
apiBranches = make([]*api.Branch, 0, len(branches))
|
||||
for i := range branches {
|
||||
c, err := branches[i].GetCommit()
|
||||
c, err := ctx.Repo.GitRepo.GetBranchCommit(branches[i].Name)
|
||||
if err != nil {
|
||||
// Skip if this branch doesn't exist anymore.
|
||||
if git.IsErrNotExist(err) {
|
||||
total--
|
||||
totalNumOfBranches--
|
||||
continue
|
||||
}
|
||||
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
|
||||
@ -308,19 +359,17 @@ func ListBranches(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
branchProtection := rules.GetFirstMatched(branches[i].Name)
|
||||
apiBranch, err := convert.ToBranch(ctx, ctx.Repo.Repository, branches[i], c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
|
||||
apiBranch, err := convert.ToBranch(ctx, ctx.Repo.Repository, branches[i].Name, c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
|
||||
return
|
||||
}
|
||||
apiBranches = append(apiBranches, apiBranch)
|
||||
}
|
||||
|
||||
totalNumOfBranches = total
|
||||
}
|
||||
|
||||
ctx.SetLinkHeader(totalNumOfBranches, listOptions.PageSize)
|
||||
ctx.SetTotalCountHeader(int64(totalNumOfBranches))
|
||||
ctx.SetLinkHeader(int(totalNumOfBranches), listOptions.PageSize)
|
||||
ctx.SetTotalCountHeader(totalNumOfBranches)
|
||||
ctx.JSON(http.StatusOK, apiBranches)
|
||||
}
|
||||
|
||||
@ -580,7 +629,7 @@ func CreateBranchProtection(ctx *context.APIContext) {
|
||||
}()
|
||||
}
|
||||
// FIXME: since we only need to recheck files protected rules, we could improve this
|
||||
matchedBranches, err := git_model.FindAllMatchedBranches(ctx, ctx.Repo.GitRepo, ruleName)
|
||||
matchedBranches, err := git_model.FindAllMatchedBranches(ctx, ctx.Repo.Repository.ID, ruleName)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "FindAllMatchedBranches", err)
|
||||
return
|
||||
@ -851,7 +900,7 @@ func EditBranchProtection(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
// FIXME: since we only need to recheck files protected rules, we could improve this
|
||||
matchedBranches, err := git_model.FindAllMatchedBranches(ctx, ctx.Repo.GitRepo, protectBranch.RuleName)
|
||||
matchedBranches, err := git_model.FindAllMatchedBranches(ctx, ctx.Repo.Repository.ID, protectBranch.RuleName)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "FindAllMatchedBranches", err)
|
||||
return
|
||||
|
||||
@ -687,12 +687,12 @@ func handleCreateOrUpdateFileError(ctx *context.APIContext, err error) {
|
||||
ctx.Error(http.StatusForbidden, "Access", err)
|
||||
return
|
||||
}
|
||||
if models.IsErrBranchAlreadyExists(err) || models.IsErrFilenameInvalid(err) || models.IsErrSHADoesNotMatch(err) ||
|
||||
if git_model.IsErrBranchAlreadyExists(err) || models.IsErrFilenameInvalid(err) || models.IsErrSHADoesNotMatch(err) ||
|
||||
models.IsErrFilePathInvalid(err) || models.IsErrRepoFileAlreadyExists(err) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "Invalid", err)
|
||||
return
|
||||
}
|
||||
if models.IsErrBranchDoesNotExist(err) || git.IsErrBranchNotExist(err) {
|
||||
if git_model.IsErrBranchNotExist(err) || git.IsErrBranchNotExist(err) {
|
||||
ctx.Error(http.StatusNotFound, "BranchDoesNotExist", err)
|
||||
return
|
||||
}
|
||||
@ -843,7 +843,7 @@ func DeleteFile(ctx *context.APIContext) {
|
||||
if git.IsErrBranchNotExist(err) || models.IsErrRepoFileDoesNotExist(err) || git.IsErrNotExist(err) {
|
||||
ctx.Error(http.StatusNotFound, "DeleteFile", err)
|
||||
return
|
||||
} else if models.IsErrBranchAlreadyExists(err) ||
|
||||
} else if git_model.IsErrBranchAlreadyExists(err) ||
|
||||
models.IsErrFilenameInvalid(err) ||
|
||||
models.IsErrSHADoesNotMatch(err) ||
|
||||
models.IsErrCommitIDDoesNotMatch(err) ||
|
||||
|
||||
@ -258,7 +258,7 @@ func AddPushMirror(ctx *context.APIContext) {
|
||||
// schema:
|
||||
// "$ref": "#/definitions/CreatePushMirrorOption"
|
||||
// responses:
|
||||
// "201":
|
||||
// "200":
|
||||
// "$ref": "#/responses/PushMirror"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
@ -91,12 +92,12 @@ func ApplyDiffPatch(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusForbidden, "Access", err)
|
||||
return
|
||||
}
|
||||
if models.IsErrBranchAlreadyExists(err) || models.IsErrFilenameInvalid(err) || models.IsErrSHADoesNotMatch(err) ||
|
||||
if git_model.IsErrBranchAlreadyExists(err) || models.IsErrFilenameInvalid(err) || models.IsErrSHADoesNotMatch(err) ||
|
||||
models.IsErrFilePathInvalid(err) || models.IsErrRepoFileAlreadyExists(err) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "Invalid", err)
|
||||
return
|
||||
}
|
||||
if models.IsErrBranchDoesNotExist(err) || git.IsErrBranchNotExist(err) {
|
||||
if git_model.IsErrBranchNotExist(err) || git.IsErrBranchNotExist(err) {
|
||||
ctx.Error(http.StatusNotFound, "BranchDoesNotExist", err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -181,4 +181,10 @@ type swaggerParameterBodies struct {
|
||||
|
||||
// in:body
|
||||
CreatePushMirrorOption api.CreatePushMirrorOption
|
||||
|
||||
// in:body
|
||||
UpdateUserAvatarOptions api.UpdateUserAvatarOption
|
||||
|
||||
// in:body
|
||||
UpdateRepoAvatarOptions api.UpdateRepoAvatarOption
|
||||
}
|
||||
|
||||
63
routers/api/v1/user/avatar.go
Normal file
63
routers/api/v1/user/avatar.go
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
user_service "code.gitea.io/gitea/services/user"
|
||||
)
|
||||
|
||||
// UpdateAvatar updates the Avatar of an User
|
||||
func UpdateAvatar(ctx *context.APIContext) {
|
||||
// swagger:operation POST /user/avatar user userUpdateAvatar
|
||||
// ---
|
||||
// summary: Update Avatar
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/UpdateUserAvatarOption"
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
form := web.GetForm(ctx).(*api.UpdateUserAvatarOption)
|
||||
|
||||
content, err := base64.StdEncoding.DecodeString(form.Image)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusBadRequest, "DecodeImage", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = user_service.UploadAvatar(ctx.Doer, content)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "UploadAvatar", err)
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// DeleteAvatar deletes the Avatar of an User
|
||||
func DeleteAvatar(ctx *context.APIContext) {
|
||||
// swagger:operation DELETE /user/avatar user userDeleteAvatar
|
||||
// ---
|
||||
// summary: Delete Avatar
|
||||
// produces:
|
||||
// - application/json
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
err := user_service.DeleteAvatar(ctx.Doer)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "DeleteAvatar", err)
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
@ -14,12 +14,15 @@ import (
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/updatechecker"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/services/cron"
|
||||
"code.gitea.io/gitea/services/forms"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -133,12 +136,22 @@ func DashboardPost(ctx *context.Context) {
|
||||
|
||||
// Run operation.
|
||||
if form.Op != "" {
|
||||
task := cron.GetTask(form.Op)
|
||||
if task != nil {
|
||||
go task.RunWithUser(ctx.Doer, nil)
|
||||
ctx.Flash.Success(ctx.Tr("admin.dashboard.task.started", ctx.Tr("admin.dashboard."+form.Op)))
|
||||
} else {
|
||||
ctx.Flash.Error(ctx.Tr("admin.dashboard.task.unknown", form.Op))
|
||||
switch form.Op {
|
||||
case "sync_repo_branches":
|
||||
go func() {
|
||||
if err := repo_service.AddAllRepoBranchesToSyncQueue(graceful.GetManager().ShutdownContext(), ctx.Doer.ID); err != nil {
|
||||
log.Error("AddAllRepoBranchesToSyncQueue: %v: %v", ctx.Doer.ID, err)
|
||||
}
|
||||
}()
|
||||
ctx.Flash.Success(ctx.Tr("admin.dashboard.sync_branch.started"))
|
||||
default:
|
||||
task := cron.GetTask(form.Op)
|
||||
if task != nil {
|
||||
go task.RunWithUser(ctx.Doer, nil)
|
||||
ctx.Flash.Success(ctx.Tr("admin.dashboard.task.started", ctx.Tr("admin.dashboard."+form.Op)))
|
||||
} else {
|
||||
ctx.Flash.Error(ctx.Tr("admin.dashboard.task.unknown", form.Op))
|
||||
}
|
||||
}
|
||||
}
|
||||
if form.From == "monitor" {
|
||||
|
||||
@ -383,7 +383,7 @@ func SignOut(ctx *context.Context) {
|
||||
})
|
||||
}
|
||||
HandleSignOut(ctx)
|
||||
ctx.Redirect(setting.AppSubURL + "/")
|
||||
ctx.JSONRedirect(setting.AppSubURL + "/")
|
||||
}
|
||||
|
||||
// SignUp render the register page
|
||||
|
||||
@ -73,6 +73,14 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
|
||||
orderBy = db.SearchOrderBySizeReverse
|
||||
case "size":
|
||||
orderBy = db.SearchOrderBySize
|
||||
case "reversegitsize":
|
||||
orderBy = db.SearchOrderByGitSizeReverse
|
||||
case "gitsize":
|
||||
orderBy = db.SearchOrderByGitSize
|
||||
case "reverselfssize":
|
||||
orderBy = db.SearchOrderByLFSSizeReverse
|
||||
case "lfssize":
|
||||
orderBy = db.SearchOrderByLFSSize
|
||||
case "moststars":
|
||||
orderBy = db.SearchOrderByStarsReverse
|
||||
case "feweststars":
|
||||
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
@ -310,6 +311,55 @@ func rerunJob(ctx *context_module.Context, job *actions_model.ActionRunJob) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
func Logs(ctx *context_module.Context) {
|
||||
runIndex := ctx.ParamsInt64("run")
|
||||
jobIndex := ctx.ParamsInt64("job")
|
||||
|
||||
job, _ := getRunJobs(ctx, runIndex, jobIndex)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
if job.TaskID == 0 {
|
||||
ctx.Error(http.StatusNotFound, "job is not started")
|
||||
return
|
||||
}
|
||||
|
||||
err := job.LoadRun(ctx)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
task, err := actions_model.GetTaskByID(ctx, job.TaskID)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
if task.LogExpired {
|
||||
ctx.Error(http.StatusNotFound, "logs have been cleaned up")
|
||||
return
|
||||
}
|
||||
|
||||
reader, err := actions.OpenLogs(ctx, task.LogInStorage, task.LogFilename)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
workflowName := job.Run.WorkflowID
|
||||
if p := strings.Index(workflowName, "."); p > 0 {
|
||||
workflowName = workflowName[0:p]
|
||||
}
|
||||
ctx.ServeContent(reader, &context_module.ServeHeaderOptions{
|
||||
Filename: fmt.Sprintf("%v-%v-%v.log", workflowName, job.Name, task.ID),
|
||||
ContentLength: &task.LogSize,
|
||||
ContentType: "text/plain",
|
||||
ContentTypeCharset: "utf-8",
|
||||
Disposition: "attachment",
|
||||
})
|
||||
}
|
||||
|
||||
func Cancel(ctx *context_module.Context) {
|
||||
runIndex := ctx.ParamsInt64("run")
|
||||
|
||||
|
||||
@ -13,7 +13,6 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
@ -28,32 +27,16 @@ import (
|
||||
"code.gitea.io/gitea/services/forms"
|
||||
release_service "code.gitea.io/gitea/services/release"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
files_service "code.gitea.io/gitea/services/repository/files"
|
||||
)
|
||||
|
||||
const (
|
||||
tplBranch base.TplName = "repo/branch/list"
|
||||
)
|
||||
|
||||
// Branch contains the branch information
|
||||
type Branch struct {
|
||||
Name string
|
||||
Commit *git.Commit
|
||||
IsProtected bool
|
||||
IsDeleted bool
|
||||
IsIncluded bool
|
||||
DeletedBranch *git_model.DeletedBranch
|
||||
CommitsAhead int
|
||||
CommitsBehind int
|
||||
LatestPullRequest *issues_model.PullRequest
|
||||
MergeMovedOn bool
|
||||
}
|
||||
|
||||
// Branches render repository branch page
|
||||
func Branches(ctx *context.Context) {
|
||||
ctx.Data["Title"] = "Branches"
|
||||
ctx.Data["IsRepoToolbarBranches"] = true
|
||||
ctx.Data["DefaultBranch"] = ctx.Repo.Repository.DefaultBranch
|
||||
ctx.Data["AllowsPulls"] = ctx.Repo.Repository.AllowsPulls()
|
||||
ctx.Data["IsWriter"] = ctx.Repo.CanWrite(unit.TypeCode)
|
||||
ctx.Data["IsMirror"] = ctx.Repo.Repository.IsMirror
|
||||
@ -68,15 +51,15 @@ func Branches(ctx *context.Context) {
|
||||
}
|
||||
pageSize := setting.Git.BranchesRangeSize
|
||||
|
||||
skip := (page - 1) * pageSize
|
||||
log.Debug("Branches: skip: %d limit: %d", skip, pageSize)
|
||||
defaultBranchBranch, branches, branchesCount := loadBranches(ctx, skip, pageSize)
|
||||
if ctx.Written() {
|
||||
defaultBranch, branches, branchesCount, err := repo_service.LoadBranches(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, util.OptionalBoolNone, page, pageSize)
|
||||
if err != nil {
|
||||
ctx.ServerError("LoadBranches", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["Branches"] = branches
|
||||
ctx.Data["DefaultBranchBranch"] = defaultBranchBranch
|
||||
pager := context.NewPagination(branchesCount, pageSize, page, 5)
|
||||
ctx.Data["DefaultBranchBranch"] = defaultBranch
|
||||
pager := context.NewPagination(int(branchesCount), pageSize, page, 5)
|
||||
pager.SetDefaultParams(ctx)
|
||||
ctx.Data["Page"] = pager
|
||||
|
||||
@ -130,7 +113,7 @@ func RestoreBranchPost(ctx *context.Context) {
|
||||
|
||||
if err := git.Push(ctx, ctx.Repo.Repository.RepoPath(), git.PushOptions{
|
||||
Remote: ctx.Repo.Repository.RepoPath(),
|
||||
Branch: fmt.Sprintf("%s:%s%s", deletedBranch.Commit, git.BranchPrefix, deletedBranch.Name),
|
||||
Branch: fmt.Sprintf("%s:%s%s", deletedBranch.CommitID, git.BranchPrefix, deletedBranch.Name),
|
||||
Env: repo_module.PushingEnvironment(ctx.Doer, ctx.Repo.Repository),
|
||||
}); err != nil {
|
||||
if strings.Contains(err.Error(), "already exists") {
|
||||
@ -148,7 +131,7 @@ func RestoreBranchPost(ctx *context.Context) {
|
||||
&repo_module.PushUpdateOptions{
|
||||
RefFullName: git.RefNameFromBranch(deletedBranch.Name),
|
||||
OldCommitID: git.EmptySHA,
|
||||
NewCommitID: deletedBranch.Commit,
|
||||
NewCommitID: deletedBranch.CommitID,
|
||||
PusherID: ctx.Doer.ID,
|
||||
PusherName: ctx.Doer.Name,
|
||||
RepoUserName: ctx.Repo.Owner.Name,
|
||||
@ -166,180 +149,6 @@ func redirect(ctx *context.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// loadBranches loads branches from the repository limited by page & pageSize.
|
||||
// NOTE: May write to context on error.
|
||||
func loadBranches(ctx *context.Context, skip, limit int) (*Branch, []*Branch, int) {
|
||||
defaultBranch, err := ctx.Repo.GitRepo.GetBranch(ctx.Repo.Repository.DefaultBranch)
|
||||
if err != nil {
|
||||
if !git.IsErrBranchNotExist(err) {
|
||||
log.Error("loadBranches: get default branch: %v", err)
|
||||
ctx.ServerError("GetDefaultBranch", err)
|
||||
return nil, nil, 0
|
||||
}
|
||||
log.Warn("loadBranches: missing default branch %s for %-v", ctx.Repo.Repository.DefaultBranch, ctx.Repo.Repository)
|
||||
}
|
||||
|
||||
rawBranches, totalNumOfBranches, err := ctx.Repo.GitRepo.GetBranches(skip, limit)
|
||||
if err != nil {
|
||||
log.Error("GetBranches: %v", err)
|
||||
ctx.ServerError("GetBranches", err)
|
||||
return nil, nil, 0
|
||||
}
|
||||
|
||||
rules, err := git_model.FindRepoProtectedBranchRules(ctx, ctx.Repo.Repository.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("FindRepoProtectedBranchRules", err)
|
||||
return nil, nil, 0
|
||||
}
|
||||
|
||||
repoIDToRepo := map[int64]*repo_model.Repository{}
|
||||
repoIDToRepo[ctx.Repo.Repository.ID] = ctx.Repo.Repository
|
||||
|
||||
repoIDToGitRepo := map[int64]*git.Repository{}
|
||||
repoIDToGitRepo[ctx.Repo.Repository.ID] = ctx.Repo.GitRepo
|
||||
|
||||
var branches []*Branch
|
||||
for i := range rawBranches {
|
||||
if defaultBranch != nil && rawBranches[i].Name == defaultBranch.Name {
|
||||
// Skip default branch
|
||||
continue
|
||||
}
|
||||
|
||||
branch := loadOneBranch(ctx, rawBranches[i], defaultBranch, &rules, repoIDToRepo, repoIDToGitRepo)
|
||||
if branch == nil {
|
||||
return nil, nil, 0
|
||||
}
|
||||
|
||||
branches = append(branches, branch)
|
||||
}
|
||||
|
||||
var defaultBranchBranch *Branch
|
||||
if defaultBranch != nil {
|
||||
// Always add the default branch
|
||||
log.Debug("loadOneBranch: load default: '%s'", defaultBranch.Name)
|
||||
defaultBranchBranch = loadOneBranch(ctx, defaultBranch, defaultBranch, &rules, repoIDToRepo, repoIDToGitRepo)
|
||||
branches = append(branches, defaultBranchBranch)
|
||||
}
|
||||
|
||||
if ctx.Repo.CanWrite(unit.TypeCode) {
|
||||
deletedBranches, err := getDeletedBranches(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("getDeletedBranches", err)
|
||||
return nil, nil, 0
|
||||
}
|
||||
branches = append(branches, deletedBranches...)
|
||||
}
|
||||
|
||||
return defaultBranchBranch, branches, totalNumOfBranches
|
||||
}
|
||||
|
||||
func loadOneBranch(ctx *context.Context, rawBranch, defaultBranch *git.Branch, protectedBranches *git_model.ProtectedBranchRules,
|
||||
repoIDToRepo map[int64]*repo_model.Repository,
|
||||
repoIDToGitRepo map[int64]*git.Repository,
|
||||
) *Branch {
|
||||
log.Trace("loadOneBranch: '%s'", rawBranch.Name)
|
||||
|
||||
commit, err := rawBranch.GetCommit()
|
||||
if err != nil {
|
||||
ctx.ServerError("GetCommit", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
branchName := rawBranch.Name
|
||||
p := protectedBranches.GetFirstMatched(branchName)
|
||||
isProtected := p != nil
|
||||
|
||||
divergence := &git.DivergeObject{
|
||||
Ahead: -1,
|
||||
Behind: -1,
|
||||
}
|
||||
if defaultBranch != nil {
|
||||
divergence, err = files_service.CountDivergingCommits(ctx, ctx.Repo.Repository, git.BranchPrefix+branchName)
|
||||
if err != nil {
|
||||
log.Error("CountDivergingCommits", err)
|
||||
}
|
||||
}
|
||||
|
||||
pr, err := issues_model.GetLatestPullRequestByHeadInfo(ctx.Repo.Repository.ID, branchName)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetLatestPullRequestByHeadInfo", err)
|
||||
return nil
|
||||
}
|
||||
headCommit := commit.ID.String()
|
||||
|
||||
mergeMovedOn := false
|
||||
if pr != nil {
|
||||
pr.HeadRepo = ctx.Repo.Repository
|
||||
if err := pr.LoadIssue(ctx); err != nil {
|
||||
ctx.ServerError("LoadIssue", err)
|
||||
return nil
|
||||
}
|
||||
if repo, ok := repoIDToRepo[pr.BaseRepoID]; ok {
|
||||
pr.BaseRepo = repo
|
||||
} else if err := pr.LoadBaseRepo(ctx); err != nil {
|
||||
ctx.ServerError("LoadBaseRepo", err)
|
||||
return nil
|
||||
} else {
|
||||
repoIDToRepo[pr.BaseRepoID] = pr.BaseRepo
|
||||
}
|
||||
pr.Issue.Repo = pr.BaseRepo
|
||||
|
||||
if pr.HasMerged {
|
||||
baseGitRepo, ok := repoIDToGitRepo[pr.BaseRepoID]
|
||||
if !ok {
|
||||
baseGitRepo, err = git.OpenRepository(ctx, pr.BaseRepo.RepoPath())
|
||||
if err != nil {
|
||||
ctx.ServerError("OpenRepository", err)
|
||||
return nil
|
||||
}
|
||||
defer baseGitRepo.Close()
|
||||
repoIDToGitRepo[pr.BaseRepoID] = baseGitRepo
|
||||
}
|
||||
pullCommit, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
|
||||
if err != nil && !git.IsErrNotExist(err) {
|
||||
ctx.ServerError("GetBranchCommitID", err)
|
||||
return nil
|
||||
}
|
||||
if err == nil && headCommit != pullCommit {
|
||||
// the head has moved on from the merge - we shouldn't delete
|
||||
mergeMovedOn = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isIncluded := divergence.Ahead == 0 && ctx.Repo.Repository.DefaultBranch != branchName
|
||||
return &Branch{
|
||||
Name: branchName,
|
||||
Commit: commit,
|
||||
IsProtected: isProtected,
|
||||
IsIncluded: isIncluded,
|
||||
CommitsAhead: divergence.Ahead,
|
||||
CommitsBehind: divergence.Behind,
|
||||
LatestPullRequest: pr,
|
||||
MergeMovedOn: mergeMovedOn,
|
||||
}
|
||||
}
|
||||
|
||||
func getDeletedBranches(ctx *context.Context) ([]*Branch, error) {
|
||||
branches := []*Branch{}
|
||||
|
||||
deletedBranches, err := git_model.GetDeletedBranches(ctx, ctx.Repo.Repository.ID)
|
||||
if err != nil {
|
||||
return branches, err
|
||||
}
|
||||
|
||||
for i := range deletedBranches {
|
||||
deletedBranches[i].LoadUser(ctx)
|
||||
branches = append(branches, &Branch{
|
||||
Name: deletedBranches[i].Name,
|
||||
IsDeleted: true,
|
||||
DeletedBranch: deletedBranches[i],
|
||||
})
|
||||
}
|
||||
|
||||
return branches, nil
|
||||
}
|
||||
|
||||
// CreateBranch creates new branch in repository
|
||||
func CreateBranch(ctx *context.Context) {
|
||||
form := web.GetForm(ctx).(*forms.NewBranchForm)
|
||||
@ -380,13 +189,13 @@ func CreateBranch(ctx *context.Context) {
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||
return
|
||||
}
|
||||
if models.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
|
||||
if git_model.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.branch.branch_already_exists", form.NewBranchName))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||
return
|
||||
}
|
||||
if models.IsErrBranchNameConflict(err) {
|
||||
e := err.(models.ErrBranchNameConflict)
|
||||
if git_model.IsErrBranchNameConflict(err) {
|
||||
e := err.(git_model.ErrBranchNameConflict)
|
||||
ctx.Flash.Error(ctx.Tr("repo.branch.branch_name_conflict", form.NewBranchName, e.BranchName))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||
return
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
@ -124,9 +125,9 @@ func CherryPickPost(ctx *context.Context) {
|
||||
// First lets try the simple plain read-tree -m approach
|
||||
opts.Content = sha
|
||||
if _, err := files.CherryPick(ctx, ctx.Repo.Repository, ctx.Doer, form.Revert, opts); err != nil {
|
||||
if models.IsErrBranchAlreadyExists(err) {
|
||||
if git_model.IsErrBranchAlreadyExists(err) {
|
||||
// User has specified a branch that already exists
|
||||
branchErr := err.(models.ErrBranchAlreadyExists)
|
||||
branchErr := err.(git_model.ErrBranchAlreadyExists)
|
||||
ctx.Data["Err_NewBranchName"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplCherryPick, &form)
|
||||
return
|
||||
@ -161,9 +162,9 @@ func CherryPickPost(ctx *context.Context) {
|
||||
ctx.Data["FileContent"] = opts.Content
|
||||
|
||||
if _, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, opts); err != nil {
|
||||
if models.IsErrBranchAlreadyExists(err) {
|
||||
if git_model.IsErrBranchAlreadyExists(err) {
|
||||
// User has specified a branch that already exists
|
||||
branchErr := err.(models.ErrBranchAlreadyExists)
|
||||
branchErr := err.(git_model.ErrBranchAlreadyExists)
|
||||
ctx.Data["Err_NewBranchName"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplCherryPick, &form)
|
||||
return
|
||||
|
||||
@ -16,6 +16,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
@ -683,7 +684,13 @@ func getBranchesAndTagsForRepo(ctx gocontext.Context, repo *repo_model.Repositor
|
||||
}
|
||||
defer gitRepo.Close()
|
||||
|
||||
branches, _, err = gitRepo.GetBranchNames(0, 0)
|
||||
branches, err = git_model.FindBranchNames(ctx, git_model.FindBranchOptions{
|
||||
RepoID: repo.ID,
|
||||
ListOptions: db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
IsDeletedBranch: util.OptionalBoolFalse,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -734,7 +741,13 @@ func CompareDiff(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
headBranches, _, err := ci.HeadGitRepo.GetBranchNames(0, 0)
|
||||
headBranches, err := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{
|
||||
RepoID: ci.HeadRepo.ID,
|
||||
ListOptions: db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
IsDeletedBranch: util.OptionalBoolFalse,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("GetBranches", err)
|
||||
return
|
||||
|
||||
@ -327,10 +327,10 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
} else if models.IsErrBranchAlreadyExists(err) {
|
||||
} else if git_model.IsErrBranchAlreadyExists(err) {
|
||||
// For when a user specifies a new branch that already exists
|
||||
ctx.Data["Err_NewBranchName"] = true
|
||||
if branchErr, ok := err.(models.ErrBranchAlreadyExists); ok {
|
||||
if branchErr, ok := err.(git_model.ErrBranchAlreadyExists); ok {
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplEditFile, &form)
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||
@ -529,9 +529,9 @@ func DeleteFilePost(ctx *context.Context) {
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
} else if models.IsErrBranchAlreadyExists(err) {
|
||||
} else if git_model.IsErrBranchAlreadyExists(err) {
|
||||
// For when a user specifies a new branch that already exists
|
||||
if branchErr, ok := err.(models.ErrBranchAlreadyExists); ok {
|
||||
if branchErr, ok := err.(git_model.ErrBranchAlreadyExists); ok {
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplDeleteFile, &form)
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||
@ -731,10 +731,10 @@ func UploadFilePost(ctx *context.Context) {
|
||||
} else if git.IsErrBranchNotExist(err) {
|
||||
branchErr := err.(git.ErrBranchNotExist)
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_does_not_exist", branchErr.Name), tplUploadFile, &form)
|
||||
} else if models.IsErrBranchAlreadyExists(err) {
|
||||
} else if git_model.IsErrBranchAlreadyExists(err) {
|
||||
// For when a user specifies a new branch that already exists
|
||||
ctx.Data["Err_NewBranchName"] = true
|
||||
branchErr := err.(models.ErrBranchAlreadyExists)
|
||||
branchErr := err.(git_model.ErrBranchAlreadyExists)
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplUploadFile, &form)
|
||||
} else if git.IsErrPushOutOfDate(err) {
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(ctx.Repo.CommitID)+"..."+util.PathEscapeSegments(form.NewBranchName)), tplUploadFile, &form)
|
||||
|
||||
@ -785,7 +785,13 @@ func RetrieveRepoMetas(ctx *context.Context, repo *repo_model.Repository, isPull
|
||||
return nil
|
||||
}
|
||||
|
||||
brs, _, err := ctx.Repo.GitRepo.GetBranchNames(0, 0)
|
||||
brs, err := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
ListOptions: db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
IsDeletedBranch: util.OptionalBoolFalse,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("GetBranches", err)
|
||||
return nil
|
||||
|
||||
@ -20,14 +20,12 @@ func LockIssue(ctx *context.Context) {
|
||||
}
|
||||
|
||||
if issue.IsLocked {
|
||||
ctx.Flash.Error(ctx.Tr("repo.issues.lock_duplicate"))
|
||||
ctx.Redirect(issue.Link())
|
||||
ctx.JSONError(ctx.Tr("repo.issues.lock_duplicate"))
|
||||
return
|
||||
}
|
||||
|
||||
if !form.HasValidReason() {
|
||||
ctx.Flash.Error(ctx.Tr("repo.issues.lock.unknown_reason"))
|
||||
ctx.Redirect(issue.Link())
|
||||
ctx.JSONError(ctx.Tr("repo.issues.lock.unknown_reason"))
|
||||
return
|
||||
}
|
||||
|
||||
@ -40,7 +38,7 @@ func LockIssue(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Redirect(issue.Link())
|
||||
ctx.JSONRedirect(issue.Link())
|
||||
}
|
||||
|
||||
// UnlockIssue unlocks a previously locked issue.
|
||||
@ -51,8 +49,7 @@ func UnlockIssue(ctx *context.Context) {
|
||||
}
|
||||
|
||||
if !issue.IsLocked {
|
||||
ctx.Flash.Error(ctx.Tr("repo.issues.unlock_error"))
|
||||
ctx.Redirect(issue.Link())
|
||||
ctx.JSONError(ctx.Tr("repo.issues.unlock_error"))
|
||||
return
|
||||
}
|
||||
|
||||
@ -64,5 +61,5 @@ func UnlockIssue(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Redirect(issue.Link())
|
||||
ctx.JSONRedirect(issue.Link())
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ func IssuePinOrUnpin(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Redirect(issue.Link())
|
||||
ctx.JSONRedirect(issue.Link())
|
||||
}
|
||||
|
||||
// IssueUnpin unpins a Issue
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
@ -94,9 +95,9 @@ func NewDiffPatchPost(ctx *context.Context) {
|
||||
Content: strings.ReplaceAll(form.Content, "\r", ""),
|
||||
})
|
||||
if err != nil {
|
||||
if models.IsErrBranchAlreadyExists(err) {
|
||||
if git_model.IsErrBranchAlreadyExists(err) {
|
||||
// User has specified a branch that already exists
|
||||
branchErr := err.(models.ErrBranchAlreadyExists)
|
||||
branchErr := err.(git_model.ErrBranchAlreadyExists)
|
||||
ctx.Data["Err_NewBranchName"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplEditFile, &form)
|
||||
return
|
||||
|
||||
@ -1493,7 +1493,7 @@ func UpdatePullRequestTarget(ctx *context.Context) {
|
||||
"error": err.Error(),
|
||||
"user_error": errorMessage,
|
||||
})
|
||||
} else if models.IsErrBranchesEqual(err) {
|
||||
} else if git_model.IsErrBranchesEqual(err) {
|
||||
errorMessage := ctx.Tr("repo.pulls.nothing_to_compare")
|
||||
|
||||
ctx.Flash.Error(errorMessage)
|
||||
|
||||
@ -286,7 +286,7 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
|
||||
}
|
||||
|
||||
// FIXME: since we only need to recheck files protected rules, we could improve this
|
||||
matchedBranches, err := git_model.FindAllMatchedBranches(ctx, ctx.Repo.GitRepo, protectBranch.RuleName)
|
||||
matchedBranches, err := git_model.FindAllMatchedBranches(ctx, ctx.Repo.Repository.ID, protectBranch.RuleName)
|
||||
if err != nil {
|
||||
ctx.ServerError("FindAllMatchedBranches", err)
|
||||
return
|
||||
|
||||
@ -420,7 +420,13 @@ func PackageSettingsPost(ctx *context.Context) {
|
||||
ctx.Flash.Success(ctx.Tr("packages.settings.delete.success"))
|
||||
}
|
||||
|
||||
ctx.Redirect(ctx.Package.Owner.HomeLink() + "/-/packages")
|
||||
redirectURL := ctx.Package.Owner.HomeLink() + "/-/packages"
|
||||
// redirect to the package if there are still versions available
|
||||
if has, _ := packages_model.ExistVersion(ctx, &packages_model.PackageSearchOptions{PackageID: ctx.Package.Descriptor.Package.ID}); has {
|
||||
redirectURL = ctx.Package.Descriptor.PackageWebLink()
|
||||
}
|
||||
|
||||
ctx.Redirect(redirectURL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@ -1207,6 +1207,7 @@ func registerRoutes(m *web.Route) {
|
||||
Get(actions.View).
|
||||
Post(web.Bind(actions.ViewRequest{}), actions.ViewPost)
|
||||
m.Post("/rerun", reqRepoActionsWriter, actions.RerunOne)
|
||||
m.Get("/logs", actions.Logs)
|
||||
})
|
||||
m.Post("/cancel", reqRepoActionsWriter, actions.Cancel)
|
||||
m.Post("/approve", reqRepoActionsWriter, actions.Approve)
|
||||
|
||||
@ -56,12 +56,20 @@ func stopTasks(ctx context.Context, opts actions_model.FindTaskOptions) error {
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Warn("Cannot stop task %v: %v", task.ID, err)
|
||||
// go on
|
||||
} else if remove, err := actions.TransferLogs(ctx, task.LogFilename); err != nil {
|
||||
log.Warn("Cannot transfer logs of task %v: %v", task.ID, err)
|
||||
} else {
|
||||
remove()
|
||||
continue
|
||||
}
|
||||
|
||||
remove, err := actions.TransferLogs(ctx, task.LogFilename)
|
||||
if err != nil {
|
||||
log.Warn("Cannot transfer logs of task %v: %v", task.ID, err)
|
||||
continue
|
||||
}
|
||||
task.LogInStorage = true
|
||||
if err := actions_model.UpdateTask(ctx, task, "log_in_storage"); err != nil {
|
||||
log.Warn("Cannot update task %v: %v", task.ID, err)
|
||||
continue
|
||||
}
|
||||
remove()
|
||||
}
|
||||
|
||||
CreateCommitStatus(ctx, jobs...)
|
||||
|
||||
@ -50,7 +50,7 @@ func ToEmailSearch(email *user_model.SearchEmailResult) *api.Email {
|
||||
}
|
||||
|
||||
// ToBranch convert a git.Commit and git.Branch to an api.Branch
|
||||
func ToBranch(ctx context.Context, repo *repo_model.Repository, b *git.Branch, c *git.Commit, bp *git_model.ProtectedBranch, user *user_model.User, isRepoAdmin bool) (*api.Branch, error) {
|
||||
func ToBranch(ctx context.Context, repo *repo_model.Repository, branchName string, c *git.Commit, bp *git_model.ProtectedBranch, user *user_model.User, isRepoAdmin bool) (*api.Branch, error) {
|
||||
if bp == nil {
|
||||
var hasPerm bool
|
||||
var canPush bool
|
||||
@ -65,11 +65,11 @@ func ToBranch(ctx context.Context, repo *repo_model.Repository, b *git.Branch, c
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
canPush = issues_model.CanMaintainerWriteToBranch(perms, b.Name, user)
|
||||
canPush = issues_model.CanMaintainerWriteToBranch(perms, branchName, user)
|
||||
}
|
||||
|
||||
return &api.Branch{
|
||||
Name: b.Name,
|
||||
Name: branchName,
|
||||
Commit: ToPayloadCommit(ctx, repo, c),
|
||||
Protected: false,
|
||||
RequiredApprovals: 0,
|
||||
@ -81,7 +81,7 @@ func ToBranch(ctx context.Context, repo *repo_model.Repository, b *git.Branch, c
|
||||
}
|
||||
|
||||
branch := &api.Branch{
|
||||
Name: b.Name,
|
||||
Name: branchName,
|
||||
Commit: ToPayloadCommit(ctx, repo, c),
|
||||
Protected: true,
|
||||
RequiredApprovals: bp.RequiredApprovals,
|
||||
|
||||
@ -642,7 +642,7 @@ func (g *RepositoryDumper) Finish() error {
|
||||
|
||||
// DumpRepository dump repository according MigrateOptions to a local directory
|
||||
func DumpRepository(ctx context.Context, baseDir, ownerName string, opts base.MigrateOptions) error {
|
||||
doer, err := user_model.GetAdminUser()
|
||||
doer, err := user_model.GetAdminUser(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -705,7 +705,7 @@ func updateOptionsUnits(opts *base.MigrateOptions, units []string) error {
|
||||
|
||||
// RestoreRepository restore a repository from the disk directory
|
||||
func RestoreRepository(ctx context.Context, baseDir, ownerName, repoName string, units []string, validation bool) error {
|
||||
doer, err := user_model.GetAdminUser()
|
||||
doer, err := user_model.GetAdminUser(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -170,7 +170,7 @@ func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer
|
||||
return err
|
||||
}
|
||||
if branchesEqual {
|
||||
return models.ErrBranchesEqual{
|
||||
return git_model.ErrBranchesEqual{
|
||||
HeadBranchName: pr.HeadBranch,
|
||||
BaseBranchName: targetBranch,
|
||||
}
|
||||
@ -338,7 +338,7 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string,
|
||||
for _, pr := range prs {
|
||||
divergence, err := GetDiverging(ctx, pr)
|
||||
if err != nil {
|
||||
if models.IsErrBranchDoesNotExist(err) && !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) {
|
||||
if git_model.IsErrBranchNotExist(err) && !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), 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)
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
@ -181,7 +181,7 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest)
|
||||
Run(prCtx.RunOpts()); err != nil {
|
||||
cancel()
|
||||
if !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) {
|
||||
return nil, nil, models.ErrBranchDoesNotExist{
|
||||
return nil, nil, git_model.ErrBranchNotExist{
|
||||
BranchName: pr.HeadBranch,
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
@ -168,7 +167,7 @@ func GetDiverging(ctx context.Context, pr *issues_model.PullRequest) (*git.Diver
|
||||
log.Trace("GetDiverging[%-v]: compare commits", pr)
|
||||
prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr)
|
||||
if err != nil {
|
||||
if !models.IsErrBranchDoesNotExist(err) {
|
||||
if !git_model.IsErrBranchNotExist(err) {
|
||||
log.Error("CreateTemporaryRepoForPR %-v: %v", pr, err)
|
||||
}
|
||||
return nil, err
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
@ -146,7 +147,15 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r
|
||||
}
|
||||
}
|
||||
}
|
||||
branches, _, _ := gitRepo.GetBranchNames(0, 0)
|
||||
|
||||
branches, _ := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{
|
||||
RepoID: repo.ID,
|
||||
ListOptions: db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
IsDeletedBranch: util.OptionalBoolFalse,
|
||||
})
|
||||
|
||||
found := false
|
||||
hasDefault := false
|
||||
hasMaster := false
|
||||
|
||||
@ -10,13 +10,21 @@ import (
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
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/graceful"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
"code.gitea.io/gitea/modules/queue"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
files_service "code.gitea.io/gitea/services/repository/files"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// CreateNewBranch creates a new repository branch
|
||||
@ -27,7 +35,7 @@ func CreateNewBranch(ctx context.Context, doer *user_model.User, repo *repo_mode
|
||||
}
|
||||
|
||||
if !git.IsBranchExist(ctx, repo.RepoPath(), oldBranchName) {
|
||||
return models.ErrBranchDoesNotExist{
|
||||
return git_model.ErrBranchNotExist{
|
||||
BranchName: oldBranchName,
|
||||
}
|
||||
}
|
||||
@ -40,16 +48,165 @@ func CreateNewBranch(ctx context.Context, doer *user_model.User, repo *repo_mode
|
||||
if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("Push: %w", err)
|
||||
return fmt.Errorf("push: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBranches returns branches from the repository, skipping skip initial branches and
|
||||
// returning at most limit branches, or all branches if limit is 0.
|
||||
func GetBranches(ctx context.Context, repo *repo_model.Repository, skip, limit int) ([]*git.Branch, int, error) {
|
||||
return git.GetBranchesByPath(ctx, repo.RepoPath(), skip, limit)
|
||||
// Branch contains the branch information
|
||||
type Branch struct {
|
||||
DBBranch *git_model.Branch
|
||||
IsProtected bool
|
||||
IsIncluded bool
|
||||
CommitsAhead int
|
||||
CommitsBehind int
|
||||
LatestPullRequest *issues_model.PullRequest
|
||||
MergeMovedOn bool
|
||||
}
|
||||
|
||||
// LoadBranches loads branches from the repository limited by page & pageSize.
|
||||
func LoadBranches(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, isDeletedBranch util.OptionalBool, page, pageSize int) (*Branch, []*Branch, int64, error) {
|
||||
defaultDBBranch, err := git_model.GetBranch(ctx, repo.ID, repo.DefaultBranch)
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
branchOpts := git_model.FindBranchOptions{
|
||||
RepoID: repo.ID,
|
||||
IsDeletedBranch: isDeletedBranch,
|
||||
ListOptions: db.ListOptions{
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
},
|
||||
}
|
||||
|
||||
totalNumOfBranches, err := git_model.CountBranches(ctx, branchOpts)
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
branchOpts.ExcludeBranchNames = []string{repo.DefaultBranch}
|
||||
|
||||
dbBranches, err := git_model.FindBranches(ctx, branchOpts)
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
if err := dbBranches.LoadDeletedBy(ctx); err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
if err := dbBranches.LoadPusher(ctx); err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
rules, err := git_model.FindRepoProtectedBranchRules(ctx, repo.ID)
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
repoIDToRepo := map[int64]*repo_model.Repository{}
|
||||
repoIDToRepo[repo.ID] = repo
|
||||
|
||||
repoIDToGitRepo := map[int64]*git.Repository{}
|
||||
repoIDToGitRepo[repo.ID] = gitRepo
|
||||
|
||||
branches := make([]*Branch, 0, len(dbBranches))
|
||||
for i := range dbBranches {
|
||||
branch, err := loadOneBranch(ctx, repo, dbBranches[i], &rules, repoIDToRepo, repoIDToGitRepo)
|
||||
if err != nil {
|
||||
return nil, nil, 0, fmt.Errorf("loadOneBranch: %v", err)
|
||||
}
|
||||
|
||||
branches = append(branches, branch)
|
||||
}
|
||||
|
||||
// Always add the default branch
|
||||
log.Debug("loadOneBranch: load default: '%s'", defaultDBBranch.Name)
|
||||
defaultBranch, err := loadOneBranch(ctx, repo, defaultDBBranch, &rules, repoIDToRepo, repoIDToGitRepo)
|
||||
if err != nil {
|
||||
return nil, nil, 0, fmt.Errorf("loadOneBranch: %v", err)
|
||||
}
|
||||
|
||||
return defaultBranch, branches, totalNumOfBranches, nil
|
||||
}
|
||||
|
||||
func loadOneBranch(ctx context.Context, repo *repo_model.Repository, dbBranch *git_model.Branch, protectedBranches *git_model.ProtectedBranchRules,
|
||||
repoIDToRepo map[int64]*repo_model.Repository,
|
||||
repoIDToGitRepo map[int64]*git.Repository,
|
||||
) (*Branch, error) {
|
||||
log.Trace("loadOneBranch: '%s'", dbBranch.Name)
|
||||
|
||||
branchName := dbBranch.Name
|
||||
p := protectedBranches.GetFirstMatched(branchName)
|
||||
isProtected := p != nil
|
||||
|
||||
divergence := &git.DivergeObject{
|
||||
Ahead: -1,
|
||||
Behind: -1,
|
||||
}
|
||||
|
||||
// it's not default branch
|
||||
if repo.DefaultBranch != dbBranch.Name && !dbBranch.IsDeleted {
|
||||
var err error
|
||||
divergence, err = files_service.CountDivergingCommits(ctx, repo, git.BranchPrefix+branchName)
|
||||
if err != nil {
|
||||
log.Error("CountDivergingCommits: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
pr, err := issues_model.GetLatestPullRequestByHeadInfo(repo.ID, branchName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetLatestPullRequestByHeadInfo: %v", err)
|
||||
}
|
||||
headCommit := dbBranch.CommitID
|
||||
|
||||
mergeMovedOn := false
|
||||
if pr != nil {
|
||||
pr.HeadRepo = repo
|
||||
if err := pr.LoadIssue(ctx); err != nil {
|
||||
return nil, fmt.Errorf("LoadIssue: %v", err)
|
||||
}
|
||||
if repo, ok := repoIDToRepo[pr.BaseRepoID]; ok {
|
||||
pr.BaseRepo = repo
|
||||
} else if err := pr.LoadBaseRepo(ctx); err != nil {
|
||||
return nil, fmt.Errorf("LoadBaseRepo: %v", err)
|
||||
} else {
|
||||
repoIDToRepo[pr.BaseRepoID] = pr.BaseRepo
|
||||
}
|
||||
pr.Issue.Repo = pr.BaseRepo
|
||||
|
||||
if pr.HasMerged {
|
||||
baseGitRepo, ok := repoIDToGitRepo[pr.BaseRepoID]
|
||||
if !ok {
|
||||
baseGitRepo, err = git.OpenRepository(ctx, pr.BaseRepo.RepoPath())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("OpenRepository: %v", err)
|
||||
}
|
||||
defer baseGitRepo.Close()
|
||||
repoIDToGitRepo[pr.BaseRepoID] = baseGitRepo
|
||||
}
|
||||
pullCommit, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
|
||||
if err != nil && !git.IsErrNotExist(err) {
|
||||
return nil, fmt.Errorf("GetBranchCommitID: %v", err)
|
||||
}
|
||||
if err == nil && headCommit != pullCommit {
|
||||
// the head has moved on from the merge - we shouldn't delete
|
||||
mergeMovedOn = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isIncluded := divergence.Ahead == 0 && repo.DefaultBranch != branchName
|
||||
return &Branch{
|
||||
DBBranch: dbBranch,
|
||||
IsProtected: isProtected,
|
||||
IsIncluded: isIncluded,
|
||||
CommitsAhead: divergence.Ahead,
|
||||
CommitsBehind: divergence.Behind,
|
||||
LatestPullRequest: pr,
|
||||
MergeMovedOn: mergeMovedOn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetBranchCommitID(ctx context.Context, repo *repo_model.Repository, branch string) (string, error) {
|
||||
@ -62,17 +219,17 @@ func checkBranchName(ctx context.Context, repo *repo_model.Repository, name stri
|
||||
branchRefName := strings.TrimPrefix(refName, git.BranchPrefix)
|
||||
switch {
|
||||
case branchRefName == name:
|
||||
return models.ErrBranchAlreadyExists{
|
||||
return git_model.ErrBranchAlreadyExists{
|
||||
BranchName: name,
|
||||
}
|
||||
// If branchRefName like a/b but we want to create a branch named a then we have a conflict
|
||||
case strings.HasPrefix(branchRefName, name+"/"):
|
||||
return models.ErrBranchNameConflict{
|
||||
return git_model.ErrBranchNameConflict{
|
||||
BranchName: branchRefName,
|
||||
}
|
||||
// Conversely if branchRefName like a but we want to create a branch named a/b then we also have a conflict
|
||||
case strings.HasPrefix(name, branchRefName+"/"):
|
||||
return models.ErrBranchNameConflict{
|
||||
return git_model.ErrBranchNameConflict{
|
||||
BranchName: branchRefName,
|
||||
}
|
||||
case refName == git.TagPrefix+name:
|
||||
@ -101,7 +258,7 @@ func CreateNewBranchFromCommit(ctx context.Context, doer *user_model.User, repo
|
||||
if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("Push: %w", err)
|
||||
return fmt.Errorf("push: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -169,13 +326,28 @@ func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.R
|
||||
return git_model.ErrBranchIsProtected
|
||||
}
|
||||
|
||||
rawBranch, err := git_model.GetBranch(ctx, repo.ID, branchName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetBranch: %vc", err)
|
||||
}
|
||||
|
||||
if rawBranch.IsDeleted {
|
||||
return nil
|
||||
}
|
||||
|
||||
commit, err := gitRepo.GetBranchCommit(branchName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gitRepo.DeleteBranch(branchName, git.DeleteBranchOptions{
|
||||
Force: true,
|
||||
if err := db.WithTx(ctx, func(ctx context.Context) error {
|
||||
if err := git_model.AddDeletedBranch(ctx, repo.ID, branchName, doer.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gitRepo.DeleteBranch(branchName, git.DeleteBranchOptions{
|
||||
Force: true,
|
||||
})
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -196,3 +368,45 @@ func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.R
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type BranchSyncOptions struct {
|
||||
RepoID int64
|
||||
}
|
||||
|
||||
// branchSyncQueue represents a queue to handle branch sync jobs.
|
||||
var branchSyncQueue *queue.WorkerPoolQueue[*BranchSyncOptions]
|
||||
|
||||
func handlerBranchSync(items ...*BranchSyncOptions) []*BranchSyncOptions {
|
||||
for _, opts := range items {
|
||||
_, err := repo_module.SyncRepoBranches(graceful.GetManager().ShutdownContext(), opts.RepoID, 0)
|
||||
if err != nil {
|
||||
log.Error("syncRepoBranches [%d] failed: %v", opts.RepoID, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func addRepoToBranchSyncQueue(repoID, doerID int64) error {
|
||||
return branchSyncQueue.Push(&BranchSyncOptions{
|
||||
RepoID: repoID,
|
||||
})
|
||||
}
|
||||
|
||||
func initBranchSyncQueue(ctx context.Context) error {
|
||||
branchSyncQueue = queue.CreateUniqueQueue(ctx, "branch_sync", handlerBranchSync)
|
||||
if branchSyncQueue == nil {
|
||||
return errors.New("unable to create branch_sync queue")
|
||||
}
|
||||
go graceful.GetManager().RunWithCancel(branchSyncQueue)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func AddAllRepoBranchesToSyncQueue(ctx context.Context, doerID int64) error {
|
||||
if err := db.Iterate(ctx, builder.Eq{"is_empty": false}, func(ctx context.Context, repo *repo_model.Repository) error {
|
||||
return addRepoToBranchSyncQueue(repo.ID, doerID)
|
||||
}); err != nil {
|
||||
return fmt.Errorf("run sync all branches failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -58,7 +58,7 @@ func (opts *ApplyDiffPatchOptions) Validate(ctx context.Context, repo *repo_mode
|
||||
if opts.NewBranch != opts.OldBranch {
|
||||
existingBranch, err := gitRepo.GetBranch(opts.NewBranch)
|
||||
if existingBranch != nil {
|
||||
return models.ErrBranchAlreadyExists{
|
||||
return git_model.ErrBranchAlreadyExists{
|
||||
BranchName: opts.NewBranch,
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,7 +197,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
|
||||
if opts.NewBranch != opts.OldBranch {
|
||||
existingBranch, err := gitRepo.GetBranch(opts.NewBranch)
|
||||
if existingBranch != nil {
|
||||
return nil, models.ErrBranchAlreadyExists{
|
||||
return nil, git_model.ErrBranchAlreadyExists{
|
||||
BranchName: opts.NewBranch,
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,7 +157,15 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
|
||||
if err = repo_module.CreateDelegateHooks(repoPath); err != nil {
|
||||
return fmt.Errorf("createDelegateHooks: %w", err)
|
||||
}
|
||||
return nil
|
||||
|
||||
gitRepo, err := git.OpenRepository(txCtx, repo.RepoPath())
|
||||
if err != nil {
|
||||
return fmt.Errorf("OpenRepository: %w", err)
|
||||
}
|
||||
defer gitRepo.Close()
|
||||
|
||||
_, err = repo_module.SyncRepoBranchesWithRepo(txCtx, repo, gitRepo, doer.ID)
|
||||
return err
|
||||
})
|
||||
needsRollbackInPanic = false
|
||||
if err != nil {
|
||||
|
||||
@ -93,7 +93,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
|
||||
defer gitRepo.Close()
|
||||
|
||||
if err = repo_module.UpdateRepoSize(ctx, repo); err != nil {
|
||||
log.Error("Failed to update size for repository: %v", err)
|
||||
return fmt.Errorf("Failed to update size for repository: %v", err)
|
||||
}
|
||||
|
||||
addTags := make([]string, 0, len(optsList))
|
||||
@ -259,8 +259,8 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
|
||||
|
||||
notification.NotifyPushCommits(ctx, pusher, repo, opts, commits)
|
||||
|
||||
if err = git_model.RemoveDeletedBranchByName(ctx, repo.ID, branch); err != nil {
|
||||
log.Error("models.RemoveDeletedBranch %s/%s failed: %v", repo.ID, branch, err)
|
||||
if err = git_model.UpdateBranch(ctx, repo.ID, branch, newCommit.ID.String(), newCommit.CommitMessage, opts.PusherID, newCommit.Committer.When); err != nil {
|
||||
return fmt.Errorf("git_model.UpdateBranch %s:%s failed: %v", repo.FullName(), branch, err)
|
||||
}
|
||||
|
||||
// Cache for big repository
|
||||
@ -273,8 +273,9 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
|
||||
// close all related pulls
|
||||
log.Error("close related pull request failed: %v", err)
|
||||
}
|
||||
if err := git_model.AddDeletedBranch(db.DefaultContext, repo.ID, branch, opts.OldCommitID, pusher.ID); err != nil {
|
||||
log.Warn("AddDeletedBranch: %v", err)
|
||||
|
||||
if err := git_model.AddDeletedBranch(db.DefaultContext, repo.ID, branch, pusher.ID); err != nil {
|
||||
return fmt.Errorf("AddDeletedBranch %s:%s failed: %v", repo.FullName(), branch, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ import (
|
||||
system_model "code.gitea.io/gitea/models/system"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
@ -100,7 +101,10 @@ func Init() error {
|
||||
}
|
||||
system_model.RemoveAllWithNotice(db.DefaultContext, "Clean up temporary repository uploads", setting.Repository.Upload.TempPath)
|
||||
system_model.RemoveAllWithNotice(db.DefaultContext, "Clean up temporary repositories", repo_module.LocalCopyPath())
|
||||
return initPushQueue()
|
||||
if err := initPushQueue(); err != nil {
|
||||
return err
|
||||
}
|
||||
return initBranchSyncQueue(graceful.GetManager().ShutdownContext())
|
||||
}
|
||||
|
||||
// UpdateRepository updates a repository
|
||||
|
||||
@ -1,6 +1,12 @@
|
||||
<div class="ui right floated secondary filter menu">
|
||||
<!-- Sort -->
|
||||
<div class="ui dropdown type jump item">
|
||||
<div class="ui secondary filter menu gt-ac gt-mx-0">
|
||||
<form class="ui form ignore-dirty gt-f1">
|
||||
<div class="ui fluid action input">
|
||||
{{template "shared/searchinput" dict "locale" .locale "Value" .Keyword "AutoFocus" true}}
|
||||
<button class="ui primary button">{{.locale.Tr "explore.search"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
<!-- Sort -->
|
||||
<div class="ui dropdown type jump item gt-mr-0">
|
||||
<span class="text">
|
||||
{{.locale.Tr "repo.issues.filter_sort"}}
|
||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
@ -15,9 +21,3 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form class="ui form ignore-dirty">
|
||||
<div class="ui fluid action input">
|
||||
{{template "shared/searchinput" dict "locale" .locale "Value" .Keyword "AutoFocus" true}}
|
||||
<button class="ui primary button">{{.locale.Tr "explore.search"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
{{.locale.Tr "admin.config.server_config"}}
|
||||
</h4>
|
||||
<div class="ui attached table segment">
|
||||
<dl class="dl-horizontal admin-dl-horizontal">
|
||||
<dl class="admin-dl-horizontal">
|
||||
<dt>{{.locale.Tr "admin.config.app_name"}}</dt>
|
||||
<dd>{{AppName}}</dd>
|
||||
<dt>{{.locale.Tr "admin.config.app_ver"}}</dt>
|
||||
@ -20,19 +20,19 @@
|
||||
<dt>{{.locale.Tr "admin.config.disable_router_log"}}</dt>
|
||||
<dd>{{if .DisableRouterLog}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
|
||||
|
||||
<div class="ui divider"></div>
|
||||
<div class="divider"></div>
|
||||
|
||||
<dt>{{.locale.Tr "admin.config.run_user"}}</dt>
|
||||
<dd>{{.RunUser}}</dd>
|
||||
<dt>{{.locale.Tr "admin.config.run_mode"}}</dt>
|
||||
<dd>{{.RunMode}}</dd>
|
||||
|
||||
<div class="ui divider"></div>
|
||||
<div class="divider"></div>
|
||||
|
||||
<dt>{{.locale.Tr "admin.config.git_version"}}</dt>
|
||||
<dd>{{.GitVersion}}</dd>
|
||||
|
||||
<div class="ui divider"></div>
|
||||
<div class="divider"></div>
|
||||
|
||||
<dt>{{.locale.Tr "admin.config.repo_root_path"}}</dt>
|
||||
<dd>{{.RepoRootPath}}</dd>
|
||||
@ -53,7 +53,7 @@
|
||||
{{.locale.Tr "admin.config.ssh_config"}}
|
||||
</h4>
|
||||
<div class="ui attached table segment">
|
||||
<dl class="dl-horizontal admin-dl-horizontal">
|
||||
<dl class="admin-dl-horizontal">
|
||||
<dt>{{.locale.Tr "admin.config.ssh_enabled"}}</dt>
|
||||
<dd>{{if not .SSH.Disabled}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
|
||||
{{if not .SSH.Disabled}}
|
||||
@ -88,7 +88,7 @@
|
||||
{{.locale.Tr "admin.config.lfs_config"}}
|
||||
</h4>
|
||||
<div class="ui attached table segment">
|
||||
<dl class="dl-horizontal admin-dl-horizontal">
|
||||
<dl class="admin-dl-horizontal">
|
||||
<dt>{{.locale.Tr "admin.config.lfs_enabled"}}</dt>
|
||||
<dd>{{if .LFS.StartServer}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
|
||||
{{if .LFS.StartServer}}
|
||||
@ -104,7 +104,7 @@
|
||||
{{.locale.Tr "admin.config.db_config"}}
|
||||
</h4>
|
||||
<div class="ui attached table segment">
|
||||
<dl class="dl-horizontal admin-dl-horizontal">
|
||||
<dl class="admin-dl-horizontal">
|
||||
<dt>{{.locale.Tr "admin.config.db_type"}}</dt>
|
||||
<dd>{{.DbCfg.Type}}</dd>
|
||||
{{if not (eq .DbCfg.Type "sqlite3")}}
|
||||
@ -132,7 +132,7 @@
|
||||
{{.locale.Tr "admin.config.service_config"}}
|
||||
</h4>
|
||||
<div class="ui attached table segment">
|
||||
<dl class="dl-horizontal admin-dl-horizontal">
|
||||
<dl class="admin-dl-horizontal">
|
||||
<dt>{{.locale.Tr "admin.config.register_email_confirm"}}</dt>
|
||||
<dd>{{if .Service.RegisterEmailConfirm}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
|
||||
<dt>{{.locale.Tr "admin.config.disable_register"}}</dt>
|
||||
@ -174,7 +174,7 @@
|
||||
<dd>{{if .Service.NoReplyAddress}}{{.Service.NoReplyAddress}}{{else}}-{{end}}</dd>
|
||||
<dt>{{.locale.Tr "admin.config.default_enable_dependencies"}}</dt>
|
||||
<dd>{{if .Service.DefaultEnableDependencies}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
|
||||
<div class="ui divider"></div>
|
||||
<div class="divider"></div>
|
||||
<dt>{{.locale.Tr "admin.config.active_code_lives"}}</dt>
|
||||
<dd>{{.Service.ActiveCodeLives}} {{.locale.Tr "tool.raw_minutes"}}</dd>
|
||||
<dt>{{.locale.Tr "admin.config.reset_password_code_lives"}}</dt>
|
||||
@ -186,7 +186,7 @@
|
||||
{{.locale.Tr "admin.config.webhook_config"}}
|
||||
</h4>
|
||||
<div class="ui attached table segment">
|
||||
<dl class="dl-horizontal admin-dl-horizontal">
|
||||
<dl class="admin-dl-horizontal">
|
||||
<dt>{{.locale.Tr "admin.config.queue_length"}}</dt>
|
||||
<dd>{{.Webhook.QueueLength}}</dd>
|
||||
<dt>{{.locale.Tr "admin.config.deliver_timeout"}}</dt>
|
||||
@ -200,7 +200,7 @@
|
||||
{{.locale.Tr "admin.config.mailer_config"}}
|
||||
</h4>
|
||||
<div class="ui attached table segment">
|
||||
<dl class="dl-horizontal admin-dl-horizontal">
|
||||
<dl class="admin-dl-horizontal">
|
||||
<dt>{{.locale.Tr "admin.config.mailer_enabled"}}</dt>
|
||||
<dd>{{if .MailerEnabled}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
|
||||
{{if .MailerEnabled}}
|
||||
@ -230,7 +230,7 @@
|
||||
{{end}}
|
||||
<dt>{{.locale.Tr "admin.config.mailer_user"}}</dt>
|
||||
<dd>{{if .Mailer.User}}{{.Mailer.User}}{{else}}(empty){{end}}</dd>
|
||||
<div class="ui divider"></div>
|
||||
<div class="divider"></div>
|
||||
<dt class="gt-py-2">{{.locale.Tr "admin.config.send_test_mail"}}</dt>
|
||||
<dd>
|
||||
<form class="ui form ignore-dirty" action="{{AppSubUrl}}/admin/config/test_mail" method="post">
|
||||
@ -249,7 +249,7 @@
|
||||
{{.locale.Tr "admin.config.cache_config"}}
|
||||
</h4>
|
||||
<div class="ui attached table segment">
|
||||
<dl class="dl-horizontal admin-dl-horizontal">
|
||||
<dl class="admin-dl-horizontal">
|
||||
<dt>{{.locale.Tr "admin.config.cache_adapter"}}</dt>
|
||||
<dd>{{.CacheAdapter}}</dd>
|
||||
{{if eq .CacheAdapter "memory"}}
|
||||
@ -269,7 +269,7 @@
|
||||
{{.locale.Tr "admin.config.session_config"}}
|
||||
</h4>
|
||||
<div class="ui attached table segment">
|
||||
<dl class="dl-horizontal admin-dl-horizontal">
|
||||
<dl class="admin-dl-horizontal">
|
||||
<dt>{{.locale.Tr "admin.config.session_provider"}}</dt>
|
||||
<dd>{{.SessionConfig.Provider}}</dd>
|
||||
<dt>{{.locale.Tr "admin.config.provider_config"}}</dt>
|
||||
@ -289,14 +289,14 @@
|
||||
{{.locale.Tr "admin.config.picture_config"}}
|
||||
</h4>
|
||||
<div class="ui attached table segment">
|
||||
<dl class="dl-horizontal admin-dl-horizontal">
|
||||
<dl class="admin-dl-horizontal">
|
||||
<dt>{{.locale.Tr "admin.config.disable_gravatar"}}</dt>
|
||||
<dd>
|
||||
<div class="ui toggle checkbox">
|
||||
<input type="checkbox" name="picture.disable_gravatar" version="{{.SystemSettings.GetVersion "picture.disable_gravatar"}}"{{if .SystemSettings.GetBool "picture.disable_gravatar"}} checked{{end}} title="{{.locale.Tr "admin.config.disable_gravatar"}}">
|
||||
</div>
|
||||
</dd>
|
||||
<div class="ui divider"></div>
|
||||
<div class="divider"></div>
|
||||
<dt>{{.locale.Tr "admin.config.enable_federated_avatar"}}</dt>
|
||||
<dd>
|
||||
<div class="ui toggle checkbox">
|
||||
@ -310,7 +310,7 @@
|
||||
{{.locale.Tr "admin.config.git_config"}}
|
||||
</h4>
|
||||
<div class="ui attached table segment">
|
||||
<dl class="dl-horizontal admin-dl-horizontal">
|
||||
<dl class="admin-dl-horizontal">
|
||||
<dt>{{.locale.Tr "admin.config.git_disable_diff_highlight"}}</dt>
|
||||
<dd>{{if .Git.DisableDiffHighlight}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
|
||||
<dt>{{.locale.Tr "admin.config.git_max_diff_lines"}}</dt>
|
||||
@ -322,7 +322,7 @@
|
||||
<dt>{{.locale.Tr "admin.config.git_gc_args"}}</dt>
|
||||
<dd><code>{{.Git.GCArgs}}</code></dd>
|
||||
|
||||
<div class="ui divider"></div>
|
||||
<div class="divider"></div>
|
||||
|
||||
<dt>{{.locale.Tr "admin.config.git_migrate_timeout"}}</dt>
|
||||
<dd>{{.Git.Timeout.Migrate}} {{.locale.Tr "tool.raw_seconds"}}</dd>
|
||||
@ -341,7 +341,7 @@
|
||||
{{.locale.Tr "admin.config.log_config"}}
|
||||
</h4>
|
||||
<div class="ui attached table segment">
|
||||
<dl class="dl-horizontal admin-dl-horizontal">
|
||||
<dl class="admin-dl-horizontal">
|
||||
{{if .Loggers.xorm.IsEnabled}}
|
||||
<dt>{{$.locale.Tr "admin.config.xorm_log_sql"}}</dt>
|
||||
<dd>{{if $.LogSQL}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
|
||||
|
||||
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