Merge branch 'main' into fix-incorrect-recently-pushed-new-branches-check

This commit is contained in:
yp05327 2024-01-24 17:36:31 +09:00 committed by GitHub
commit 58dbb670b0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
942 changed files with 12284 additions and 8383 deletions

54
.github/stale.yml vendored
View File

@ -1,54 +0,0 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 60
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
daysUntilClose: 14
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
- status/blocked
- kind/security
- lgtm/done
- reviewed/confirmed
- priority/critical
- kind/proposal
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: false
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: false
# Label to use when marking as stale
staleLabel: stale
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had recent activity.
I am here to help clear issues left open even if solved or waiting for more insight.
This issue will be closed if no further activity occurs during the next 2 weeks.
If the issue is still valid just add a comment to keep it alive.
Thank you for your contributions.
# Comment to post when closing a stale Issue or Pull Request.
closeComment: >
This issue has been automatically closed because of inactivity.
You can re-open it if needed.
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 1
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
pulls:
daysUntilStale: 60
daysUntilClose: 60
markComment: >
This pull request has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs during the next 2 months. Thank you
for your contributions.
closeComment: >
This pull request has been automatically closed because of inactivity.
You can re-open it if needed.

View File

@ -11,7 +11,7 @@ jobs:
if: github.repository == 'go-gitea/gitea'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true

View File

@ -17,6 +17,6 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'go-gitea/gitea'
steps:
- uses: dessant/lock-threads@v4
- uses: dessant/lock-threads@v5
with:
issue-inactive-days: 45

View File

@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true
@ -70,7 +70,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true
@ -87,7 +87,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true
@ -102,7 +102,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true
@ -130,7 +130,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true
@ -175,7 +175,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true

View File

@ -39,7 +39,7 @@ jobs:
- "9000:9000"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true
@ -64,7 +64,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true
@ -115,7 +115,7 @@ jobs:
- "9000:9000"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true
@ -165,7 +165,7 @@ jobs:
- "993:993"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true
@ -198,7 +198,7 @@ jobs:
- "1433:1433"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true

View File

@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true

View File

@ -18,7 +18,7 @@ jobs:
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true
@ -64,7 +64,7 @@ jobs:
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true
@ -101,7 +101,7 @@ jobs:
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true

View File

@ -17,7 +17,7 @@ jobs:
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true
@ -78,6 +78,8 @@ jobs:
id: meta
with:
images: gitea/gitea
flavor: |
latest=false
# 1.2.3-rc0
tags: |
type=semver,pattern={{version}}
@ -109,6 +111,7 @@ jobs:
images: gitea/gitea
# each tag below will have the suffix of -rootless
flavor: |
latest=false
suffix=-rootless
# 1.2.3-rc0
tags: |

View File

@ -19,7 +19,7 @@ jobs:
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true
@ -86,7 +86,6 @@ jobs:
# 1.2
# 1.2.3
tags: |
type=raw,value=latest
type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{version}}
@ -118,14 +117,13 @@ jobs:
images: gitea/gitea
# each tag below will have the suffix of -rootless
flavor: |
suffix=-rootless
suffix=-rootless,onlatest=true
# this will generate tags in the following format (with -rootless suffix added):
# latest
# 1
# 1.2
# 1.2.3
tags: |
type=raw,value=latest
type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{version}}

1
.gitignore vendored
View File

@ -11,6 +11,7 @@ _test
.idea
# Goland's output filename can not be set manually
/go_build_*
/gitea_*
# MS VSCode
.vscode

View File

@ -1,12 +1,12 @@
commands-show-output: false
fenced-code-language: false
first-line-h1: false
header-increment: false
heading-increment: false
line-length: {code_blocks: false, tables: false, stern: true, line_length: -1}
no-alt-text: false
no-bare-urls: false
no-blanks-blockquote: false
no-emphasis-as-header: false
no-emphasis-as-heading: false
no-empty-links: false
no-hard-tabs: {code_blocks: false}
no-inline-html: false

View File

@ -74,7 +74,7 @@ rules:
keyframe-declaration-no-important: true
keyframe-selector-notation: null
keyframes-name-pattern: null
length-zero-no-unit: true
length-zero-no-unit: [true, ignore: [custom-properties], ignoreFunctions: [var]]
max-nesting-depth: null
media-feature-name-allowed-list: null
media-feature-name-disallowed-list: null

View File

@ -1,5 +1,5 @@
# Build stage
FROM docker.io/library/golang:1.21-alpine3.18 AS build-env
FROM docker.io/library/golang:1.21-alpine3.19 AS build-env
ARG GOPROXY
ENV GOPROXY ${GOPROXY:-direct}
@ -41,7 +41,7 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \
/go/src/code.gitea.io/gitea/environment-to-ini
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
FROM docker.io/library/alpine:3.18
FROM docker.io/library/alpine:3.19
LABEL maintainer="maintainers@gitea.io"
EXPOSE 22 3000

View File

@ -1,5 +1,5 @@
# Build stage
FROM docker.io/library/golang:1.21-alpine3.18 AS build-env
FROM docker.io/library/golang:1.21-alpine3.19 AS build-env
ARG GOPROXY
ENV GOPROXY ${GOPROXY:-direct}
@ -39,7 +39,7 @@ RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \
/go/src/code.gitea.io/gitea/environment-to-ini
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
FROM docker.io/library/alpine:3.18
FROM docker.io/library/alpine:3.19
LABEL maintainer="maintainers@gitea.io"
EXPOSE 2222 3000

View File

@ -62,11 +62,16 @@ painless way of setting up a self-hosted Git service.
As Gitea is written in Go, it works across **all** the platforms and
architectures that are supported by Go, including Linux, macOS, and
Windows on x86, amd64, ARM and PowerPC architectures.
You can try it out using [the online demo](https://try.gitea.io/).
This project has been
[forked](https://blog.gitea.com/welcome-to-gitea/) from
[Gogs](https://gogs.io) since November of 2016, but a lot has changed.
For online demonstrations, you can visit [try.gitea.io](https://try.gitea.io).
For accessing free Gitea service (with a limited number of repositories), you can visit [gitea.com](https://gitea.com/user/login).
To quickly deploy your own dedicated Gitea instance on Gitea Cloud, you can start a free trial at [cloud.gitea.com](https://cloud.gitea.com).
## Building
From the root of the source tree, run:

View File

@ -58,7 +58,11 @@
Gitea 的首要目标是创建一个极易安装,运行非常快速,安装和使用体验良好的自建 Git 服务。我们采用 Go 作为后端语言,这使我们只要生成一个可执行程序即可。并且他还支持跨平台,支持 Linux, macOS 和 Windows 以及各种架构,除了 x86amd64还包括 ARM 和 PowerPC。
如果您想试用一下,请访问 [在线Demo](https://try.gitea.io/)
如果你想试用在线演示,请访问 [try.gitea.io](https://try.gitea.io/)。
如果你想使用免费的 Gitea 服务(有仓库数量限制),请访问 [gitea.com](https://gitea.com/user/login)。
如果你想在 Gitea Cloud 上快速部署你自己独享的 Gitea 实例,请访问 [cloud.gitea.com](https://cloud.gitea.com) 开始免费试用。
## 提示

File diff suppressed because one or more lines are too long

View File

@ -15,9 +15,8 @@ import (
var (
// CmdActions represents the available actions sub-commands.
CmdActions = &cli.Command{
Name: "actions",
Usage: "",
Description: "Commands for managing Gitea Actions",
Name: "actions",
Usage: "Manage Gitea Actions",
Subcommands: []*cli.Command{
subcmdActionsGenRunnerToken,
},
@ -51,6 +50,6 @@ func runGenerateActionsRunnerToken(c *cli.Context) error {
if extra.HasError() {
return handleCliResponseExtra(extra)
}
_, _ = fmt.Printf("%s\n", respText)
_, _ = fmt.Printf("%s\n", respText.Text)
return nil
}

View File

@ -21,7 +21,7 @@ var (
// CmdAdmin represents the available admin sub-command.
CmdAdmin = &cli.Command{
Name: "admin",
Usage: "Command line interface to perform common administrative operations",
Usage: "Perform common administrative operations",
Subcommands: []*cli.Command{
subcmdUser,
subcmdRepoSyncReleases,
@ -157,10 +157,10 @@ func runRepoSyncReleases(_ *cli.Context) error {
}
func getReleaseCount(ctx context.Context, id int64) (int64, error) {
return repo_model.GetReleaseCountByRepoID(
return db.Count[repo_model.Release](
ctx,
id,
repo_model.FindReleasesOptions{
RepoID: id,
IncludeTags: true,
},
)

View File

@ -14,14 +14,28 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/migrations"
migrate_base "code.gitea.io/gitea/models/migrations/base"
"code.gitea.io/gitea/modules/doctor"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/services/doctor"
"github.com/urfave/cli/v2"
"xorm.io/xorm"
)
// CmdDoctor represents the available doctor sub-command.
var CmdDoctor = &cli.Command{
Name: "doctor",
Usage: "Diagnose and optionally fix problems, convert or re-create database tables",
Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
Subcommands: []*cli.Command{
cmdDoctorCheck,
cmdRecreateTable,
cmdDoctorConvert,
},
}
var cmdDoctorCheck = &cli.Command{
Name: "check",
Usage: "Diagnose and optionally fix problems",
@ -60,19 +74,6 @@ var cmdDoctorCheck = &cli.Command{
},
}
// CmdDoctor represents the available doctor sub-command.
var CmdDoctor = &cli.Command{
Name: "doctor",
Usage: "Diagnose and optionally fix problems",
Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
Subcommands: []*cli.Command{
cmdDoctorCheck,
cmdRecreateTable,
cmdDoctorConvert,
},
}
var cmdRecreateTable = &cli.Command{
Name: "recreate-table",
Usage: "Recreate tables from XORM definitions and copy the data.",
@ -177,6 +178,7 @@ func runDoctorCheck(ctx *cli.Context) error {
if ctx.IsSet("list") {
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
_, _ = w.Write([]byte("Default\tName\tTitle\n"))
doctor.SortChecks(doctor.Checks)
for _, check := range doctor.Checks {
if check.IsDefault {
_, _ = w.Write([]byte{'*'})
@ -192,26 +194,20 @@ func runDoctorCheck(ctx *cli.Context) error {
var checks []*doctor.Check
if ctx.Bool("all") {
checks = doctor.Checks
checks = make([]*doctor.Check, len(doctor.Checks))
copy(checks, doctor.Checks)
} else if ctx.IsSet("run") {
addDefault := ctx.Bool("default")
names := ctx.StringSlice("run")
for i, name := range names {
names[i] = strings.ToLower(strings.TrimSpace(name))
}
runNamesSet := container.SetOf(ctx.StringSlice("run")...)
for _, check := range doctor.Checks {
if addDefault && check.IsDefault {
if (addDefault && check.IsDefault) || runNamesSet.Contains(check.Name) {
checks = append(checks, check)
continue
}
for _, name := range names {
if name == check.Name {
checks = append(checks, check)
break
}
runNamesSet.Remove(check.Name)
}
}
if len(runNamesSet) > 0 {
return fmt.Errorf("unknown checks: %q", strings.Join(runNamesSet.Values(), ","))
}
} else {
for _, check := range doctor.Checks {
if check.IsDefault {
@ -219,6 +215,5 @@ func runDoctorCheck(ctx *cli.Context) error {
}
}
}
return doctor.RunChecks(stdCtx, colorize, ctx.Bool("fix"), checks)
}

View File

@ -37,8 +37,8 @@ func runDoctorConvert(ctx *cli.Context) error {
switch {
case setting.Database.Type.IsMySQL():
if err := db.ConvertUtf8ToUtf8mb4(); err != nil {
log.Fatal("Failed to convert database from utf8 to utf8mb4: %v", err)
if err := db.ConvertDatabaseTable(); err != nil {
log.Fatal("Failed to convert database & table: %v", err)
return err
}
fmt.Println("Converted successfully, please confirm your database's character set is now utf8mb4")

33
cmd/doctor_test.go Normal file
View File

@ -0,0 +1,33 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"context"
"testing"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/services/doctor"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli/v2"
)
func TestDoctorRun(t *testing.T) {
doctor.Register(&doctor.Check{
Title: "Test Check",
Name: "test-check",
Run: func(ctx context.Context, logger log.Logger, autofix bool) error { return nil },
SkipDatabaseInitialization: true,
})
app := cli.NewApp()
app.Commands = []*cli.Command{cmdDoctorCheck}
err := app.Run([]string{"./gitea", "check", "--run", "test-check"})
assert.NoError(t, err)
err = app.Run([]string{"./gitea", "check", "--run", "no-such"})
assert.ErrorContains(t, err, `unknown checks: "no-such"`)
err = app.Run([]string{"./gitea", "check", "--run", "test-check,no-such"})
assert.ErrorContains(t, err, `unknown checks: "no-such"`)
}

View File

@ -18,7 +18,7 @@ var (
// CmdGenerate represents the available generate sub-command.
CmdGenerate = &cli.Command{
Name: "generate",
Usage: "Command line interface for running generators",
Usage: "Generate Gitea's secrets/keys/tokens",
Subcommands: []*cli.Command{
subcmdSecret,
},

View File

@ -31,8 +31,8 @@ var (
// CmdHook represents the available hooks sub-command.
CmdHook = &cli.Command{
Name: "hook",
Usage: "Delegate commands to corresponding Git hooks",
Description: "This should only be called by Git",
Usage: "(internal) Should only be called by Git",
Description: "Delegate commands to corresponding Git hooks",
Before: PrepareConsoleLoggerLevel(log.FATAL),
Subcommands: []*cli.Command{
subcmdHookPreReceive,
@ -376,7 +376,9 @@ Gitea or set your environment appropriately.`, "")
oldCommitIDs[count] = string(fields[0])
newCommitIDs[count] = string(fields[1])
refFullNames[count] = git.RefName(fields[2])
if refFullNames[count] == git.BranchPrefix+"master" && newCommitIDs[count] != git.EmptySHA && count == total {
commitID, _ := git.NewIDFromString(newCommitIDs[count])
if refFullNames[count] == git.BranchPrefix+"master" && !commitID.IsZero() && count == total {
masterPushed = true
}
count++
@ -669,7 +671,8 @@ Gitea or set your environment appropriately.`, "")
if err != nil {
return err
}
if rs.OldOID != git.EmptySHA {
commitID, _ := git.NewIDFromString(rs.OldOID)
if !commitID.IsZero() {
err = writeDataPktLine(ctx, os.Stdout, []byte("option old-oid "+rs.OldOID))
if err != nil {
return err

View File

@ -16,10 +16,11 @@ import (
// CmdKeys represents the available keys sub-command
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,
Name: "keys",
Usage: "(internal) Should only be called by SSH server",
Description: "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{
Name: "expected",
@ -77,6 +78,6 @@ func runKeys(c *cli.Context) error {
if extra.Error != nil {
return extra.Error
}
_, _ = fmt.Fprintln(c.App.Writer, strings.TrimSpace(authorizedString))
_, _ = fmt.Fprintln(c.App.Writer, strings.TrimSpace(authorizedString.Text))
return nil
}

View File

@ -45,6 +45,6 @@ func runSendMail(c *cli.Context) error {
if extra.HasError() {
return handleCliResponseExtra(extra)
}
_, _ = fmt.Printf("Sent %s email(s) to all users\n", respText)
_, _ = fmt.Printf("Sent %s email(s) to all users\n", respText.Text)
return nil
}

View File

@ -10,12 +10,12 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"github.com/urfave/cli/v2"
)
// cmdHelp is our own help subcommand with more information
// Keep in mind that the "./gitea help"(subcommand) is different from "./gitea --help"(flag), the flag doesn't parse the config or output "DEFAULT CONFIGURATION:" information
func cmdHelp() *cli.Command {
c := &cli.Command{
Name: "help",
@ -47,16 +47,10 @@ DEFAULT CONFIGURATION:
return c
}
var helpFlag = cli.HelpFlag
func init() {
// cli.HelpFlag = nil TODO: after https://github.com/urfave/cli/issues/1794 we can use this
}
func appGlobalFlags() []cli.Flag {
return []cli.Flag{
// make the builtin flags at the top
helpFlag,
cli.HelpFlag,
// 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
@ -121,20 +115,22 @@ func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context)
func NewMainApp(version, versionExtra string) *cli.App {
app := cli.NewApp()
app.Name = "Gitea"
app.HelpName = "gitea"
app.Usage = "A painless self-hosted Git service"
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.Description = `Gitea program contains "web" and other subcommands. If no subcommand is given, it starts the web server by default. Use "web" subcommand for more web server arguments, use other subcommands for other purposes.`
app.Version = version + versionExtra
app.EnableBashCompletion = true
// these sub-commands need to use config file
subCmdWithConfig := []*cli.Command{
cmdHelp(), // the "help" sub-command was used to show the more information for "work path" and "custom config"
CmdWeb,
CmdServ,
CmdHook,
CmdKeys,
CmdDump,
CmdAdmin,
CmdMigrate,
CmdKeys,
CmdDoctor,
CmdManager,
CmdEmbedded,
@ -142,13 +138,8 @@ func NewMainApp(version, versionExtra string) *cli.App {
CmdDumpRepository,
CmdRestoreRepository,
CmdActions,
cmdHelp(), // the "help" sub-command was used to show the more information for "work path" and "custom config"
}
cmdConvert := util.ToPointer(*cmdDoctorConvert)
cmdConvert.Hidden = true // still support the legacy "./gitea doctor" by the hidden sub-command, remove it in next release
subCmdWithConfig = append(subCmdWithConfig, cmdConvert)
// these sub-commands do not need the config file, and they do not depend on any path or environment variable.
subCmdStandalone := []*cli.Command{
CmdCert,

View File

@ -110,6 +110,9 @@ func migrateLFS(ctx context.Context, dstStorage storage.ObjectStorage) error {
func migrateAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error {
return db.Iterate(ctx, nil, func(ctx context.Context, user *user_model.User) error {
if user.CustomAvatarRelativePath() == "" {
return nil
}
_, err := storage.Copy(dstStorage, user.CustomAvatarRelativePath(), storage.Avatars, user.CustomAvatarRelativePath())
return err
})
@ -117,6 +120,9 @@ func migrateAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error
func migrateRepoAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error {
return db.Iterate(ctx, nil, func(ctx context.Context, repo *repo_model.Repository) error {
if repo.CustomAvatarRelativePath() == "" {
return nil
}
_, err := storage.Copy(dstStorage, repo.CustomAvatarRelativePath(), storage.RepoAvatars, repo.CustomAvatarRelativePath())
return err
})

View File

@ -42,7 +42,7 @@ const (
// CmdServ represents the available serv sub-command.
var CmdServ = &cli.Command{
Name: "serv",
Usage: "This command should only be called by SSH shell",
Usage: "(internal) Should only be called by SSH shell",
Description: "Serv provides access auth for repositories",
Before: PrepareConsoleLoggerLevel(log.FATAL),
Action: runServ,

View File

@ -17,7 +17,7 @@ import (
"strings"
"syscall"
"github.com/google/go-github/v53/github"
"github.com/google/go-github/v57/github"
"github.com/urfave/cli/v2"
"gopkg.in/yaml.v3"
)

View File

@ -52,7 +52,7 @@ After=network.target
# Uncomment the next line if you have repos with lots of files and get a HTTP 500 error because of that
# LimitNOFILE=524288:524288
RestartSec=2s
Type=notify
Type=simple
User=git
Group=git
WorkingDirectory=/var/lib/gitea/
@ -62,7 +62,6 @@ WorkingDirectory=/var/lib/gitea/
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
Restart=always
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
WatchdogSec=30s
# If you install Git to directory prefix other than default PATH (which happens
# for example if you install other versions of Git side-to-side with
# distribution version), uncomment below line and add that prefix to PATH

View File

@ -234,7 +234,7 @@ RUN_USER = ; git
;MINIMUM_KEY_SIZE_CHECK = false
;;
;; Disable CDN even in "prod" mode
;OFFLINE_MODE = false
;OFFLINE_MODE = true
;;
;; TLS Settings: Either ACME or manual
;; (Other common TLS configuration are found before)
@ -351,6 +351,7 @@ NAME = gitea
USER = root
;PASSWD = ;Use PASSWD = `your password` for quoting if you use special characters in the password.
;SSL_MODE = false ; either "false" (default), "true", or "skip-verify"
;CHARSET_COLLATION = ; Empty as default, Gitea will try to find a case-sensitive collation. Don't change it unless you clearly know what you need.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
@ -382,6 +383,7 @@ USER = root
;NAME = gitea
;USER = SA
;PASSWD = MwantsaSecurePassword1
;CHARSET_COLLATION = ; Empty as default, Gitea will try to find a case-sensitive collation. Don't change it unless you clearly know what you need.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
@ -492,6 +494,11 @@ INTERNAL_TOKEN=
;; Cache successful token hashes. API tokens are stored in the DB as pbkdf2 hashes however, this means that there is a potentially significant hashing load when there are multiple API operations.
;; This cache will store the successfully hashed tokens in a LRU cache as a balance between performance and security.
;SUCCESSFUL_TOKENS_CACHE_SIZE = 20
;;
;; Reject API tokens sent in URL query string (Accept Header-based API tokens only). This avoids security vulnerabilities
;; stemming from cached/logged plain-text API tokens.
;; In future releases, this will become the default behavior
;DISABLE_QUERY_AUTH_TOKEN = false
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -1060,6 +1067,9 @@ LEVEL = Info
;;
;; In addition to testing patches using the three-way merge method, re-test conflicting patches with git apply
;TEST_CONFLICTING_PATCHES_WITH_GIT_APPLY = false
;;
;; Retarget child pull requests to the parent pull request branch target on merge of parent pull request. It only works on merged PRs where the head and base branch target the same repo.
;RETARGET_CHILDREN_ON_MERGE = true
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -1153,15 +1163,9 @@ LEVEL = Info
;; enable cors headers (disabled by default)
;ENABLED = false
;;
;; scheme of allowed requests
;SCHEME = http
;;
;; list of requesting domains that are allowed
;; list of requesting origins that are allowed, eg: "https://*.example.com"
;ALLOW_DOMAIN = *
;;
;; allow subdomains of headers listed above to request
;ALLOW_SUBDOMAIN = false
;;
;; list of methods allowed to request
;METHODS = GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS
;;
@ -1207,6 +1211,9 @@ LEVEL = Info
;; Max size of files to be displayed (default is 8MiB)
;MAX_DISPLAY_FILE_SIZE = 8388608
;;
;; Detect ambiguous unicode characters in file contents and show warnings on the UI
;AMBIGUOUS_UNICODE_DETECTION = true
;;
;; Whether the email of the user should be shown in the Explore Users page
;SHOW_USER_EMAIL = true
;;
@ -1242,6 +1249,10 @@ LEVEL = Info
;; Change the sort type of the explore pages.
;; Default is "recentupdate", but you also have "alphabetically", "reverselastlogin", "newest", "oldest".
;EXPLORE_PAGING_DEFAULT_SORT = recentupdate
;;
;; The tense all timestamps should be rendered in. Possible values are `absolute` time (i.e. 1970-01-01, 11:59) and `mixed`.
;; `mixed` means most timestamps are rendered in relative time (i.e. 2 days ago).
;PREFERRED_TIMESTAMP_TENSE = mixed
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -1523,6 +1534,10 @@ LEVEL = Info
;; userid = use the userid / sub attribute
;; nickname = use the nickname attribute
;; email = use the username part of the email attribute
;; Note: `nickname` and `email` options will normalize input strings using the following criteria:
;; - diacritics are removed
;; - the characters in the set `['´\x60]` are removed
;; - the characters in the set `[\s~+]` are replaced with `-`
;USERNAME = nickname
;;
;; Update avatar if available from oauth2 provider.
@ -1697,9 +1712,6 @@ LEVEL = Info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; if the cache enabled
;ENABLED = true
;;
;; Either "memory", "redis", "memcache", or "twoqueue". default is "memory"
;ADAPTER = memory
;;
@ -1724,8 +1736,6 @@ LEVEL = Info
;[cache.last_commit]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; if the cache enabled
;ENABLED = true
;;
;; Time to keep items in cache if not used, default is 8760 hours.
;; Setting it to -1 disables caching
@ -2575,7 +2585,7 @@ LEVEL = Info
;;
;; Default platform to get action plugins, `github` for `https://github.com`, `self` for the current Gitea instance.
;DEFAULT_ACTIONS_URL = github
;; Default artifact retention time in days, default is 90 days
;; Default artifact retention time in days. Artifacts could have their own retention periods by setting the `retention-days` option in `actions/upload-artifact` step.
;ARTIFACT_RETENTION_DAYS = 90
;; Timeout to stop the task which have running status, but haven't been updated for a long time
;ZOMBIE_TASK_TIMEOUT = 10m

View File

@ -19,10 +19,10 @@ Some jurisdictions (such as EU), requires certain legal pages (e.g. Privacy Poli
## Getting Pages
Gitea source code ships with sample pages, available in `contrib/legal` directory. Copy them to `custom/public/`. For example, to add Privacy Policy:
Gitea source code ships with sample pages, available in `contrib/legal` directory. Copy them to `custom/public/assets/`. For example, to add Privacy Policy:
```
wget -O /path/to/custom/public/privacy.html https://raw.githubusercontent.com/go-gitea/gitea/main/contrib/legal/privacy.html.sample
wget -O /path/to/custom/public/assets/privacy.html https://raw.githubusercontent.com/go-gitea/gitea/main/contrib/legal/privacy.html.sample
```
Now you need to edit the page to meet your requirements. In particular you must change the email addresses, web addresses and references to "Your Gitea Instance" to match your situation.

View File

@ -19,10 +19,10 @@ menu:
## 获取页面
Gitea 源代码附带了示例页面,位于 `contrib/legal` 目录中。将它们复制到 `custom/public/` 目录下。例如,如果要添加隐私政策:
Gitea 源代码附带了示例页面,位于 `contrib/legal` 目录中。将它们复制到 `custom/public/assets/` 目录下。例如,如果要添加隐私政策:
```
wget -O /path/to/custom/public/privacy.html https://raw.githubusercontent.com/go-gitea/gitea/main/contrib/legal/privacy.html.sample
wget -O /path/to/custom/public/assets/privacy.html https://raw.githubusercontent.com/go-gitea/gitea/main/contrib/legal/privacy.html.sample
```
现在,你需要编辑该页面以满足你的需求。特别是,你必须更改电子邮件地址、网址以及与 "Your Gitea Instance" 相关的引用,以匹配你的情况。

View File

@ -135,6 +135,7 @@ In addition, there is _`StaticRootPath`_ which can be set as a built-in at build
- `POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES`: **false**: In default squash-merge messages include the commit message of all commits comprising the pull request.
- `ADD_CO_COMMITTER_TRAILERS`: **true**: Add co-authored-by and co-committed-by trailers to merge commit messages if committer does not match author.
- `TEST_CONFLICTING_PATCHES_WITH_GIT_APPLY`: **false**: PR patches are tested using a three-way merge method to discover if there are conflicts. If this setting is set to **true**, conflicting patches will be retested using `git apply` - This was the previous behaviour in 1.18 (and earlier) but is somewhat inefficient. Please report if you find that this setting is required.
- `RETARGET_CHILDREN_ON_MERGE`: **true**: Retarget child pull requests to the parent pull request branch target on merge of parent pull request. It only works on merged PRs where the head and base branch target the same repo.
### Repository - Issue (`repository.issue`)
@ -196,9 +197,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
## CORS (`cors`)
- `ENABLED`: **false**: enable cors headers (disabled by default)
- `SCHEME`: **http**: scheme of allowed requests
- `ALLOW_DOMAIN`: **\***: list of requesting domains that are allowed
- `ALLOW_SUBDOMAIN`: **false**: allow subdomains of headers listed above to request
- `ALLOW_DOMAIN`: **\***: list of requesting origins that are allowed, eg: "https://*.example.com"
- `METHODS`: **GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS**: list of methods allowed to request
- `MAX_AGE`: **10m**: max time to cache response
- `ALLOW_CREDENTIALS`: **false**: allow request with credentials
@ -220,6 +219,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
- `THEMES`: **gitea-auto,gitea-light,gitea-dark**: All available themes. Allow users select personalized themes.
regardless of the value of `DEFAULT_THEME`.
- `MAX_DISPLAY_FILE_SIZE`: **8388608**: Max size of files to be displayed (default is 8MiB)
- `AMBIGUOUS_UNICODE_DETECTION`: **true**: Detect ambiguous unicode characters in file contents and show warnings on the UI
- `REACTIONS`: All available reactions users can choose on issues/prs and comments
Values can be emoji alias (:smile:) or a unicode emoji.
For custom reactions, add a tightly cropped square image to public/assets/img/emoji/reaction_name.png
@ -232,6 +232,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
- `ONLY_SHOW_RELEVANT_REPOS`: **false**: Whether to only show relevant repos on the explore page when no keyword is specified and default sorting is used.
A repo is considered irrelevant if it's a fork or if it has no metadata (no description, no icon, no topic).
- `EXPLORE_PAGING_DEFAULT_SORT`: **recentupdate**: Change the sort type of the explore pages. Valid values are "recentupdate", "alphabetically", "reverselastlogin", "newest" and "oldest"
- `PREFERRED_TIMESTAMP_TENSE`: **mixed**: The tense all timestamps should be rendered in. Possible values are `absolute` time (i.e. 1970-01-01, 11:59) and `mixed`. `mixed` means most timestamps are rendered in relative time (i.e. 2 days ago).
### UI - Admin (`ui.admin`)
@ -356,7 +357,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
- `SSH_PER_WRITE_PER_KB_TIMEOUT`: **10s**: Timeout per Kb written to SSH connections.
- `MINIMUM_KEY_SIZE_CHECK`: **true**: Indicate whether to check minimum key size with corresponding type.
- `OFFLINE_MODE`: **false**: Disables use of CDN for static files and Gravatar for profile pictures.
- `OFFLINE_MODE`: **true**: Disables use of CDN for static files and Gravatar for profile pictures.
- `CERT_FILE`: **https/cert.pem**: Cert file path used for HTTPS. When chaining, the server certificate must come first, then intermediate CA certificates (if any). This is ignored if `ENABLE_ACME=true`. Paths are relative to `CUSTOM_PATH`.
- `KEY_FILE`: **https/key.pem**: Key file path used for HTTPS. This is ignored if `ENABLE_ACME=true`. Paths are relative to `CUSTOM_PATH`.
- `STATIC_ROOT_PATH`: **_`StaticRootPath`_**: Upper level of template and static files path.
@ -430,6 +431,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
- `NAME`: **gitea**: Database name.
- `USER`: **root**: Database username.
- `PASSWD`: **_empty_**: Database user password. Use \`your password\` or """your password""" for quoting if you use special characters in the password.
- `CHARSET_COLLATION`: **_empty_**: (MySQL/MSSQL only) Gitea expects to use a case-sensitive collation for database. Leave it empty to use the default collation decided by the Gitea. Don't change it unless you clearly know what you need.
- `SCHEMA`: **_empty_**: For PostgreSQL only, schema to use if different from "public". The schema must exist beforehand,
the user must have creation privileges on it, and the user search path must be set to the look into the schema first
(e.g. `ALTER USER user SET SEARCH_PATH = schema_name,"$user",public;`).
@ -572,6 +574,7 @@ And the following unique queues:
- off - do not check password complexity
- `PASSWORD_CHECK_PWN`: **false**: Check [HaveIBeenPwned](https://haveibeenpwned.com/Passwords) to see if a password has been exposed.
- `SUCCESSFUL_TOKENS_CACHE_SIZE`: **20**: Cache successful token hashes. API tokens are stored in the DB as pbkdf2 hashes however, this means that there is a potentially significant hashing load when there are multiple API operations. This cache will store the successfully hashed tokens in a LRU cache as a balance between performance and security.
- `DISABLE_QUERY_AUTH_TOKEN`: **false**: Reject API tokens sent in URL query string (Accept Header-based API tokens only). This setting will default to `true` in Gitea 1.23 and be deprecated in Gitea 1.24.
## Camo (`camo`)
@ -595,9 +598,13 @@ And the following unique queues:
- `OPENID_CONNECT_SCOPES`: **_empty_**: List of additional openid connect scopes. (`openid` is implicitly added)
- `ENABLE_AUTO_REGISTRATION`: **false**: Automatically create user accounts for new oauth2 users.
- `USERNAME`: **nickname**: The source of the username for new oauth2 accounts:
- userid - use the userid / sub attribute
- nickname - use the nickname attribute
- email - use the username part of the email attribute
- `userid` - use the userid / sub attribute
- `nickname` - use the nickname attribute
- `email` - use the username part of the email attribute
- Note: `nickname` and `email` options will normalize input strings using the following criteria:
- diacritics are removed
- the characters in the set `['´\x60]` are removed
- the characters in the set `[\s~+]` are replaced with `-`
- `UPDATE_AVATAR`: **false**: Update avatar if available from oauth2 provider. Update will be performed on each login.
- `ACCOUNT_LINKING`: **login**: How to handle if an account / email already exists:
- disabled - show an error
@ -761,7 +768,6 @@ and
## Cache (`cache`)
- `ENABLED`: **true**: Enable the cache.
- `ADAPTER`: **memory**: Cache engine adapter, either `memory`, `redis`, `redis-cluster`, `twoqueue` or `memcache`. (`twoqueue` represents a size limited LRU cache.)
- `INTERVAL`: **60**: Garbage Collection interval (sec), for memory and twoqueue cache only.
- `HOST`: **_empty_**: Connection string for `redis`, `redis-cluster` and `memcache`. For `twoqueue` sets configuration for the queue.
@ -773,7 +779,6 @@ and
## Cache - LastCommitCache settings (`cache.last_commit`)
- `ENABLED`: **true**: Enable the cache.
- `ITEM_TTL`: **8760h**: Time to keep items in cache if not used, Setting it to -1 disables caching.
- `COMMITS_COUNT`: **1000**: Only enable the cache when repository's commits count great than.
@ -1392,15 +1397,15 @@ PROXY_HOSTS = *.github.com
- `DEFAULT_ACTIONS_URL`: **github**: Default platform to get action plugins, `github` for `https://github.com`, `self` for the current Gitea instance.
- `STORAGE_TYPE`: **local**: Storage type for actions logs, `local` for local disk or `minio` for s3 compatible object storage service, default is `local` or other name defined with `[storage.xxx]`
- `MINIO_BASE_PATH`: **actions_log/**: Minio base path on the bucket only available when STORAGE_TYPE is `minio`
- `ARTIFACT_RETENTION_DAYS`: **90**: Number of days to keep artifacts. Set to 0 to disable artifact retention. Default is 90 days if not set.
- `ARTIFACT_RETENTION_DAYS`: **90**: Default number of days to keep artifacts. Artifacts could have their own retention periods by setting the `retention-days` option in `actions/upload-artifact` step.
- `ZOMBIE_TASK_TIMEOUT`: **10m**: Timeout to stop the task which have running status, but haven't been updated for a long time
- `ENDLESS_TASK_TIMEOUT`: **3h**: Timeout to stop the tasks which have running status and continuous updates, but don't end for a long time
- `ABANDONED_JOB_TIMEOUT`: **24h**: Timeout to cancel the jobs which have waiting status, but haven't been picked by a runner for a long time
- `SKIP_WORKFLOW_STRINGS`: **[skip ci],[ci skip],[no ci],[skip actions],[actions skip]**: Strings committers can place inside a commit message to skip executing the corresponding actions workflow
`DEFAULT_ACTIONS_URL` indicates where the Gitea Actions runners should find the actions with relative path.
For example, `uses: actions/checkout@v3` means `https://github.com/actions/checkout@v3` since the value of `DEFAULT_ACTIONS_URL` is `github`.
And it can be changed to `self` to make it `root_url_of_your_gitea/actions/checkout@v3`.
For example, `uses: actions/checkout@v4` means `https://github.com/actions/checkout@v4` since the value of `DEFAULT_ACTIONS_URL` is `github`.
And it can be changed to `self` to make it `root_url_of_your_gitea/actions/checkout@v4`.
Please note that using `self` is not recommended for most cases, as it could make names globally ambiguous.
Additionally, it requires you to mirror all the actions you need to your Gitea instance, which may not be worth it.
@ -1409,7 +1414,7 @@ Therefore, please use `self` only if you understand what you are doing.
In earlier versions (`<= 1.19`), `DEFAULT_ACTIONS_URL` could be set to any custom URLs like `https://gitea.com` or `http://your-git-server,https://gitea.com`, and the default value was `https://gitea.com`.
However, later updates removed those options, and now the only options are `github` and `self`, with the default value being `github`.
However, if you want to use actions from other git server, you can use a complete URL in `uses` field, it's supported by Gitea (but not GitHub).
Like `uses: https://gitea.com/actions/checkout@v3` or `uses: http://your-git-server/actions/checkout@v3`.
Like `uses: https://gitea.com/actions/checkout@v4` or `uses: http://your-git-server/actions/checkout@v4`.
## Other (`other`)

View File

@ -195,9 +195,7 @@ menu:
## 跨域 (`cors`)
- `ENABLED`: **false**: 启用 CORS 头部(默认禁用)
- `SCHEME`: **http**: 允许请求的协议
- `ALLOW_DOMAIN`: **\***: 允许请求的域名列表
- `ALLOW_SUBDOMAIN`: **false**: 允许上述列出的头部的子域名发出请求。
- `METHODS`: **GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS**: 允许发起的请求方式列表
- `MAX_AGE`: **10m**: 缓存响应的最大时间
- `ALLOW_CREDENTIALS`: **false**: 允许带有凭据的请求
@ -346,7 +344,7 @@ menu:
- `SSH_PER_WRITE_TIMEOUT`: **30s**:对 SSH 连接的任何写入设置超时。(将其设置为 -1 可以禁用所有超时。)
- `SSH_PER_WRITE_PER_KB_TIMEOUT`: **10s**:对写入 SSH 连接的每 KB 设置超时。
- `MINIMUM_KEY_SIZE_CHECK`: **true**:指示是否检查最小密钥大小与相应类型。
- `OFFLINE_MODE`: **false**:禁用 CDN 用于静态文件和 Gravatar 用于个人资料图片。
- `OFFLINE_MODE`: **true**:禁用 CDN 用于静态文件和 Gravatar 用于个人资料图片。
- `CERT_FILE`: **https/cert.pem**:用于 HTTPS 的证书文件路径。在链接时,服务器证书必须首先出现,然后是中间 CA 证书(如果有)。如果 `ENABLE_ACME=true`,则此设置会被忽略。路径相对于 `CUSTOM_PATH`
- `KEY_FILE`: **https/key.pem**:用于 HTTPS 的密钥文件路径。如果 `ENABLE_ACME=true`,则此设置会被忽略。路径相对于 `CUSTOM_PATH`
- `STATIC_ROOT_PATH`: **_`StaticRootPath`_**:模板和静态文件路径的上一级。
@ -721,7 +719,6 @@ Gitea 创建以下非唯一队列:
## 缓存 (`cache`)
- `ENABLED`: **true**: 是否启用缓存。
- `ADAPTER`: **memory**: 缓存引擎,可以为 `memory`, `redis`, `redis-cluster`, `twoqueue``memcache`. (`twoqueue` 代表缓冲区固定的LRU缓存)
- `INTERVAL`: **60**: 垃圾回收间隔(秒),只对`memory`和`towqueue`有效。
- `HOST`: **_empty_**: 缓存配置。`redis`, `redis-cluster``memcache`配置连接字符串;`twoqueue` 设置队列参数
@ -733,7 +730,6 @@ Gitea 创建以下非唯一队列:
### 缓存 - 最后提交缓存设置 (`cache.last_commit`)
- `ENABLED`: **true**:是否启用缓存。
- `ITEM_TTL`: **8760h**:如果未使用,保持缓存中的项目的时间,将其设置为 -1 会禁用缓存。
- `COMMITS_COUNT`: **1000**:仅在存储库的提交计数大于时启用缓存。
@ -991,7 +987,7 @@ Gitea 创建以下非唯一队列:
- `LAST_UPDATED_MORE_THAN_AGO`: **72h**: 只会尝试回收超过此时间默认3天没有尝试过回收的 LFSMetaObject。
- `NUMBER_TO_CHECK_PER_REPO`: **100**: 每个仓库要检查的过期 LFSMetaObject 的最小数量。设置为 `0` 以始终检查所有。
# Git (`git`)
## Git (`git`)
- `PATH`: **""**: Git可执行文件的路径。如果为空Gitea将在PATH环境中搜索。
- `HOME_PATH`: **%(APP_DATA_PATH)s/home**: Git的HOME目录。
@ -1039,10 +1035,11 @@ Gitea 创建以下非唯一队列:
## API (`api`)
- `ENABLE_SWAGGER`: **true**: 是否启用swagger路由 (`/api/swagger`, `/api/v1/swagger`, …)。
- `MAX_RESPONSE_ITEMS`: **50**: 单个页面的最大 Feed.
- `ENABLE_OPENID_SIGNIN`: **false**: 允许使用OpenID登录当设置为`true`时可以通过 `/user/login` 页面进行OpenID登录。
- `DISABLE_REGISTRATION`: **false**: 关闭用户注册。
- `ENABLE_SWAGGER`: **true**: 启用API文档接口 (`/api/swagger`, `/api/v1/swagger`, …). True or false。
- `MAX_RESPONSE_ITEMS`: **50**: API分页的最大单页项目数。
- `DEFAULT_PAGING_NUM`: **30**: API分页的默认分页数。
- `DEFAULT_GIT_TREES_PER_PAGE`: **1000**: Git trees API的默认单页项目数。
- `DEFAULT_MAX_BLOB_SIZE`: **10485760** (10MiB): blobs API的默认最大文件大小。
## OAuth2 (`oauth2`)
@ -1336,8 +1333,8 @@ PROXY_HOSTS = *.github.com
- `MINIO_BASE_PATH`: **actions_log/**Minio存储桶上的基本路径仅在`STORAGE_TYPE`为`minio`时可用。
`DEFAULT_ACTIONS_URL` 指示 Gitea 操作运行程序应该在哪里找到带有相对路径的操作。
例如,`uses: actions/checkout@v3` 表示 `https://github.com/actions/checkout@v3`,因为 `DEFAULT_ACTIONS_URL` 的值为 `github`
它可以更改为 `self`,以使其成为 `root_url_of_your_gitea/actions/checkout@v3`。
例如,`uses: actions/checkout@v4` 表示 `https://github.com/actions/checkout@v4`,因为 `DEFAULT_ACTIONS_URL` 的值为 `github`
它可以更改为 `self`,以使其成为 `root_url_of_your_gitea/actions/checkout@v4`。
请注意,对于大多数情况,不建议使用 `self`,因为它可能使名称在全局范围内产生歧义。
此外,它要求您将所有所需的操作镜像到您的 Gitea 实例,这可能不值得。
@ -1346,7 +1343,7 @@ PROXY_HOSTS = *.github.com
在早期版本(`<= 1.19`)中,`DEFAULT_ACTIONS_URL` 可以设置为任何自定义 URL例如 `https://gitea.com``http://your-git-server,https://gitea.com`,默认值为 `https://gitea.com`
然而,后来的更新删除了这些选项,现在唯一的选项是 `github``self`,默认值为 `github`
但是,如果您想要使用其他 Git 服务器中的操作,您可以在 `uses` 字段中使用完整的 URLGitea 支持此功能GitHub 不支持)。
例如 `uses: https://gitea.com/actions/checkout@v3` 或 `uses: http://your-git-server/actions/checkout@v3`。
例如 `uses: https://gitea.com/actions/checkout@v4` 或 `uses: http://your-git-server/actions/checkout@v4`。
## 其他 (`other`)

View File

@ -42,11 +42,11 @@ Gitea 引用 `custom` 目录中的自定义配置文件来覆盖配置、模板
将自定义的公共文件(比如页面和图片)作为 webroot 放在 `custom/public/` 中来让 Gitea 提供这些自定义内容(符号链接将被追踪)。
举例说明:`image.png` 存放在 `custom/public/`中,那么它可以通过链接 http://gitea.domain.tld/assets/image.png 访问。
举例说明:`image.png` 存放在 `custom/public/assets/`中,那么它可以通过链接 http://gitea.domain.tld/assets/image.png 访问。
## 修改默认头像
替换以下目录中的 png 图片: `custom/public/img/avatar\_default.png`
替换以下目录中的 png 图片: `custom/public/assets/img/avatar\_default.png`
## 自定义 Gitea 页面

View File

@ -27,14 +27,15 @@ GITEA_CUSTOM=/home/gitea/custom ./gitea web
## From Go language
As Gitea is written in Go, it uses some Go variables, such as:
As Gitea is written in Go, it uses some variables that influence the behaviour of Go's runtime, such as:
- `GOOS`
- `GOARCH`
- [`GOPATH`](https://golang.org/cmd/go/#hdr-GOPATH_environment_variable)
- `GOMEMLIMIT`
- `GOGC`
- `GOMAXPROCS`
- `GODEBUG`
For documentation about each of the variables available, refer to the
[official Go documentation](https://golang.org/cmd/go/#hdr-Environment_variables).
[official Go documentation on runtime environment variables](https://pkg.go.dev/runtime#hdr-Environment_Variables).
## Gitea files

View File

@ -194,7 +194,7 @@ ALLOW_DATA_URI_IMAGES = true
}
```
将您的样式表添加到自定义目录中,例如 `custom/public/css/my-style-XXXXX.css`,并使用自定义的头文件 `custom/templates/custom/header.tmpl` 进行导入:
将您的样式表添加到自定义目录中,例如 `custom/public/assets/css/my-style-XXXXX.css`,并使用自定义的头文件 `custom/templates/custom/header.tmpl` 进行导入:
```html
<link rel="stylesheet" href="{{AppSubUrl}}/assets/css/my-style-XXXXX.css" />

View File

@ -48,11 +48,12 @@ We recommend [Google HTML/CSS Style Guide](https://google.github.io/styleguide/h
10. Avoid mixing different events in one event listener, prefer to use individual event listeners for every event.
11. Custom event names are recommended to use `ce-` prefix.
12. Gitea's tailwind-style CSS classes use `gt-` prefix (`gt-relative`), while Gitea's own private framework-level CSS classes use `g-` prefix (`g-modal-confirm`).
13. Avoid inline scripts & styles as much as possible, it's recommended to put JS code into JS files and use CSS classes. If inline scripts & styles are unavoidable, explain the reason why it can't be avoided.
### Accessibility / ARIA
In history, Gitea heavily uses Fomantic UI which is not an accessibility-friendly framework.
Gitea uses some patches to make Fomantic UI more accessible (see the `aria.js` and `aria.md`),
Gitea uses some patches to make Fomantic UI more accessible (see `aria.md` and related JS files),
but there are still many problems which need a lot of work and time to fix.
### Framework Usage

View File

@ -362,7 +362,7 @@ If you are receiving errors on upgrade of Gitea using MySQL that read:
> `ORM engine initialization failed: migrate: do migrate: Error: 1118: Row size too large...`
Please run `gitea convert` or run `ALTER TABLE table_name ROW_FORMAT=dynamic;` for each table in the database.
Please run `gitea doctor convert` or run `ALTER TABLE table_name ROW_FORMAT=dynamic;` for each table in the database.
The underlying problem is that the space allocated for indices by the default row format
is too small. Gitea requires that the `ROWFORMAT` for its tables is `DYNAMIC`.
@ -371,24 +371,6 @@ If you are receiving an error line containing `Error 1071: Specified key was too
then you are attempting to run Gitea on tables which use the ISAM engine. While this may have worked by chance in previous versions of Gitea, it has never been officially supported and
you must use InnoDB. You should run `ALTER TABLE table_name ENGINE=InnoDB;` for each table in the database.
If you are using MySQL 5, another possible fix is
```mysql
SET GLOBAL innodb_file_format=Barracuda;
SET GLOBAL innodb_file_per_table=1;
SET GLOBAL innodb_large_prefix=1;
```
## Why Are Emoji Broken On MySQL
Unfortunately MySQL's `utf8` charset does not completely allow all possible UTF-8 characters, in particular Emoji.
They created a new charset and collation called `utf8mb4` that allows for emoji to be stored but tables which use
the `utf8` charset, and connections which use the `utf8` charset will not use this.
Please run `gitea convert`, or run `ALTER DATABASE database_name CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;`
for the database_name and run `ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;`
for each table in the database.
## Why are Emoji displaying only as placeholders or in monochrome
Gitea requires the system or browser to have one of the supported Emoji fonts installed, which are Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji and Twemoji Mozilla. Generally, the operating system should already provide one of these fonts, but especially on Linux, it may be necessary to install them manually.

View File

@ -189,7 +189,7 @@ Gitea 目前支持三个官方主题,分别是 `gitea-light`、`gitea-dark`
假设我们的主题是 `arc-blue`(这是一个真实的主题,可以在[此问题](https://github.com/go-gitea/gitea/issues/6011)中找到)
将`.css`文件命名为`theme-arc-blue.css`并将其添加到`custom/public/css`文件夹中
将`.css`文件命名为`theme-arc-blue.css`并将其添加到`custom/public/assets/css`文件夹中
通过将`arc-blue`添加到`app.ini`中的`THEMES`列表中,允许用户使用该主题
@ -366,7 +366,7 @@ Gitea 提供了一个子命令`gitea migrate`来初始化数据库,然后您
> `ORM engine initialization failed: migrate: do migrate: Error: 1118: Row size too large...`
请运行`gitea convert`或对数据库中的每个表运行`ALTER TABLE table_name ROW_FORMAT=dynamic;`。
请运行 `gitea doctor convert` 或对数据库中的每个表运行 `ALTER TABLE table_name ROW_FORMAT=dynamic;`
潜在问题是默认行格式分配给每个表的索引空间
太小。Gitea 要求其表的`ROWFORMAT`为`DYNAMIC`。
@ -375,26 +375,6 @@ Gitea 提供了一个子命令`gitea migrate`来初始化数据库,然后您
的错误行,则表示您正在尝试在使用 ISAM 引擎的表上运行 Gitea。尽管在先前版本的 Gitea 中可能是凑巧能够工作的,但它从未得到官方支持,
您必须使用 InnoDB。您应该对数据库中的每个表运行`ALTER TABLE table_name ENGINE=InnoDB;`。
如果您使用的是 MySQL 5另一个可能的修复方法是
```mysql
SET GLOBAL innodb_file_format=Barracuda;
SET GLOBAL innodb_file_per_table=1;
SET GLOBAL innodb_large_prefix=1;
```
## 为什么 MySQL 上的 Emoji 显示错误
不幸的是MySQL 的`utf8`字符集不完全允许所有可能的 UTF-8 字符,特别是 Emoji。
他们创建了一个名为 `utf8mb4`的字符集和校对规则,允许存储 Emoji但使用
utf8 字符集的表和连接将不会使用它。
请运行 `gitea convert` 或对数据库运行`ALTER DATABASE database_name CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;`
并对每个表运行
`ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;`
您还需要将`app.ini`文件中的数据库字符集设置为`CHARSET=utf8mb4`。
## 为什么 Emoji 只显示占位符或单色图像
Gitea 需要系统或浏览器安装其中一个受支持的 Emoji 字体,例如 Apple Color Emoji、Segoe UI Emoji、Segoe UI Symbol、Noto Color Emoji 和 Twemoji Mozilla。通常操作系统应该已经提供了其中一个字体但特别是在 Linux 上,可能需要手动安装它们。

View File

@ -52,6 +52,7 @@ _Symbols used in table:_
| Markdown support | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| CSV support | ✓ | ✘ | ✓ | ✘ | ✘ | ✓ | ✘ | ✘ |
| 'GitHub / GitLab pages' | [⚙️][gitea-pages-server], [⚙️][gitea-caddy-plugin] | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ | ✘ |
| Gists / Snippets | [⚙️][opengist] | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Repo-specific wiki (as a repo itself) | ✓ | ✓ | ✓ | ✓ | ✓ | / | ✘ | ✘ |
| Deploy Tokens | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Repository Tokens with write rights | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
@ -147,3 +148,4 @@ _Symbols used in table:_
[gitea-caddy-plugin]: https://github.com/42wim/caddy-gitea
[gitea-pages-server]: https://codeberg.org/Codeberg/pages-server
[opengist]: https://github.com/thomiceli/opengist

View File

@ -17,7 +17,7 @@ menu:
# Database Preparation
You need a database to use Gitea. Gitea supports PostgreSQL (>= 12), MySQL (>= 8.0), MariaDB, SQLite, and MSSQL (>= 2012 SP4). This page will guide into preparing database. Only PostgreSQL and MySQL will be covered here since those database engines are widely-used in production. If you plan to use SQLite, you can ignore this chapter.
You need a database to use Gitea. Gitea supports PostgreSQL (>= 12), MySQL (>= 8.0), MariaDB (>= 10.4), SQLite (builtin), and MSSQL (>= 2012 SP4). This page will guide into preparing database. Only PostgreSQL and MySQL will be covered here since those database engines are widely-used in production. If you plan to use SQLite, you can ignore this chapter.
If you use an unsupported database version, please [get in touch](/help/support) with us for information on our Extended Support Contracts. We can provide testing and support for older databases and integrate those fixes into the Gitea codebase.
@ -61,10 +61,14 @@ Note: All steps below requires that the database engine of your choice is instal
Replace username and password above as appropriate.
4. Create database with UTF-8 charset and collation. Make sure to use `utf8mb4` charset instead of `utf8` as the former supports all Unicode characters (including emojis) beyond _Basic Multilingual Plane_. Also, collation chosen depending on your expected content. When in doubt, use either `unicode_ci` or `general_ci`.
4. Create database with UTF-8 charset and case-sensitive collation.
`utf8mb4_bin` is a common collation for both MySQL/MariaDB.
When Gitea starts, it will try to find a better collation (`utf8mb4_0900_as_cs` or `uca1400_as_cs`) and alter the database if it is possible.
If you would like to use other collation, you can set `[database].CHARSET_COLLATION` in the `app.ini` file.
```sql
CREATE DATABASE giteadb CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_unicode_ci';
CREATE DATABASE giteadb CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_bin';
```
Replace database name as appropriate.

View File

@ -59,10 +59,12 @@ menu:
根据需要替换上述用户名和密码。
4. 使用 UTF-8 字符集和排序规则创建数据库。确保使用 `**utf8mb4**` 字符集,而不是 `utf8`,因为前者支持 _Basic Multilingual Plane_ 之外的所有 Unicode 字符(包括表情符号)。排序规则根据您预期的内容选择。如果不确定,可以使用 `unicode_ci``general_ci`
4. 使用 UTF-8 字符集和大小写敏感的排序规则创建数据库。
Gitea 启动后会尝试把数据库修改为更合适的字符集,如果你想指定自己的字符集规则,可以在 app.ini 中设置 `[database].CHARSET_COLLATION`
```sql
CREATE DATABASE giteadb CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_unicode_ci';
CREATE DATABASE giteadb CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_bin';
```
根据需要替换数据库名称。

View File

@ -22,13 +22,17 @@ Even though Gitea Actions is designed to be compatible with GitHub Actions, ther
### Absolute action URLs
Gitea Actions supports defining actions via absolute URL, which means that you can use actions from any git repository.
Like `uses: https://github.com/actions/checkout@v3` or `uses: http://your_gitea.com/owner/repo@branch`.
Like `uses: https://github.com/actions/checkout@v4` or `uses: http://your_gitea.com/owner/repo@branch`.
### Actions written in Go
Gitea Actions supports writing actions in Go.
See [Creating Go Actions](https://blog.gitea.com/creating-go-actions/).
### Support the non-standard syntax @yearly, @monthly, @weekly, @daily, @hourly on schedule
Github Actions doesn't support that. https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule
## Unsupported workflows syntax
### `concurrency`
@ -110,6 +114,10 @@ It's ignored by Gitea Actions now.
Pre and Post steps don't have their own section in the job log user interface.
### Services steps
Services steps don't have their own section in the job log user interface.
## Different behavior
### Downloading actions
@ -117,9 +125,9 @@ Pre and Post steps don't have their own section in the job log user interface.
Previously (Pre 1.21.0), `[actions].DEFAULT_ACTIONS_URL` defaulted to `https://gitea.com`.
We have since restricted this option to only allow two values (`github` and `self`).
When set to `github`, the new default, Gitea will download non-fully-qualified actions from `https://github.com`.
For example, if you use `uses: actions/checkout@v3`, it will download the checkout repository from `https://github.com/actions/checkout.git`.
For example, if you use `uses: actions/checkout@v4`, it will download the checkout repository from `https://github.com/actions/checkout.git`.
If you want to download an action from another git hoster, you can use an absolute URL, e.g. `uses: https://gitea.com/actions/checkout@v3`.
If you want to download an action from another git hoster, you can use an absolute URL, e.g. `uses: https://gitea.com/actions/checkout@v4`.
If your Gitea instance is in an intranet or a restricted area, you can set the URL to `self` to only download actions from your own instance by default.
Of course, you can still use absolute URLs in workflows.

View File

@ -22,13 +22,17 @@ menu:
### Action URL绝对路径
Gitea Actions支持通过URL绝对路径定义actions这意味着您可以使用来自任何Git存储库的Actions。
例如,`uses: https://github.com/actions/checkout@v3`或`uses: http://your_gitea.com/owner/repo@branch`。
例如,`uses: https://github.com/actions/checkout@v4`或`uses: http://your_gitea.com/owner/repo@branch`。
### 使用Go编写Actions
Gitea Actions支持使用Go编写Actions。
请参阅[创建Go Actions](https://blog.gitea.com/creating-go-actions/)。
### 支持非标准的调度语法 @yearly, @monthly, @weekly, @daily, @hourly
Github Actions 不支持这些语法,详见: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule
## 不支持的工作流语法
### `concurrency`
@ -116,15 +120,19 @@ Gitea Actions目前不支持此功能。
预处理和后处理步骤在Job日志用户界面中没有自己的用户界面。
### 服务步骤
服务步骤在Job日志用户界面中没有自己的用户界面。
## 不一样的行为
### 下载Actions
`[actions].DEFAULT_ACTIONS_URL` 保持默认值为 `github`Gitea将会从 https://github.com 下载相对路径的actions。比如
如果你使用 `uses: actions/checkout@v3`Gitea将会从 https://github.com/actions/checkout.git 下载这个 actions 项目。
如果你想要从另外一个 Git服务下载actions你只需要使用绝对URL `uses: https://gitea.com/actions/checkout@v3` 来下载。
如果你使用 `uses: actions/checkout@v4`Gitea将会从 https://github.com/actions/checkout.git 下载这个 actions 项目。
如果你想要从另外一个 Git服务下载actions你只需要使用绝对URL `uses: https://gitea.com/actions/checkout@v4` 来下载。
如果你的 Gitea 实例是部署在一个互联网限制的网络中,有可以使用绝对地址来下载 actions。你也可以讲配置项修改为 `[actions].DEFAULT_ACTIONS_URL = self`。这样所有的相对路径的actions引用将不再会从 github.com 去下载,而会从这个 Gitea 实例自己的仓库中去下载。例如: `uses: actions/checkout@v3` 将会从 `[server].ROOT_URL`/actions/checkout.git 这个地址去下载 actions。
如果你的 Gitea 实例是部署在一个互联网限制的网络中,有可以使用绝对地址来下载 actions。你也可以讲配置项修改为 `[actions].DEFAULT_ACTIONS_URL = self`。这样所有的相对路径的actions引用将不再会从 github.com 去下载,而会从这个 Gitea 实例自己的仓库中去下载。例如: `uses: actions/checkout@v4` 将会从 `[server].ROOT_URL`/actions/checkout.git 这个地址去下载 actions。
设置`[actions].DEFAULT_ACTIONS_URL`进行配置。请参阅[配置备忘单](administration/config-cheat-sheet.md#actions-actions)。

View File

@ -95,7 +95,7 @@ The act runner must be able to connect to Gitea to receive tasks and send back t
### Connection 2, job containers to Gitea instance
The job containers have different network namespaces than the runner, even if they are on the same machine.
They need to connect to Gitea to fetch codes if there is `actions/checkout@v3` in the workflow, for example.
They need to connect to Gitea to fetch codes if there is `actions/checkout@v4` in the workflow, for example.
Fetching code is not always necessary to run some jobs, but it is required in most cases.
If you use a loopback address to register a runner, the runner can connect to Gitea when it is on the same machine.
@ -103,7 +103,7 @@ However, if a job container tries to fetch code from localhost, it will fail bec
### Connection 3, act runner to internet
When you use some actions like `actions/checkout@v3`, the act runner downloads the scripts, not the job containers.
When you use some actions like `actions/checkout@v4`, the act runner downloads the scripts, not the job containers.
By default, it downloads from [gitea.com](http://gitea.com/), so it requires access to the internet.
It also downloads some docker images from Docker Hub by default, which also requires internet access.
@ -116,7 +116,7 @@ And [Gitea Container Registry](usage/packages/container.md) can be used as a Doc
### Connection 4, job containers to internet
When using actions such as `actions/setup-go@v4`, it may be necessary to download resources from the internet to set up the Go language environment in job containers.
When using actions such as `actions/setup-go@v5`, it may be necessary to download resources from the internet to set up the Go language environment in job containers.
Therefore, access to the internet is required for the successful completion of these actions.
However, it is optional as well.

View File

@ -96,7 +96,7 @@ act runner 必须能够连接到Gitea以接收任务并发送执行结果回来
### 连接 2Job容器到Gitea实例
即使Job容器位于同一台机器上它们的网络命名空间与Runner不同。
举个例子,如果工作流中包含 `actions/checkout@v3`Job容器需要连接到Gitea来获取代码。
举个例子,如果工作流中包含 `actions/checkout@v4`Job容器需要连接到Gitea来获取代码。
获取代码并不总是运行某些Job所必需的但在大多数情况下是必需的。
如果您使用回环地址注册Runner当Runner与Gitea在同一台机器上时Runner可以连接到Gitea。
@ -104,7 +104,7 @@ act runner 必须能够连接到Gitea以接收任务并发送执行结果回来
### 连接 3act runner到互联网
当您使用诸如 `actions/checkout@v3` 的一些Actions时act runner下载的是脚本而不是Job容器。
当您使用诸如 `actions/checkout@v4` 的一些Actions时act runner下载的是脚本而不是Job容器。
默认情况下,它从[gitea.com](http://gitea.com/)下载,因此需要访问互联网。
它还默认从Docker Hub下载一些Docker镜像这也需要互联网访问。
@ -117,7 +117,7 @@ act runner 必须能够连接到Gitea以接收任务并发送执行结果回来
### 连接 4Job容器到互联网
当使用诸如`actions/setup-go@v4`的Actions时可能需要从互联网下载资源以设置Job容器中的Go语言环境。
当使用诸如`actions/setup-go@v5`的Actions时可能需要从互联网下载资源以设置Job容器中的Go语言环境。
因此成功完成这些Actions需要访问互联网。
然而,这也是可选的。

View File

@ -43,10 +43,10 @@ Still, this is completely optional since both options have the same effect at th
Not yet.
It is technically possible to implement, but we need to discuss whether it is necessary.
## Where will the runner download scripts when using actions such as `actions/checkout@v3`?
## Where will the runner download scripts when using actions such as `actions/checkout@v4`?
You may be aware that there are tens of thousands of [marketplace actions](https://github.com/marketplace?type=actions) in GitHub.
However, when you write `uses: actions/checkout@v3`, it actually downloads the scripts from [gitea.com/actions/checkout](http://gitea.com/actions/checkout) by default (not GitHub).
However, when you write `uses: actions/checkout@v4`, it actually downloads the scripts from [gitea.com/actions/checkout](http://gitea.com/actions/checkout) by default (not GitHub).
This is a mirror of [github.com/actions/checkout](http://github.com/actions/checkout), but it's impossible to mirror all of them.
That's why you may encounter failures when trying to use some actions that haven't been mirrored.

View File

@ -43,10 +43,10 @@ DEFAULT_REPO_UNITS = ...,repo.actions
目前还不可以。
从技术上讲是可以实现的,但我们需要讨论是否有必要。
## 使用`actions/checkout@v3`等Actions时Job容器会从何处下载脚本
## 使用`actions/checkout@v4`等Actions时Job容器会从何处下载脚本
您可能知道GitHub上有成千上万个[Actions市场](https://github.com/marketplace?type=actions)。
然而,当您编写`uses: actions/checkout@v3`时,它实际上默认从[gitea.com/actions/checkout](http://gitea.com/actions/checkout)下载脚本而不是从GitHub下载
然而,当您编写`uses: actions/checkout@v4`时,它实际上默认从[gitea.com/actions/checkout](http://gitea.com/actions/checkout)下载脚本而不是从GitHub下载
这是[github.com/actions/checkout](http://github.com/actions/checkout)的镜像,但无法将它们全部镜像。
这就是为什么在尝试使用尚未镜像的某些Actions时可能会遇到失败的原因。

View File

@ -25,7 +25,7 @@ To avoid confusion, we have clarified the spelling here:
- "Gitea Actions" (with an "s", both words capitalized) is the name of the Gitea feature.
- "GitHub Actions" is the name of the GitHub feature.
- "Actions" could refer to either of the above, depending on the context. So it refers to "Gitea Actions" in this document.
- "action" or "actions" refer to some scripts/plugins to be used, like "actions/checkout@v3" or "actions/cache@v3".
- "action" or "actions" refer to some scripts/plugins to be used, like "actions/checkout@v4" or "actions/cache@v3".
## Runners

View File

@ -25,7 +25,7 @@ Gitea Actions与[GitHub Actions](https://github.com/features/actions)相似且
- "Gitea Actions"(两个单词都大写且带有"s"是Gitea功能的名称。
- "GitHub Actions"是GitHub功能的名称。
- "Actions"根据上下文的不同可以指代以上任意一个。在本文档中指代的是"Gitea Actions"。
- "action"或"actions"指代一些要使用的脚本/插件,比如"actions/checkout@v3"或"actions/cache@v3"。
- "action"或"actions"指代一些要使用的脚本/插件,比如"actions/checkout@v4"或"actions/cache@v3"。
## Runner

View File

@ -113,7 +113,7 @@ jobs:
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by Gitea!"
- run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
- name: Check out repository code
uses: actions/checkout@v3
uses: actions/checkout@v4
- run: echo "💡 The ${{ gitea.repository }} repository has been cloned to the runner."
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
- name: List files in the repository

View File

@ -112,7 +112,7 @@ jobs:
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by Gitea!"
- run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
- name: Check out repository code
uses: actions/checkout@v3
uses: actions/checkout@v4
- run: echo "💡 The ${{ gitea.repository }} repository has been cloned to the runner."
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
- name: List files in the repository

View File

@ -145,25 +145,25 @@ Adds the following fields:
Uses the following fields:
- Group Search Base (optional)
- Group Search Base DN (optional)
- The LDAP DN used for groups.
- Example: `ou=group,dc=mydomain,dc=com`
- Group Name Filter (optional)
- Group Attribute Containing List Of Users (optional)
- The attribute of the group object that lists/contains the group members.
- Example: `memberUid` or `member`
- An LDAP filter declaring how to find valid groups in the above DN.
- Example: `(|(cn=gitea_users)(cn=admins))`
- User Attribute in Group (optional)
- User Attribute Listed in Group (optional)
- The user attribute that is used to reference a user in the group object.
- Example: `uid` if the group objects contains a `member: bender` and the user object contains a `uid: bender`.
- Example: `dn` if the group object contains a `member: uid=bender,ou=users,dc=planetexpress,dc=com`.
- Group Attribute for User (optional)
- The attribute of the group object that lists/contains the group members.
- Example: `memberUid` or `member`
- Verify group membership in LDAP (optional)
- An LDAP filter declaring how to find valid groups in the above DN.
- Example: `(|(cn=gitea_users)(cn=admins))`
## PAM (Pluggable Authentication Module)

View File

@ -27,7 +27,7 @@ The following examples use `apt`.
To register the Debian registry add the url to the list of known apt sources:
```shell
echo "deb https://gitea.example.com/api/packages/{owner}/debian {distribution} {component}" | sudo tee -a /etc/apt/sources.list.d/gitea.list
echo "deb [signed-by=/etc/apt/keyrings/gitea-{owner}.asc] https://gitea.example.com/api/packages/{owner}/debian {distribution} {component}" | sudo tee -a /etc/apt/sources.list.d/gitea.list
```
| Placeholder | Description |
@ -39,13 +39,13 @@ echo "deb https://gitea.example.com/api/packages/{owner}/debian {distribution} {
If the registry is private, provide credentials in the url. You can use a password or a [personal access token](development/api-usage.md#authentication):
```shell
echo "deb https://{username}:{your_password_or_token}@gitea.example.com/api/packages/{owner}/debian {distribution} {component}" | sudo tee -a /etc/apt/sources.list.d/gitea.list
echo "deb [signed-by=/etc/apt/keyrings/gitea-{owner}.asc] https://{username}:{your_password_or_token}@gitea.example.com/api/packages/{owner}/debian {distribution} {component}" | sudo tee -a /etc/apt/sources.list.d/gitea.list
```
The Debian registry files are signed with a PGP key which must be known to apt:
```shell
sudo curl https://gitea.example.com/api/packages/{owner}/debian/repository.key -o /etc/apt/trusted.gpg.d/gitea-{owner}.asc
sudo curl https://gitea.example.com/api/packages/{owner}/debian/repository.key -o /etc/apt/keyrings/gitea-{owner}.asc
```
Afterwards update the local package index:

View File

@ -27,7 +27,7 @@ menu:
要注册 Debian 注册表,请将 URL 添加到已知 `apt` 源列表中:
```shell
echo "deb https://gitea.example.com/api/packages/{owner}/debian {distribution} {component}" | sudo tee -a /etc/apt/sources.list.d/gitea.list
echo "deb [signed-by=/etc/apt/keyrings/gitea-{owner}.asc] https://gitea.example.com/api/packages/{owner}/debian {distribution} {component}" | sudo tee -a /etc/apt/sources.list.d/gitea.list
```
| 占位符 | 描述 |
@ -39,13 +39,13 @@ echo "deb https://gitea.example.com/api/packages/{owner}/debian {distribution} {
如果注册表是私有的,请在 URL 中提供凭据。您可以使用密码或[个人访问令牌](development/api-usage.md#通过-api-认证)
```shell
echo "deb https://{username}:{your_password_or_token}@gitea.example.com/api/packages/{owner}/debian {distribution} {component}" | sudo tee -a /etc/apt/sources.list.d/gitea.list
echo "deb [signed-by=/etc/apt/keyrings/gitea-{owner}.asc] https://{username}:{your_password_or_token}@gitea.example.com/api/packages/{owner}/debian {distribution} {component}" | sudo tee -a /etc/apt/sources.list.d/gitea.list
```
Debian 注册表文件使用 PGP 密钥进行签名,`apt` 必须知道该密钥:
```shell
sudo curl https://gitea.example.com/api/packages/{owner}/debian/repository.key -o /etc/apt/trusted.gpg.d/gitea-{owner}.asc
sudo curl https://gitea.example.com/api/packages/{owner}/debian/repository.key -o /etc/apt/keyrings/gitea-{owner}.asc
```
然后更新本地软件包索引:

View File

@ -24,42 +24,60 @@ The following examples use `dnf`.
## Configuring the package registry
To register the RPM registry add the url to the list of known apt sources:
To register the RPM registry add the url to the list of known sources:
```shell
dnf config-manager --add-repo https://gitea.example.com/api/packages/{owner}/rpm.repo
dnf config-manager --add-repo https://gitea.example.com/api/packages/{owner}/rpm/{group}.repo
```
| Placeholder | Description |
| ----------- | ----------- |
| `owner` | The owner of the package. |
| `group` | Optional: Everything, e.g. empty, `el7`, `rocky/el9`, `test/fc38`. |
Example:
```shell
# without a group
dnf config-manager --add-repo https://gitea.example.com/api/packages/testuser/rpm.repo
# with the group 'centos/el7'
dnf config-manager --add-repo https://gitea.example.com/api/packages/testuser/rpm/centos/el7.repo
```
If the registry is private, provide credentials in the url. You can use a password or a [personal access token](development/api-usage.md#authentication):
```shell
dnf config-manager --add-repo https://{username}:{your_password_or_token}@gitea.example.com/api/packages/{owner}/rpm.repo
dnf config-manager --add-repo https://{username}:{your_password_or_token}@gitea.example.com/api/packages/{owner}/rpm/{group}.repo
```
You have to add the credentials to the urls in the `rpm.repo` file in `/etc/yum.repos.d` too.
You have to add the credentials to the urls in the created `.repo` file in `/etc/yum.repos.d` too.
## Publish a package
To publish a RPM package (`*.rpm`), perform a HTTP PUT operation with the package content in the request body.
```
PUT https://gitea.example.com/api/packages/{owner}/rpm/upload
PUT https://gitea.example.com/api/packages/{owner}/rpm/{group}/upload
```
| Parameter | Description |
| --------- | ----------- |
| `owner` | The owner of the package. |
| `group` | Optional: Everything, e.g. empty, `el7`, `rocky/el9`, `test/fc38`. |
Example request using HTTP Basic authentication:
```shell
# without a group
curl --user your_username:your_password_or_token \
--upload-file path/to/file.rpm \
https://gitea.example.com/api/packages/testuser/rpm/upload
# with the group 'centos/el7'
curl --user your_username:your_password_or_token \
--upload-file path/to/file.rpm \
https://gitea.example.com/api/packages/testuser/rpm/centos/el7/upload
```
If you are using 2FA or OAuth use a [personal access token](development/api-usage.md#authentication) instead of the password.
@ -78,12 +96,13 @@ The server responds with the following HTTP Status codes.
To delete an RPM package perform a HTTP DELETE operation. This will delete the package version too if there is no file left.
```
DELETE https://gitea.example.com/api/packages/{owner}/rpm/{package_name}/{package_version}/{architecture}
DELETE https://gitea.example.com/api/packages/{owner}/rpm/{group}/package/{package_name}/{package_version}/{architecture}
```
| Parameter | Description |
| ----------------- | ----------- |
| `owner` | The owner of the package. |
| `group` | Optional: The package group. |
| `package_name` | The package name. |
| `package_version` | The package version. |
| `architecture` | The package architecture. |
@ -91,8 +110,13 @@ DELETE https://gitea.example.com/api/packages/{owner}/rpm/{package_name}/{packag
Example request using HTTP Basic authentication:
```shell
# without a group
curl --user your_username:your_token_or_password -X DELETE \
https://gitea.example.com/api/packages/testuser/rpm/test-package/1.0.0/x86_64
https://gitea.example.com/api/packages/testuser/rpm/package/test-package/1.0.0/x86_64
# with the group 'centos/el7'
curl --user your_username:your_token_or_password -X DELETE \
https://gitea.example.com/api/packages/testuser/rpm/centos/el7/package/test-package/1.0.0/x86_64
```
The server responds with the following HTTP Status codes.

View File

@ -27,17 +27,18 @@ menu:
要注册RPM注册表请将 URL 添加到已知 `apt` 源列表中:
```shell
dnf config-manager --add-repo https://gitea.example.com/api/packages/{owner}/rpm.repo
dnf config-manager --add-repo https://gitea.example.com/api/packages/{owner}/rpm/{group}.repo
```
| 占位符 | 描述 |
| ------- | -------------- |
| `owner` | 软件包的所有者 |
| 占位符 | 描述 |
| ------- |--------------------------------------|
| `owner` | 软件包的所有者 |
| `group` | 任何名称,例如 `centos/7`、`el-7`、`fc38` |
如果注册表是私有的请在URL中提供凭据。您可以使用密码或[个人访问令牌](development/api-usage.md#通过-api-认证)
```shell
dnf config-manager --add-repo https://{username}:{your_password_or_token}@gitea.example.com/api/packages/{owner}/rpm.repo
dnf config-manager --add-repo https://{username}:{your_password_or_token}@gitea.example.com/api/packages/{owner}/rpm/{group}.repo
```
您还必须将凭据添加到 `/etc/yum.repos.d` 中的 `rpm.repo` 文件中的URL中。
@ -47,19 +48,20 @@ dnf config-manager --add-repo https://{username}:{your_password_or_token}@gitea.
要发布RPM软件包`*.rpm`),请执行带有软件包内容的 HTTP `PUT` 操作。
```
PUT https://gitea.example.com/api/packages/{owner}/rpm/upload
PUT https://gitea.example.com/api/packages/{owner}/rpm/{group}/upload
```
| 参数 | 描述 |
| ------- | -------------- |
| `owner` | 软件包的所有者 |
| ------- |--------------|
| `owner` | 软件包的所有者 |
| `group` | 软件包自定义分组名称 |
使用HTTP基本身份验证的示例请求
```shell
curl --user your_username:your_password_or_token \
--upload-file path/to/file.rpm \
https://gitea.example.com/api/packages/testuser/rpm/upload
https://gitea.example.com/api/packages/testuser/rpm/centos/el7/version/upload
```
如果您使用 2FA 或 OAuth请使用[个人访问令牌](development/api-usage.md#通过-api-认证)替代密码。您无法将具有相同名称的文件两次发布到软件包中。您必须先删除现有的软件包版本。
@ -77,12 +79,13 @@ curl --user your_username:your_password_or_token \
要删除 RPM 软件包,请执行 HTTP `DELETE` 操作。如果没有文件剩余,这也将删除软件包版本。
```
DELETE https://gitea.example.com/api/packages/{owner}/rpm/{package_name}/{package_version}/{architecture}
DELETE https://gitea.example.com/api/packages/{owner}/rpm/{group}/package/{package_name}/{package_version}/{architecture}
```
| 参数 | 描述 |
| ----------------- | -------------- |
| `owner` | 软件包的所有者 |
| `group` | 软件包自定义分组 |
| `package_name` | 软件包名称 |
| `package_version` | 软件包版本 |
| `architecture` | 软件包架构 |
@ -91,7 +94,7 @@ DELETE https://gitea.example.com/api/packages/{owner}/rpm/{package_name}/{packag
```shell
curl --user your_username:your_token_or_password -X DELETE \
https://gitea.example.com/api/packages/testuser/rpm/test-package/1.0.0/x86_64
https://gitea.example.com/api/packages/testuser/rpm/centos/el7/package/test-package/1.0.0/x86_64
```
服务器将以以下HTTP状态码响应

View File

@ -58,7 +58,7 @@ The repository now gets mirrored periodically to the remote repository. You can
To set up a mirror from Gitea to GitHub, you need to follow these steps:
1. Create a [GitHub personal access token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with the *public_repo* box checked.
1. Create a [GitHub personal access token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with the *public_repo* box checked. Also check the **workflow** checkbox in case your repo using act for continuous integration.
2. Create a repository with that name on GitHub. Unlike Gitea, GitHub does not support creating repositories by pushing to the remote. You can also use an existing remote repo if it has the same commit history as your Gitea repo.
3. In the settings of your Gitea repo, fill in the **Git Remote Repository URL**: `https://github.com/<your_github_group>/<your_github_project>.git`.
4. Fill in the **Authorization** fields with your GitHub username and the personal access token as **Password**.

190
go.mod
View File

@ -5,7 +5,7 @@ go 1.21
require (
code.gitea.io/actions-proto-go v0.3.1
code.gitea.io/gitea-vet v0.2.3
code.gitea.io/sdk/gitea v0.16.0
code.gitea.io/sdk/gitea v0.17.1
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
gitea.com/go-chi/binding v0.0.0-20230415142243-04b515c6d669
gitea.com/go-chi/cache v0.2.0
@ -17,12 +17,12 @@ require (
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
github.com/NYTimes/gziphandler v1.1.1
github.com/PuerkitoBio/goquery v1.8.1
github.com/alecthomas/chroma/v2 v2.10.0
github.com/alecthomas/chroma/v2 v2.12.0
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
github.com/blevesearch/bleve/v2 v2.3.10
github.com/bufbuild/connect-go v1.10.0
github.com/buildkite/terminal-to-html/v3 v3.9.1
github.com/caddyserver/certmagic v0.19.2
github.com/buildkite/terminal-to-html/v3 v3.10.1
github.com/caddyserver/certmagic v0.20.0
github.com/chi-middleware/proxy v1.1.1
github.com/denisenkom/go-mssqldb v0.12.3
github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21
@ -35,61 +35,61 @@ require (
github.com/emirpasic/gods v1.18.1
github.com/ethantkoenig/rupture v1.0.1
github.com/felixge/fgprof v0.9.3
github.com/fsnotify/fsnotify v1.6.0
github.com/gliderlabs/ssh v0.3.6-0.20230927171611-ece6c7995e46
github.com/go-ap/activitypub v0.0.0-20231003111253-1fba3772399b
github.com/fsnotify/fsnotify v1.7.0
github.com/gliderlabs/ssh v0.3.6
github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
github.com/go-chi/chi/v5 v5.0.10
github.com/go-chi/chi/v5 v5.0.11
github.com/go-chi/cors v1.2.1
github.com/go-co-op/gocron v1.31.1
github.com/go-co-op/gocron v1.37.0
github.com/go-enry/go-enry/v2 v2.8.6
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e
github.com/go-git/go-billy/v5 v5.5.0
github.com/go-git/go-git/v5 v5.9.0
github.com/go-git/go-git/v5 v5.11.0
github.com/go-ldap/ldap/v3 v3.4.6
github.com/go-sql-driver/mysql v1.7.1
github.com/go-swagger/go-swagger v0.30.5
github.com/go-testfixtures/testfixtures/v3 v3.9.0
github.com/go-webauthn/webauthn v0.8.6
github.com/go-webauthn/webauthn v0.10.0
github.com/gobwas/glob v0.2.3
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
github.com/golang-jwt/jwt/v5 v5.0.0
github.com/google/go-github/v53 v53.2.0
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98
github.com/google/uuid v1.3.1
github.com/gorilla/feeds v1.1.1
github.com/gorilla/sessions v1.2.1
github.com/golang-jwt/jwt/v5 v5.2.0
github.com/google/go-github/v57 v57.0.0
github.com/google/pprof v0.0.0-20240117000934-35fc243c5815
github.com/google/uuid v1.5.0
github.com/gorilla/feeds v1.1.2
github.com/gorilla/sessions v1.2.2
github.com/hashicorp/go-version v1.6.0
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/huandu/xstrings v1.4.0
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
github.com/jhillyerd/enmime v1.0.1
github.com/jhillyerd/enmime v1.1.0
github.com/json-iterator/go v1.1.12
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
github.com/klauspost/compress v1.17.0
github.com/klauspost/cpuid/v2 v2.2.5
github.com/klauspost/compress v1.17.4
github.com/klauspost/cpuid/v2 v2.2.6
github.com/lib/pq v1.10.9
github.com/markbates/goth v1.78.0
github.com/mattn/go-isatty v0.0.19
github.com/mattn/go-sqlite3 v1.14.17
github.com/meilisearch/meilisearch-go v0.25.1
github.com/mattn/go-isatty v0.0.20
github.com/mattn/go-sqlite3 v1.14.19
github.com/meilisearch/meilisearch-go v0.26.1
github.com/mholt/archiver/v3 v3.5.1
github.com/microcosm-cc/bluemonday v1.0.26
github.com/minio/minio-go/v7 v7.0.63
github.com/minio/minio-go/v7 v7.0.66
github.com/minio/sha256-simd v1.0.1
github.com/msteinert/pam v1.2.0
github.com/nektos/act v0.2.52
github.com/niklasfasching/go-org v1.7.0
github.com/olivere/elastic/v7 v7.0.32
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.0-rc5
github.com/opencontainers/image-spec v1.1.0-rc6
github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.4.0
github.com/prometheus/client_golang v1.17.0
github.com/prometheus/client_golang v1.18.0
github.com/quasoft/websspi v1.1.2
github.com/redis/go-redis/v9 v9.2.1
github.com/redis/go-redis/v9 v9.4.0
github.com/robfig/cron/v3 v3.0.1
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
github.com/sassoftware/go-rpmutils v0.2.0
@ -99,59 +99,58 @@ require (
github.com/syndtr/goleveldb v1.0.0
github.com/tstranex/u2f v1.0.0
github.com/ulikunitz/xz v0.5.11
github.com/urfave/cli/v2 v2.25.7
github.com/xanzy/go-gitlab v0.93.1
github.com/urfave/cli/v2 v2.27.1
github.com/xanzy/go-gitlab v0.96.0
github.com/xeipuuv/gojsonschema v1.2.0
github.com/yohcop/openid-go v1.0.1
github.com/yuin/goldmark v1.5.6
github.com/yuin/goldmark v1.6.0
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
github.com/yuin/goldmark-meta v1.1.0
golang.org/x/crypto v0.14.0
golang.org/x/image v0.13.0
golang.org/x/net v0.17.0
golang.org/x/oauth2 v0.13.0
golang.org/x/sys v0.13.0
golang.org/x/text v0.13.0
golang.org/x/tools v0.14.0
google.golang.org/grpc v1.58.3
google.golang.org/protobuf v1.31.0
golang.org/x/crypto v0.18.0
golang.org/x/image v0.15.0
golang.org/x/net v0.20.0
golang.org/x/oauth2 v0.16.0
golang.org/x/sys v0.16.0
golang.org/x/text v0.14.0
golang.org/x/tools v0.17.0
google.golang.org/grpc v1.60.1
google.golang.org/protobuf v1.32.0
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v3 v3.0.1
mvdan.cc/xurls/v2 v2.5.0
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
xorm.io/builder v0.3.13
xorm.io/xorm v1.3.4
xorm.io/xorm v1.3.7
)
require (
cloud.google.com/go/compute v1.23.1 // indirect
cloud.google.com/go/compute v1.23.3 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
dario.cat/mergo v1.0.0 // indirect
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
github.com/ClickHouse/ch-go v0.58.2 // indirect
github.com/ClickHouse/clickhouse-go/v2 v2.14.3 // indirect
github.com/ClickHouse/ch-go v0.61.1 // indirect
github.com/ClickHouse/clickhouse-go/v2 v2.17.1 // indirect
github.com/DataDog/zstd v1.5.5 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c // indirect
github.com/RoaringBitmap/roaring v1.6.0 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/ProtonMail/go-crypto v1.0.0 // indirect
github.com/RoaringBitmap/roaring v1.7.0 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/andybalholm/cascadia v1.3.2 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.9.0 // indirect
github.com/blevesearch/bleve_index_api v1.0.6 // indirect
github.com/blevesearch/geo v0.1.18 // indirect
github.com/bits-and-blooms/bitset v1.13.0 // indirect
github.com/blevesearch/bleve_index_api v1.1.5 // indirect
github.com/blevesearch/geo v0.1.19 // indirect
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
github.com/blevesearch/gtreap v0.1.1 // indirect
github.com/blevesearch/mmap-go v1.0.4 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.1.6 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.2.6 // indirect
github.com/blevesearch/segment v0.9.1 // indirect
github.com/blevesearch/snowballstem v0.9.0 // indirect
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
@ -165,9 +164,9 @@ require (
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/couchbase/go-couchbase v0.1.1 // indirect
github.com/couchbase/gomemcached v0.2.1 // indirect
github.com/couchbase/gomemcached v0.3.0 // indirect
github.com/couchbase/goutils v0.1.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
@ -175,28 +174,28 @@ require (
github.com/davidmz/go-pageant v1.0.2 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fxamacker/cbor/v2 v2.5.0 // indirect
github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
github.com/go-enry/go-oniguruma v1.2.1 // indirect
github.com/go-faster/city v1.0.1 // indirect
github.com/go-faster/errors v0.6.1 // indirect
github.com/go-faster/errors v0.7.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-openapi/analysis v0.21.4 // indirect
github.com/go-openapi/errors v0.20.4 // indirect
github.com/go-openapi/analysis v0.22.2 // indirect
github.com/go-openapi/errors v0.21.0 // indirect
github.com/go-openapi/inflect v0.19.0 // indirect
github.com/go-openapi/jsonpointer v0.20.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/loads v0.21.2 // indirect
github.com/go-openapi/runtime v0.26.0 // indirect
github.com/go-openapi/spec v0.20.9 // indirect
github.com/go-openapi/strfmt v0.21.7 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/go-openapi/validate v0.22.1 // indirect
github.com/go-webauthn/x v0.1.4 // indirect
github.com/go-openapi/jsonpointer v0.20.2 // indirect
github.com/go-openapi/jsonreference v0.20.4 // indirect
github.com/go-openapi/loads v0.21.5 // indirect
github.com/go-openapi/runtime v0.26.2 // indirect
github.com/go-openapi/spec v0.20.14 // indirect
github.com/go-openapi/strfmt v0.22.0 // indirect
github.com/go-openapi/swag v0.22.7 // indirect
github.com/go-openapi/validate v0.22.6 // indirect
github.com/go-webauthn/x v0.1.6 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
@ -207,12 +206,12 @@ require (
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/go-tpm v0.9.0 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/gorilla/handlers v1.5.1 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/gorilla/handlers v1.5.2 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.4 // indirect
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
@ -228,9 +227,8 @@ require (
github.com/markbates/going v1.0.3 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mholt/acmez v1.2.0 // indirect
github.com/miekg/dns v1.1.56 // indirect
github.com/miekg/dns v1.1.58 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
@ -243,20 +241,20 @@ require (
github.com/oklog/ulid v1.3.1 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/paulmach/orb v0.10.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pierrec/lz4/v4 v4.1.18 // indirect
github.com/paulmach/orb v0.11.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/common v0.46.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/rhysd/actionlint v1.6.26 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/rs/xid v1.5.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.3.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
@ -264,37 +262,37 @@ require (
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.2.1 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.10.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.17.0 // indirect
github.com/spf13/viper v1.18.2 // indirect
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/toqueteos/webbrowser v1.2.0 // indirect
github.com/unknwon/com v1.0.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.50.0 // indirect
github.com/valyala/fasthttp v1.51.0 // indirect
github.com/valyala/fastjson v1.6.4 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect
github.com/zeebo/blake3 v0.2.3 // indirect
go.etcd.io/bbolt v1.3.7 // indirect
go.mongodb.org/mongo-driver v1.12.1 // indirect
go.opentelemetry.io/otel v1.19.0 // indirect
go.opentelemetry.io/otel/trace v1.19.0 // indirect
go.etcd.io/bbolt v1.3.8 // indirect
go.mongodb.org/mongo-driver v1.13.1 // indirect
go.opentelemetry.io/otel v1.22.0 // indirect
go.opentelemetry.io/otel/trace v1.22.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
golang.org/x/mod v0.13.0 // indirect
golang.org/x/sync v0.4.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
@ -306,6 +304,8 @@ replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142
replace github.com/nektos/act => gitea.com/gitea/act v0.2.51
replace github.com/gorilla/feeds => github.com/yardenshoham/feeds v0.0.0-20240110072658-f3d0c21c0bd5
exclude github.com/gofrs/uuid v3.2.0+incompatible
exclude github.com/gofrs/uuid v4.0.0+incompatible

670
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -90,19 +90,6 @@ func getArtifactByNameAndPath(ctx context.Context, runID int64, name, fpath stri
return &art, nil
}
// GetArtifactByID returns an artifact by id
func GetArtifactByID(ctx context.Context, id int64) (*ActionArtifact, error) {
var art ActionArtifact
has, err := db.GetEngine(ctx).ID(id).Get(&art)
if err != nil {
return nil, err
} else if !has {
return nil, util.ErrNotExist
}
return &art, nil
}
// UpdateArtifactByID updates an artifact by id
func UpdateArtifactByID(ctx context.Context, id int64, art *ActionArtifact) error {
art.ID = id

View File

@ -46,10 +46,13 @@ type ActionRun struct {
TriggerEvent string // the trigger event defined in the `on` configuration of the triggered workflow
Status Status `xorm:"index"`
Version int `xorm:"version default 0"` // Status could be updated concomitantly, so an optimistic lock is needed
Started timeutil.TimeStamp
Stopped timeutil.TimeStamp
Created timeutil.TimeStamp `xorm:"created"`
Updated timeutil.TimeStamp `xorm:"updated"`
// Started and Stopped is used for recording last run time, if rerun happened, they will be reset to 0
Started timeutil.TimeStamp
Stopped timeutil.TimeStamp
// PreviousDuration is used for recording previous duration
PreviousDuration time.Duration
Created timeutil.TimeStamp `xorm:"created"`
Updated timeutil.TimeStamp `xorm:"updated"`
}
func init() {
@ -118,7 +121,7 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) error {
}
func (run *ActionRun) Duration() time.Duration {
return calculateDuration(run.Started, run.Stopped, run.Status)
return calculateDuration(run.Started, run.Stopped, run.Status) + run.PreviousDuration
}
func (run *ActionRun) GetPushEventPayload() (*api.PushPayload, error) {
@ -168,13 +171,14 @@ func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) err
}
// CancelRunningJobs cancels all running and waiting jobs associated with a specific workflow.
func CancelRunningJobs(ctx context.Context, repoID int64, ref, workflowID string) error {
func CancelRunningJobs(ctx context.Context, repoID int64, ref, workflowID string, event webhook_module.HookEventType) error {
// Find all runs in the specified repository, reference, and workflow with statuses 'Running' or 'Waiting'.
runs, total, err := db.FindAndCount[ActionRun](ctx, FindRunOptions{
RepoID: repoID,
Ref: ref,
WorkflowID: workflowID,
Status: []Status{StatusRunning, StatusWaiting},
RepoID: repoID,
Ref: ref,
WorkflowID: workflowID,
TriggerEvent: event,
Status: []Status{StatusRunning, StatusWaiting},
})
if err != nil {
return err

View File

@ -10,6 +10,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
webhook_module "code.gitea.io/gitea/modules/webhook"
"xorm.io/builder"
)
@ -71,6 +72,7 @@ type FindRunOptions struct {
WorkflowID string
Ref string // the commit/tag/… that caused this workflow
TriggerUserID int64
TriggerEvent webhook_module.HookEventType
Approved bool // not util.OptionalBool, it works only when it's true
Status []Status
}
@ -98,6 +100,9 @@ func (opts FindRunOptions) ToConds() builder.Cond {
if opts.Ref != "" {
cond = cond.And(builder.Eq{"ref": opts.Ref})
}
if opts.TriggerEvent != "" {
cond = cond.And(builder.Eq{"trigger_event": opts.TriggerEvent})
}
return cond
}

View File

@ -51,6 +51,11 @@ type ActionRunner struct {
Deleted timeutil.TimeStamp `xorm:"deleted"`
}
const (
RunnerOfflineTime = time.Minute
RunnerIdleTime = 10 * time.Second
)
// BelongsToOwnerName before calling, should guarantee that all attributes are loaded
func (r *ActionRunner) BelongsToOwnerName() string {
if r.RepoID != 0 {
@ -76,11 +81,12 @@ func (r *ActionRunner) BelongsToOwnerType() types.OwnerType {
return types.OwnerTypeSystemGlobal
}
// if the logic here changed, you should also modify FindRunnerOptions.ToCond
func (r *ActionRunner) Status() runnerv1.RunnerStatus {
if time.Since(r.LastOnline.AsTime()) > time.Minute {
if time.Since(r.LastOnline.AsTime()) > RunnerOfflineTime {
return runnerv1.RunnerStatus_RUNNER_STATUS_OFFLINE
}
if time.Since(r.LastActive.AsTime()) > 10*time.Second {
if time.Since(r.LastActive.AsTime()) > RunnerIdleTime {
return runnerv1.RunnerStatus_RUNNER_STATUS_IDLE
}
return runnerv1.RunnerStatus_RUNNER_STATUS_ACTIVE
@ -153,6 +159,7 @@ type FindRunnerOptions struct {
OwnerID int64
Sort string
Filter string
IsOnline util.OptionalBool
WithAvailable bool // not only runners belong to, but also runners can be used
}
@ -178,6 +185,12 @@ func (opts FindRunnerOptions) ToConds() builder.Cond {
if opts.Filter != "" {
cond = cond.And(builder.Like{"name", opts.Filter})
}
if opts.IsOnline.IsTrue() {
cond = cond.And(builder.Gt{"last_online": time.Now().Add(-RunnerOfflineTime).Unix()})
} else if opts.IsOnline.IsFalse() {
cond = cond.And(builder.Lte{"last_online": time.Now().Add(-RunnerOfflineTime).Unix()})
}
return cond
}
@ -241,7 +254,7 @@ func DeleteRunner(ctx context.Context, id int64) error {
return err
}
_, err := db.GetEngine(ctx).Delete(&ActionRunner{ID: id})
_, err := db.DeleteByID[ActionRunner](ctx, id)
return err
}

View File

@ -5,6 +5,7 @@ package actions
import (
"context"
"fmt"
"time"
"code.gitea.io/gitea/models/db"
@ -118,3 +119,22 @@ func DeleteScheduleTaskByRepo(ctx context.Context, id int64) error {
return committer.Commit()
}
func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) error {
// If actions disabled when there is schedule task, this will remove the outdated schedule tasks
// There is no other place we can do this because the app.ini will be changed manually
if err := DeleteScheduleTaskByRepo(ctx, repo.ID); err != nil {
return fmt.Errorf("DeleteCronTaskByRepo: %v", err)
}
// cancel running cron jobs of this repository and delete old schedules
if err := CancelRunningJobs(
ctx,
repo.ID,
repo.DefaultBranch,
"",
webhook_module.HookEventSchedule,
); err != nil {
return fmt.Errorf("CancelRunningJobs: %v", err)
}
return nil
}

View File

@ -31,8 +31,8 @@ func init() {
}
func (v *ActionVariable) Validate() error {
if v.OwnerID == 0 && v.RepoID == 0 {
return errors.New("the variable is not bound to any scope")
if v.OwnerID != 0 && v.RepoID != 0 {
return errors.New("a variable should not be bound to an owner and a repository at the same time")
}
return nil
}
@ -58,12 +58,8 @@ type FindVariablesOpts struct {
func (opts FindVariablesOpts) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.OwnerID > 0 {
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
}
if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
}
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
return cond
}

View File

@ -446,9 +446,12 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
return nil, 0, err
}
sess := db.GetEngine(ctx).Where(cond).
Select("`action`.*"). // this line will avoid select other joined table's columns
Join("INNER", "repository", "`repository`.id = `action`.repo_id")
sess := db.GetEngine(ctx).Where(cond)
if setting.Database.Type.IsMySQL() {
sess = sess.IndexHint("USE", "JOIN", "IDX_action_c_u_d")
}
sess = sess.Select("`action`.*"). // this line will avoid select other joined table's columns
Join("INNER", "repository", "`repository`.id = `action`.repo_id")
opts.SetDefaultValues()
sess = db.SetSessionPagination(sess, &opts)

View File

@ -59,8 +59,8 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// Mock time
timeutil.Set(time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC))
defer timeutil.Unset()
timeutil.MockSet(time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC))
defer timeutil.MockUnset()
for _, tc := range testCases {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: tc.userID})

View File

@ -11,21 +11,13 @@ import (
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
"github.com/keybase/go-crypto/openpgp"
"github.com/keybase/go-crypto/openpgp/packet"
"xorm.io/xorm"
"xorm.io/builder"
)
// __________________ ________ ____ __.
// / _____/\______ \/ _____/ | |/ _|____ ___.__.
// / \ ___ | ___/ \ ___ | <_/ __ < | |
// \ \_\ \| | \ \_\ \ | | \ ___/\___ |
// \______ /|____| \______ / |____|__ \___ > ____|
// \/ \/ \/ \/\/
// GPGKey represents a GPG key.
type GPGKey struct {
ID int64 `xorm:"pk autoincr"`
@ -54,12 +46,11 @@ func (key *GPGKey) BeforeInsert() {
key.AddedUnix = timeutil.TimeStampNow()
}
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (key *GPGKey) AfterLoad(session *xorm.Session) {
err := session.Where("primary_key_id=?", key.KeyID).Find(&key.SubsKey)
if err != nil {
log.Error("Find Sub GPGkeys[%s]: %v", key.KeyID, err)
func (key *GPGKey) LoadSubKeys(ctx context.Context) error {
if err := db.GetEngine(ctx).Where("primary_key_id=?", key.KeyID).Find(&key.SubsKey); err != nil {
return fmt.Errorf("find Sub GPGkeys[%s]: %v", key.KeyID, err)
}
return nil
}
// PaddedKeyID show KeyID padded to 16 characters
@ -76,20 +67,26 @@ func PaddedKeyID(keyID string) string {
return zeros[0:16-len(keyID)] + keyID
}
// ListGPGKeys returns a list of public keys belongs to given user.
func ListGPGKeys(ctx context.Context, uid int64, listOptions db.ListOptions) ([]*GPGKey, error) {
sess := db.GetEngine(ctx).Table(&GPGKey{}).Where("owner_id=? AND primary_key_id=''", uid)
if listOptions.Page != 0 {
sess = db.SetSessionPagination(sess, &listOptions)
}
keys := make([]*GPGKey, 0, 2)
return keys, sess.Find(&keys)
type FindGPGKeyOptions struct {
db.ListOptions
OwnerID int64
KeyID string
IncludeSubKeys bool
}
// CountUserGPGKeys return number of gpg keys a user own
func CountUserGPGKeys(ctx context.Context, userID int64) (int64, error) {
return db.GetEngine(ctx).Where("owner_id=? AND primary_key_id=''", userID).Count(&GPGKey{})
func (opts FindGPGKeyOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if !opts.IncludeSubKeys {
cond = cond.And(builder.Eq{"primary_key_id": ""})
}
if opts.OwnerID > 0 {
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
}
if opts.KeyID != "" {
cond = cond.And(builder.Eq{"key_id": opts.KeyID})
}
return cond
}
func GetGPGKeyForUserByID(ctx context.Context, ownerID, keyID int64) (*GPGKey, error) {
@ -103,12 +100,6 @@ func GetGPGKeyForUserByID(ctx context.Context, ownerID, keyID int64) (*GPGKey, e
return key, nil
}
// GetGPGKeysByKeyID returns public key by given ID.
func GetGPGKeysByKeyID(ctx context.Context, keyID string) ([]*GPGKey, error) {
keys := make([]*GPGKey, 0, 1)
return keys, db.GetEngine(ctx).Where("key_id=?", keyID).Find(&keys)
}
// GPGKeyToEntity retrieve the imported key and the traducted entity
func GPGKeyToEntity(ctx context.Context, k *GPGKey) (*openpgp.Entity, error) {
impKey, err := GetGPGImportByKeyID(ctx, k.KeyID)

View File

@ -166,7 +166,9 @@ func ParseCommitWithSignature(ctx context.Context, c *git.Commit) *CommitVerific
// Now try to associate the signature with the committer, if present
if committer.ID != 0 {
keys, err := ListGPGKeys(ctx, committer.ID, db.ListOptions{})
keys, err := db.Find[GPGKey](ctx, FindGPGKeyOptions{
OwnerID: committer.ID,
})
if err != nil { // Skipping failed to get gpg keys of user
log.Error("ListGPGKeys: %v", err)
return &CommitVerification{
@ -176,6 +178,15 @@ func ParseCommitWithSignature(ctx context.Context, c *git.Commit) *CommitVerific
}
}
if err := GPGKeyList(keys).LoadSubKeys(ctx); err != nil {
log.Error("LoadSubKeys: %v", err)
return &CommitVerification{
CommittingUser: committer,
Verified: false,
Reason: "gpg.error.failed_retrieval_gpg_keys",
}
}
committerEmailAddresses, _ := user_model.GetEmailAddresses(ctx, committer.ID)
activated := false
for _, e := range committerEmailAddresses {
@ -392,7 +403,10 @@ func hashAndVerifyForKeyID(ctx context.Context, sig *packet.Signature, payload s
if keyID == "" {
return nil
}
keys, err := GetGPGKeysByKeyID(ctx, keyID)
keys, err := db.Find[GPGKey](ctx, FindGPGKeyOptions{
KeyID: keyID,
IncludeSubKeys: true,
})
if err != nil {
log.Error("GetGPGKeysByKeyID: %v", err)
return &CommitVerification{
@ -407,7 +421,10 @@ func hashAndVerifyForKeyID(ctx context.Context, sig *packet.Signature, payload s
for _, key := range keys {
var primaryKeys []*GPGKey
if key.PrimaryKeyID != "" {
primaryKeys, err = GetGPGKeysByKeyID(ctx, key.PrimaryKeyID)
primaryKeys, err = db.Find[GPGKey](ctx, FindGPGKeyOptions{
KeyID: key.PrimaryKeyID,
IncludeSubKeys: true,
})
if err != nil {
log.Error("GetGPGKeysByKeyID: %v", err)
return &CommitVerification{

View File

@ -0,0 +1,38 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package asymkey
import (
"context"
"code.gitea.io/gitea/models/db"
)
type GPGKeyList []*GPGKey
func (keys GPGKeyList) keyIDs() []string {
ids := make([]string, len(keys))
for i, key := range keys {
ids[i] = key.KeyID
}
return ids
}
func (keys GPGKeyList) LoadSubKeys(ctx context.Context) error {
subKeys := make([]*GPGKey, 0, len(keys))
if err := db.GetEngine(ctx).In("primary_key_id", keys.keyIDs()).Find(&subKeys); err != nil {
return err
}
subKeysMap := make(map[string][]*GPGKey, len(subKeys))
for _, key := range subKeys {
subKeysMap[key.PrimaryKeyID] = append(subKeysMap[key.PrimaryKeyID], key)
}
for _, key := range keys {
if subKeys, ok := subKeysMap[key.KeyID]; ok {
key.SubsKey = subKeys
}
}
return nil
}

View File

@ -107,8 +107,9 @@ func VerifyGPGKey(ctx context.Context, ownerID int64, keyID, token, signature st
// VerificationToken returns token for the user that will be valid in minutes (time)
func VerificationToken(user *user_model.User, minutes int) string {
return base.EncodeSha256(
time.Now().Truncate(1*time.Minute).Add(time.Duration(minutes)*time.Minute).Format(time.RFC1123Z) + ":" +
user.CreatedUnix.FormatLong() + ":" +
time.Now().Truncate(1*time.Minute).Add(time.Duration(minutes)*time.Minute).Format(
time.RFC1123Z) + ":" +
user.CreatedUnix.Format(time.RFC1123Z) + ":" +
user.Name + ":" +
user.Email + ":" +
strconv.FormatInt(user.ID, 10))

View File

@ -197,10 +197,10 @@ func (opts FindPublicKeyOptions) ToConds() builder.Cond {
cond = cond.And(builder.Eq{"fingerprint": opts.Fingerprint})
}
if len(opts.KeyTypes) > 0 {
cond = cond.And(builder.In("type", opts.KeyTypes))
cond = cond.And(builder.In("`type`", opts.KeyTypes))
}
if opts.NotKeytype > 0 {
cond = cond.And(builder.Neq{"type": opts.NotKeytype})
cond = cond.And(builder.Neq{"`type`": opts.NotKeytype})
}
if opts.LoginSourceID > 0 {
cond = cond.And(builder.Eq{"login_source_id": opts.LoginSourceID})
@ -227,16 +227,6 @@ func UpdatePublicKeyUpdated(ctx context.Context, id int64) error {
return nil
}
// DeletePublicKeys does the actual key deletion but does not update authorized_keys file.
func DeletePublicKeys(ctx context.Context, keyIDs ...int64) error {
if len(keyIDs) == 0 {
return nil
}
_, err := db.GetEngine(ctx).In("id", keyIDs).Delete(new(PublicKey))
return err
}
// PublicKeysAreExternallyManaged returns whether the provided KeyID represents an externally managed Key
func PublicKeysAreExternallyManaged(ctx context.Context, keys []*PublicKey) ([]bool, error) {
sources := make([]*auth.Source, 0, 5)
@ -322,8 +312,8 @@ func deleteKeysMarkedForDeletion(ctx context.Context, keys []string) (bool, erro
log.Error("SearchPublicKeyByContent: %v", err)
continue
}
if err = DeletePublicKeys(ctx, key.ID); err != nil {
log.Error("deletePublicKeys: %v", err)
if _, err = db.DeleteByID[PublicKey](ctx, key.ID); err != nil {
log.Error("DeleteByID[PublicKey]: %v", err)
continue
}
sshKeysNeedUpdate = true

View File

@ -131,24 +131,22 @@ func AddDeployKey(ctx context.Context, repoID int64, name, content string, readO
}
defer committer.Close()
pkey := &PublicKey{
Fingerprint: fingerprint,
}
has, err := db.GetByBean(ctx, pkey)
pkey, exist, err := db.Get[PublicKey](ctx, builder.Eq{"fingerprint": fingerprint})
if err != nil {
return nil, err
}
if has {
} else if exist {
if pkey.Type != KeyTypeDeploy {
return nil, ErrKeyAlreadyExist{0, fingerprint, ""}
}
} else {
// First time use this deploy key.
pkey.Mode = accessMode
pkey.Type = KeyTypeDeploy
pkey.Content = content
pkey.Name = name
pkey = &PublicKey{
Fingerprint: fingerprint,
Mode: accessMode,
Type: KeyTypeDeploy,
Content: content,
Name: name,
}
if err = addKey(ctx, pkey); err != nil {
return nil, fmt.Errorf("addKey: %w", err)
}
@ -164,11 +162,10 @@ func AddDeployKey(ctx context.Context, repoID int64, name, content string, readO
// GetDeployKeyByID returns deploy key by given ID.
func GetDeployKeyByID(ctx context.Context, id int64) (*DeployKey, error) {
key := new(DeployKey)
has, err := db.GetEngine(ctx).ID(id).Get(key)
key, exist, err := db.GetByID[DeployKey](ctx, id)
if err != nil {
return nil, err
} else if !has {
} else if !exist {
return nil, ErrDeployKeyNotExist{id, 0, 0}
}
return key, nil
@ -176,14 +173,10 @@ func GetDeployKeyByID(ctx context.Context, id int64) (*DeployKey, error) {
// GetDeployKeyByRepo returns deploy key by given public key ID and repository ID.
func GetDeployKeyByRepo(ctx context.Context, keyID, repoID int64) (*DeployKey, error) {
key := &DeployKey{
KeyID: keyID,
RepoID: repoID,
}
has, err := db.GetByBean(ctx, key)
key, exist, err := db.Get[DeployKey](ctx, builder.Eq{"key_id": keyID, "repo_id": repoID})
if err != nil {
return nil, err
} else if !has {
} else if !exist {
return nil, ErrDeployKeyNotExist{0, keyID, repoID}
}
return key, nil

View File

@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/util"
"golang.org/x/crypto/ssh"
"xorm.io/builder"
)
// ___________.__ .__ __
@ -31,9 +32,7 @@ import (
// checkKeyFingerprint only checks if key fingerprint has been used as public key,
// it is OK to use same key as deploy key for multiple repositories/users.
func checkKeyFingerprint(ctx context.Context, fingerprint string) error {
has, err := db.GetByBean(ctx, &PublicKey{
Fingerprint: fingerprint,
})
has, err := db.Exist[PublicKey](ctx, builder.Eq{"fingerprint": fingerprint})
if err != nil {
return err
} else if has {

View File

@ -15,15 +15,6 @@ import (
"code.gitea.io/gitea/modules/util"
)
// __________ .__ .__ .__
// \______ _______|__| ____ ____ |_____________ | | ______
// | ___\_ __ | |/ \_/ ___\| \____ \__ \ | | / ___/
// | | | | \| | | \ \___| | |_> / __ \| |__\___ \
// |____| |__| |__|___| /\___ |__| __(____ |____/____ >
// \/ \/ |__| \/ \/
//
// This file contains functions related to principals
// AddPrincipalKey adds new principal to database and authorized_principals file.
func AddPrincipalKey(ctx context.Context, ownerID int64, content string, authSourceID int64) (*PublicKey, error) {
dbCtx, committer, err := db.TxContext(ctx)
@ -103,17 +94,3 @@ func CheckPrincipalKeyString(ctx context.Context, user *user_model.User, content
return "", fmt.Errorf("didn't match allowed principals: %s", setting.SSH.AuthorizedPrincipalsAllow)
}
// ListPrincipalKeys returns a list of principals belongs to given user.
func ListPrincipalKeys(ctx context.Context, uid int64, listOptions db.ListOptions) ([]*PublicKey, error) {
sess := db.GetEngine(ctx).Where("owner_id = ? AND type = ?", uid, KeyTypePrincipal)
if listOptions.Page != 0 {
sess = db.SetSessionPagination(sess, &listOptions)
keys := make([]*PublicKey, 0, listOptions.PageSize)
return keys, sess.Find(&keys)
}
keys := make([]*PublicKey, 0, 5)
return keys, sess.Find(&keys)
}

View File

@ -30,10 +30,15 @@ func VerifySSHKey(ctx context.Context, ownerID int64, fingerprint, token, signat
return "", ErrKeyNotExist{}
}
if err := sshsig.Verify(bytes.NewBuffer([]byte(token)), []byte(signature), []byte(key.Content), "gitea"); err != nil {
log.Error("Unable to validate token signature. Error: %v", err)
return "", ErrSSHInvalidTokenSignature{
Fingerprint: key.Fingerprint,
err = sshsig.Verify(bytes.NewBuffer([]byte(token)), []byte(signature), []byte(key.Content), "gitea")
if err != nil {
// edge case for Windows based shells that will add CR LF if piped to ssh-keygen command
// see https://github.com/PowerShell/PowerShell/issues/5974
if sshsig.Verify(bytes.NewBuffer([]byte(token+"\r\n")), []byte(signature), []byte(key.Content), "gitea") != nil {
log.Error("Unable to validate token signature. Error: %v", err)
return "", ErrSSHInvalidTokenSignature{
Fingerprint: key.Fingerprint,
}
}
}

View File

@ -9,6 +9,8 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder"
)
// Session represents a session compatible for go-chi session
@ -33,34 +35,31 @@ func UpdateSession(ctx context.Context, key string, data []byte) error {
// ReadSession reads the data for the provided session
func ReadSession(ctx context.Context, key string) (*Session, error) {
session := Session{
Key: key,
}
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return nil, err
}
defer committer.Close()
if has, err := db.GetByBean(ctx, &session); err != nil {
session, exist, err := db.Get[Session](ctx, builder.Eq{"`key`": key})
if err != nil {
return nil, err
} else if !has {
session.Expiry = timeutil.TimeStampNow()
if err := db.Insert(ctx, &session); err != nil {
} else if !exist {
session = &Session{
Key: key,
Expiry: timeutil.TimeStampNow(),
}
if err := db.Insert(ctx, session); err != nil {
return nil, err
}
}
return &session, committer.Commit()
return session, committer.Commit()
}
// ExistSession checks if a session exists
func ExistSession(ctx context.Context, key string) (bool, error) {
session := Session{
Key: key,
}
return db.GetEngine(ctx).Get(&session)
return db.Exist[Session](ctx, builder.Eq{"`key`": key})
}
// DestroySession destroys a session
@ -79,17 +78,13 @@ func RegenerateSession(ctx context.Context, oldKey, newKey string) (*Session, er
}
defer committer.Close()
if has, err := db.GetByBean(ctx, &Session{
Key: newKey,
}); err != nil {
if has, err := db.Exist[Session](ctx, builder.Eq{"`key`": newKey}); err != nil {
return nil, err
} else if has {
return nil, fmt.Errorf("session Key: %s already exists", newKey)
}
if has, err := db.GetByBean(ctx, &Session{
Key: oldKey,
}); err != nil {
if has, err := db.Exist[Session](ctx, builder.Eq{"`key`": oldKey}); err != nil {
return nil, err
} else if !has {
if err := db.Insert(ctx, &Session{
@ -104,14 +99,13 @@ func RegenerateSession(ctx context.Context, oldKey, newKey string) (*Session, er
return nil, err
}
s := Session{
Key: newKey,
}
if _, err := db.GetByBean(ctx, &s); err != nil {
s, _, err := db.Get[Session](ctx, builder.Eq{"`key`": newKey})
if err != nil {
// is not exist, it should be impossible
return nil, err
}
return &s, committer.Commit()
return s, committer.Commit()
}
// CountSessions returns the number of sessions

View File

@ -261,16 +261,12 @@ func (opts FindSourcesOptions) ToConds() builder.Cond {
// IsSSPIEnabled returns true if there is at least one activated login
// source of type LoginSSPI
func IsSSPIEnabled(ctx context.Context) bool {
if !db.HasEngine {
return false
}
exist, err := db.Exists[Source](ctx, FindSourcesOptions{
exist, err := db.Exist[Source](ctx, FindSourcesOptions{
IsActive: util.OptionalBoolTrue,
LoginType: SSPI,
})
}.ToConds())
if err != nil {
log.Error("Active SSPI Sources: %v", err)
log.Error("IsSSPIEnabled: failed to query active SSPI sources: %v", err)
return false
}
return exist

View File

@ -13,7 +13,6 @@ import (
"code.gitea.io/gitea/modules/util"
"github.com/go-webauthn/webauthn/webauthn"
"xorm.io/xorm"
)
// ErrWebAuthnCredentialNotExist represents a "ErrWebAuthnCRedentialNotExist" kind of error.
@ -83,7 +82,7 @@ func (cred *WebAuthnCredential) BeforeUpdate() {
}
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (cred *WebAuthnCredential) AfterLoad(session *xorm.Session) {
func (cred *WebAuthnCredential) AfterLoad() {
cred.LowerName = strings.ToLower(cred.Name)
}

View File

@ -5,6 +5,8 @@ package avatars
import (
"context"
"crypto/md5"
"encoding/hex"
"fmt"
"net/url"
"path"
@ -13,7 +15,6 @@ import (
"sync/atomic"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@ -90,7 +91,9 @@ func DefaultAvatarLink() string {
// HashEmail hashes email address to MD5 string. https://en.gravatar.com/site/implement/hash/
func HashEmail(email string) string {
return base.EncodeMD5(strings.ToLower(strings.TrimSpace(email)))
m := md5.New()
_, _ = m.Write([]byte(strings.ToLower(strings.TrimSpace(email))))
return hex.EncodeToString(m.Sum(nil))
}
// GetEmailForHash converts a provided md5sum to the email

191
models/db/collation.go Normal file
View File

@ -0,0 +1,191 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package db
import (
"errors"
"fmt"
"strings"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"xorm.io/xorm"
"xorm.io/xorm/schemas"
)
type CheckCollationsResult struct {
ExpectedCollation string
AvailableCollation container.Set[string]
DatabaseCollation string
IsCollationCaseSensitive func(s string) bool
CollationEquals func(a, b string) bool
ExistingTableNumber int
InconsistentCollationColumns []string
}
func findAvailableCollationsMySQL(x *xorm.Engine) (ret container.Set[string], err error) {
var res []struct {
Collation string
}
if err = x.SQL("SHOW COLLATION WHERE (Collation = 'utf8mb4_bin') OR (Collation LIKE '%\\_as\\_cs%')").Find(&res); err != nil {
return nil, err
}
ret = make(container.Set[string], len(res))
for _, r := range res {
ret.Add(r.Collation)
}
return ret, nil
}
func findAvailableCollationsMSSQL(x *xorm.Engine) (ret container.Set[string], err error) {
var res []struct {
Name string
}
if err = x.SQL("SELECT * FROM sys.fn_helpcollations() WHERE name LIKE '%[_]CS[_]AS%'").Find(&res); err != nil {
return nil, err
}
ret = make(container.Set[string], len(res))
for _, r := range res {
ret.Add(r.Name)
}
return ret, nil
}
func CheckCollations(x *xorm.Engine) (*CheckCollationsResult, error) {
dbTables, err := x.DBMetas()
if err != nil {
return nil, err
}
res := &CheckCollationsResult{
ExistingTableNumber: len(dbTables),
CollationEquals: func(a, b string) bool { return a == b },
}
var candidateCollations []string
if x.Dialect().URI().DBType == schemas.MYSQL {
if _, err = x.SQL("SELECT @@collation_database").Get(&res.DatabaseCollation); err != nil {
return nil, err
}
res.IsCollationCaseSensitive = func(s string) bool {
return s == "utf8mb4_bin" || strings.HasSuffix(s, "_as_cs")
}
candidateCollations = []string{"utf8mb4_0900_as_cs", "uca1400_as_cs", "utf8mb4_bin"}
res.AvailableCollation, err = findAvailableCollationsMySQL(x)
if err != nil {
return nil, err
}
res.CollationEquals = func(a, b string) bool {
// MariaDB adds the "utf8mb4_" prefix, eg: "utf8mb4_uca1400_as_cs", but not the name "uca1400_as_cs" in "SHOW COLLATION"
// At the moment, it's safe to ignore the database difference, just trim the prefix and compare. It could be fixed easily if there is any problem in the future.
return a == b || strings.TrimPrefix(a, "utf8mb4_") == strings.TrimPrefix(b, "utf8mb4_")
}
} else if x.Dialect().URI().DBType == schemas.MSSQL {
if _, err = x.SQL("SELECT DATABASEPROPERTYEX(DB_NAME(), 'Collation')").Get(&res.DatabaseCollation); err != nil {
return nil, err
}
res.IsCollationCaseSensitive = func(s string) bool {
return strings.HasSuffix(s, "_CS_AS")
}
candidateCollations = []string{"Latin1_General_CS_AS"}
res.AvailableCollation, err = findAvailableCollationsMSSQL(x)
if err != nil {
return nil, err
}
} else {
return nil, nil
}
if res.DatabaseCollation == "" {
return nil, errors.New("unable to get collation for current database")
}
res.ExpectedCollation = setting.Database.CharsetCollation
if res.ExpectedCollation == "" {
for _, collation := range candidateCollations {
if res.AvailableCollation.Contains(collation) {
res.ExpectedCollation = collation
break
}
}
}
if res.ExpectedCollation == "" {
return nil, errors.New("unable to find a suitable collation for current database")
}
allColumnsMatchExpected := true
allColumnsMatchDatabase := true
for _, table := range dbTables {
for _, col := range table.Columns() {
if col.Collation != "" {
allColumnsMatchExpected = allColumnsMatchExpected && res.CollationEquals(col.Collation, res.ExpectedCollation)
allColumnsMatchDatabase = allColumnsMatchDatabase && res.CollationEquals(col.Collation, res.DatabaseCollation)
if !res.IsCollationCaseSensitive(col.Collation) || !res.CollationEquals(col.Collation, res.DatabaseCollation) {
res.InconsistentCollationColumns = append(res.InconsistentCollationColumns, fmt.Sprintf("%s.%s", table.Name, col.Name))
}
}
}
}
// if all columns match expected collation or all match database collation, then it could also be considered as "consistent"
if allColumnsMatchExpected || allColumnsMatchDatabase {
res.InconsistentCollationColumns = nil
}
return res, nil
}
func CheckCollationsDefaultEngine() (*CheckCollationsResult, error) {
return CheckCollations(x)
}
func alterDatabaseCollation(x *xorm.Engine, collation string) error {
if x.Dialect().URI().DBType == schemas.MYSQL {
_, err := x.Exec("ALTER DATABASE CHARACTER SET utf8mb4 COLLATE " + collation)
return err
} else if x.Dialect().URI().DBType == schemas.MSSQL {
// TODO: MSSQL has many limitations on changing database collation, it could fail in many cases.
_, err := x.Exec("ALTER DATABASE CURRENT COLLATE " + collation)
return err
}
return errors.New("unsupported database type")
}
// preprocessDatabaseCollation checks database & table column collation, and alter the database collation if needed
func preprocessDatabaseCollation(x *xorm.Engine) {
r, err := CheckCollations(x)
if err != nil {
log.Error("Failed to check database collation: %v", err)
}
if r == nil {
return // no check result means the database doesn't need to do such check/process (at the moment ....)
}
// try to alter database collation to expected if the database is empty, it might fail in some cases (and it isn't necessary to succeed)
// at the moment, there is no "altering" solution for MSSQL, site admin should manually change the database collation
// and there is a bug https://github.com/go-testfixtures/testfixtures/pull/182 mssql: Invalid object name 'information_schema.tables'.
if !r.CollationEquals(r.DatabaseCollation, r.ExpectedCollation) && r.ExistingTableNumber == 0 && x.Dialect().URI().DBType == schemas.MYSQL {
if err = alterDatabaseCollation(x, r.ExpectedCollation); err != nil {
log.Error("Failed to change database collation to %q: %v", r.ExpectedCollation, err)
} else {
_, _ = x.Exec("SELECT 1") // after "altering", MSSQL's session becomes invalid, so make a simple query to "refresh" the session
if r, err = CheckCollations(x); err != nil {
log.Error("Failed to check database collation again after altering: %v", err) // impossible case
return
}
log.Warn("Current database has been altered to use collation %q", r.DatabaseCollation)
}
}
// check column collation, and show warning/error to end users -- no need to fatal, do not block the startup
if !r.IsCollationCaseSensitive(r.DatabaseCollation) {
log.Warn("Current database is using a case-insensitive collation %q, although Gitea could work with it, there might be some rare cases which don't work as expected.", r.DatabaseCollation)
}
if len(r.InconsistentCollationColumns) > 0 {
log.Error("There are %d table columns using inconsistent collation, they should use %q. Please go to admin panel Self Check page", len(r.InconsistentCollationColumns), r.DatabaseCollation)
}
}

View File

@ -173,9 +173,69 @@ func Exec(ctx context.Context, sqlAndArgs ...any) (sql.Result, error) {
return GetEngine(ctx).Exec(sqlAndArgs...)
}
// GetByBean filled empty fields of the bean according non-empty fields to query in database.
func GetByBean(ctx context.Context, bean any) (bool, error) {
return GetEngine(ctx).Get(bean)
func Get[T any](ctx context.Context, cond builder.Cond) (object *T, exist bool, err error) {
if !cond.IsValid() {
panic("cond is invalid in db.Get(ctx, cond). This should not be possible.")
}
var bean T
has, err := GetEngine(ctx).Where(cond).NoAutoCondition().Get(&bean)
if err != nil {
return nil, false, err
} else if !has {
return nil, false, nil
}
return &bean, true, nil
}
func GetByID[T any](ctx context.Context, id int64) (object *T, exist bool, err error) {
var bean T
has, err := GetEngine(ctx).ID(id).NoAutoCondition().Get(&bean)
if err != nil {
return nil, false, err
} else if !has {
return nil, false, nil
}
return &bean, true, nil
}
func Exist[T any](ctx context.Context, cond builder.Cond) (bool, error) {
if !cond.IsValid() {
panic("cond is invalid in db.Exist(ctx, cond). This should not be possible.")
}
var bean T
return GetEngine(ctx).Where(cond).NoAutoCondition().Exist(&bean)
}
func ExistByID[T any](ctx context.Context, id int64) (bool, error) {
var bean T
return GetEngine(ctx).ID(id).NoAutoCondition().Exist(&bean)
}
// DeleteByID deletes the given bean with the given ID
func DeleteByID[T any](ctx context.Context, id int64) (int64, error) {
var bean T
return GetEngine(ctx).ID(id).NoAutoCondition().NoAutoTime().Delete(&bean)
}
func DeleteByIDs[T any](ctx context.Context, ids ...int64) error {
if len(ids) == 0 {
return nil
}
var bean T
_, err := GetEngine(ctx).In("id", ids).NoAutoCondition().NoAutoTime().Delete(&bean)
return err
}
func Delete[T any](ctx context.Context, opts FindOptions) (int64, error) {
if opts == nil || !opts.ToConds().IsValid() {
panic("opts are empty or invalid in db.Delete(ctx, opts). This should not be possible.")
}
var bean T
return GetEngine(ctx).Where(opts.ToConds()).NoAutoCondition().NoAutoTime().Delete(&bean)
}
// DeleteByBean deletes all records according non-empty fields of the bean as conditions.
@ -183,11 +243,6 @@ func DeleteByBean(ctx context.Context, bean any) (int64, error) {
return GetEngine(ctx).Delete(bean)
}
// DeleteByID deletes the given bean with the given ID
func DeleteByID(ctx context.Context, id int64, bean any) (int64, error) {
return GetEngine(ctx).ID(id).NoAutoCondition().NoAutoTime().Delete(bean)
}
// FindIDs finds the IDs for the given table name satisfying the given condition
// By passing a different value than "id" for "idCol", you can query for foreign IDs, i.e. the repo IDs which satisfy the condition
func FindIDs(ctx context.Context, tableName, idCol string, cond builder.Cond) ([]int64, error) {
@ -264,8 +319,3 @@ func inTransaction(ctx context.Context) (*xorm.Session, bool) {
return nil, false
}
}
func Exists[T any](ctx context.Context, opts FindOptions) (bool, error) {
var bean T
return GetEngine(ctx).Where(opts.ToConds()).Exist(&bean)
}

View File

@ -14,13 +14,18 @@ import (
"xorm.io/xorm/schemas"
)
// ConvertUtf8ToUtf8mb4 converts database and tables from utf8 to utf8mb4 if it's mysql and set ROW_FORMAT=dynamic
func ConvertUtf8ToUtf8mb4() error {
// ConvertDatabaseTable converts database and tables from utf8 to utf8mb4 if it's mysql and set ROW_FORMAT=dynamic
func ConvertDatabaseTable() error {
if x.Dialect().URI().DBType != schemas.MYSQL {
return nil
}
_, err := x.Exec(fmt.Sprintf("ALTER DATABASE `%s` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci", setting.Database.Name))
r, err := CheckCollations(x)
if err != nil {
return err
}
_, err = x.Exec(fmt.Sprintf("ALTER DATABASE `%s` CHARACTER SET utf8mb4 COLLATE %s", setting.Database.Name, r.ExpectedCollation))
if err != nil {
return err
}
@ -30,11 +35,11 @@ func ConvertUtf8ToUtf8mb4() error {
return err
}
for _, table := range tables {
if _, err := x.Exec(fmt.Sprintf("ALTER TABLE `%s` ROW_FORMAT=dynamic;", table.Name)); err != nil {
if _, err := x.Exec(fmt.Sprintf("ALTER TABLE `%s` ROW_FORMAT=dynamic", table.Name)); err != nil {
return err
}
if _, err := x.Exec(fmt.Sprintf("ALTER TABLE `%s` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;", table.Name)); err != nil {
if _, err := x.Exec(fmt.Sprintf("ALTER TABLE `%s` CONVERT TO CHARACTER SET utf8mb4 COLLATE %s", table.Name, r.ExpectedCollation)); err != nil {
return err
}
}

View File

@ -27,9 +27,6 @@ var (
x *xorm.Engine
tables []any
initFuncs []func() error
// HasEngine specifies if we have a xorm.Engine
HasEngine bool
)
// Engine represents a xorm engine or session.
@ -185,6 +182,8 @@ func InitEngineWithMigration(ctx context.Context, migrateFunc func(*xorm.Engine)
return err
}
preprocessDatabaseCollation(x)
// We have to run migrateFunc here in case the user is re-running installation on a previously created DB.
// If we do not then table schemas will be changed and there will be conflicts when the migrations run properly.
//

View File

@ -31,11 +31,11 @@ func TestIterate(t *testing.T) {
assert.EqualValues(t, cnt, repoUnitCnt)
err = db.Iterate(db.DefaultContext, nil, func(ctx context.Context, repoUnit *repo_model.RepoUnit) error {
reopUnit2 := repo_model.RepoUnit{ID: repoUnit.ID}
has, err := db.GetByBean(ctx, &reopUnit2)
has, err := db.ExistByID[repo_model.RepoUnit](ctx, repoUnit.ID)
if err != nil {
return err
} else if !has {
}
if !has {
return db.ErrNotExist{Resource: "repo_unit", ID: repoUnit.ID}
}
assert.EqualValues(t, repoUnit.RepoID, repoUnit.RepoID)

View File

@ -21,17 +21,9 @@ const (
// Paginator is the base for different ListOptions types
type Paginator interface {
GetSkipTake() (skip, take int)
GetStartEnd() (start, end int)
IsListAll() bool
}
// GetPaginatedSession creates a paginated database session
func GetPaginatedSession(p Paginator) *xorm.Session {
skip, take := p.GetSkipTake()
return x.Limit(take, skip)
}
// SetSessionPagination sets pagination for a database session
func SetSessionPagination(sess Engine, p Paginator) *xorm.Session {
skip, take := p.GetSkipTake()
@ -39,13 +31,6 @@ func SetSessionPagination(sess Engine, p Paginator) *xorm.Session {
return sess.Limit(take, skip)
}
// SetEnginePagination sets pagination for a database engine
func SetEnginePagination(e Engine, p Paginator) Engine {
skip, take := p.GetSkipTake()
return e.Limit(take, skip)
}
// ListOptions options to paginate results
type ListOptions struct {
PageSize int
@ -66,13 +51,6 @@ func (opts *ListOptions) GetSkipTake() (skip, take int) {
return (opts.Page - 1) * opts.PageSize, opts.PageSize
}
// GetStartEnd returns the start and end of the ListOptions
func (opts *ListOptions) GetStartEnd() (start, end int) {
start, take := opts.GetSkipTake()
end = start + take
return start, end
}
func (opts ListOptions) GetPage() int {
return opts.Page
}
@ -135,11 +113,6 @@ func (opts *AbsoluteListOptions) GetSkipTake() (skip, take int) {
return opts.skip, opts.take
}
// GetStartEnd returns the start and end values
func (opts *AbsoluteListOptions) GetStartEnd() (start, end int) {
return opts.skip, opts.skip + opts.take
}
// FindOptions represents a find options
type FindOptions interface {
GetPage() int
@ -148,15 +121,34 @@ type FindOptions interface {
ToConds() builder.Cond
}
type JoinFunc func(sess Engine) error
type FindOptionsJoin interface {
ToJoins() []JoinFunc
}
type FindOptionsOrder interface {
ToOrders() string
}
// Find represents a common find function which accept an options interface
func Find[T any](ctx context.Context, opts FindOptions) ([]*T, error) {
sess := GetEngine(ctx).Where(opts.ToConds())
sess := GetEngine(ctx)
if joinOpt, ok := opts.(FindOptionsJoin); ok && len(joinOpt.ToJoins()) > 0 {
for _, joinFunc := range joinOpt.ToJoins() {
if err := joinFunc(sess); err != nil {
return nil, err
}
}
}
sess = sess.Where(opts.ToConds())
page, pageSize := opts.GetPage(), opts.GetPageSize()
if !opts.IsListAll() && pageSize > 0 && page >= 1 {
if !opts.IsListAll() && pageSize > 0 {
if page == 0 {
page = 1
}
sess.Limit(pageSize, (page-1)*pageSize)
}
if newOpt, ok := opts.(FindOptionsOrder); ok && newOpt.ToOrders() != "" {
@ -176,8 +168,17 @@ func Find[T any](ctx context.Context, opts FindOptions) ([]*T, error) {
// Count represents a common count function which accept an options interface
func Count[T any](ctx context.Context, opts FindOptions) (int64, error) {
sess := GetEngine(ctx)
if joinOpt, ok := opts.(FindOptionsJoin); ok && len(joinOpt.ToJoins()) > 0 {
for _, joinFunc := range joinOpt.ToJoins() {
if err := joinFunc(sess); err != nil {
return 0, err
}
}
}
var object T
return GetEngine(ctx).Where(opts.ToConds()).Count(&object)
return sess.Where(opts.ToConds()).Count(&object)
}
// FindAndCount represents a common findandcount function which accept an options interface

View File

@ -52,11 +52,8 @@ func TestPaginator(t *testing.T) {
for _, c := range cases {
skip, take := c.Paginator.GetSkipTake()
start, end := c.Paginator.GetStartEnd()
assert.Equal(t, c.Skip, skip)
assert.Equal(t, c.Take, take)
assert.Equal(t, c.Start, start)
assert.Equal(t, c.End, end)
}
}

View File

@ -57,6 +57,21 @@ func (err ErrUserOwnPackages) Error() string {
return fmt.Sprintf("user still has ownership of packages [uid: %d]", err.UID)
}
// ErrDeleteLastAdminUser represents a "DeleteLastAdminUser" kind of error.
type ErrDeleteLastAdminUser struct {
UID int64
}
// IsErrDeleteLastAdminUser checks if an error is a ErrDeleteLastAdminUser.
func IsErrDeleteLastAdminUser(err error) bool {
_, ok := err.(ErrDeleteLastAdminUser)
return ok
}
func (err ErrDeleteLastAdminUser) Error() string {
return fmt.Sprintf("can not delete the last admin user [uid: %d]", err.UID)
}
// ErrNoPendingRepoTransfer is an error type for repositories without a pending
// transfer request
type ErrNoPendingRepoTransfer struct {

View File

@ -66,7 +66,7 @@
num_followers: 2
num_following: 1
num_stars: 2
num_repos: 14
num_repos: 15
num_teams: 0
num_members: 0
visibility: 0

View File

@ -215,10 +215,9 @@ func DeleteBranches(ctx context.Context, repoID, doerID int64, branchIDs []int64
})
}
// 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, pusherID int64, branchName string, commit *git.Commit) error {
cnt, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repoID, branchName).
// UpdateBranch updates the branch information in the database.
func UpdateBranch(ctx context.Context, repoID, pusherID int64, branchName string, commit *git.Commit) (int64, error) {
return 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: commit.ID.String(),
@ -227,21 +226,6 @@ func UpdateBranch(ctx context.Context, repoID, pusherID int64, branchName string
CommitTime: timeutil.TimeStamp(commit.Committer.When.Unix()),
IsDeleted: false,
})
if err != nil {
return err
}
if cnt > 0 {
return nil
}
return db.Insert(ctx, &Branch{
RepoID: repoID,
Name: branchName,
CommitID: commit.ID.String(),
CommitMessage: commit.Summary(),
PusherID: pusherID,
CommitTime: timeutil.TimeStamp(commit.Committer.When.Unix()),
})
}
// AddDeletedBranch adds a deleted branch to the database
@ -309,7 +293,7 @@ func FindRenamedBranch(ctx context.Context, repoID int64, from string) (branch *
}
// RenameBranch rename a branch
func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to string, gitAction func(isDefault bool) error) (err error) {
func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to string, gitAction func(ctx context.Context, isDefault bool) error) (err error) {
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return err
@ -318,6 +302,17 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
sess := db.GetEngine(ctx)
var branch Branch
exist, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repo.ID, from).Get(&branch)
if err != nil {
return err
} else if !exist || branch.IsDeleted {
return ErrBranchNotExist{
RepoID: repo.ID,
BranchName: from,
}
}
// 1. update branch in database
if n, err := sess.Where("repo_id=? AND name=?", repo.ID, from).Update(&Branch{
Name: to,
@ -373,7 +368,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
}
// 5. do git action
if err = gitAction(isDefault); err != nil {
if err = gitAction(ctx, isDefault); err != nil {
return err
}

View File

@ -13,7 +13,6 @@ import (
"code.gitea.io/gitea/modules/util"
"xorm.io/builder"
"xorm.io/xorm"
)
type BranchList []*Branch
@ -102,7 +101,7 @@ type FindBranchOptions struct {
PullRequestCond builder.Cond
}
func (opts *FindBranchOptions) Cond() builder.Cond {
func (opts FindBranchOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.RepoID > 0 {
@ -145,41 +144,30 @@ func (opts *FindBranchOptions) Cond() builder.Cond {
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 {
func (opts FindBranchOptions) ToOrders() string {
orderBy := opts.OrderBy
if !opts.IsDeletedBranch.IsFalse() { // if deleted branch included, put them at the end
sess = sess.OrderBy("is_deleted ASC")
if orderBy != "" {
orderBy += ", "
}
orderBy += "is_deleted ASC"
}
if opts.OrderBy == "" {
if orderBy == "" {
// the commit_time might be the same, so add the "name" to make sure the order is stable
opts.OrderBy = "commit_time DESC, name ASC"
return "commit_time DESC, name ASC"
}
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)
return orderBy
}
func FindBranchNames(ctx context.Context, opts FindBranchOptions) ([]string, error) {
sess := db.GetEngine(ctx).Select("name").Where(opts.Cond())
sess := db.GetEngine(ctx).Select("name").Where(opts.ToConds())
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 {
if err := sess.Table("branch").OrderBy(opts.ToOrders()).Find(&branches); err != nil {
return nil, err
}
return branches, nil

View File

@ -4,6 +4,7 @@
package git_test
import (
"context"
"testing"
"code.gitea.io/gitea/models/db"
@ -21,6 +22,7 @@ import (
func TestAddDeletedBranch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
assert.EqualValues(t, git.Sha1ObjectFormat.Name(), repo.ObjectFormatName)
firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 1})
assert.True(t, firstBranch.IsDeleted)
@ -38,7 +40,7 @@ func TestAddDeletedBranch(t *testing.T) {
},
}
err := git_model.UpdateBranch(db.DefaultContext, repo.ID, secondBranch.PusherID, secondBranch.Name, commit)
_, err := git_model.UpdateBranch(db.DefaultContext, repo.ID, secondBranch.PusherID, secondBranch.Name, commit)
assert.NoError(t, err)
}
@ -46,10 +48,8 @@ func TestGetDeletedBranches(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
branches, err := git_model.FindBranches(db.DefaultContext, git_model.FindBranchOptions{
ListOptions: db.ListOptions{
ListAll: true,
},
branches, err := db.Find[git_model.Branch](db.DefaultContext, git_model.FindBranchOptions{
ListOptions: db.ListOptionsAll,
RepoID: repo.ID,
IsDeletedBranch: util.OptionalBoolTrue,
})
@ -134,7 +134,7 @@ func TestRenameBranch(t *testing.T) {
}, git_model.WhitelistOptions{}))
assert.NoError(t, committer.Commit())
assert.NoError(t, git_model.RenameBranch(db.DefaultContext, repo1, "master", "main", func(isDefault bool) error {
assert.NoError(t, git_model.RenameBranch(db.DefaultContext, repo1, "master", "main", func(ctx context.Context, isDefault bool) error {
_isDefault = isDefault
return nil
}))

View File

@ -25,7 +25,6 @@ import (
"code.gitea.io/gitea/modules/translation"
"xorm.io/builder"
"xorm.io/xorm"
)
// CommitStatus holds a single Status of a single Commit
@ -38,7 +37,7 @@ type CommitStatus struct {
SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_sha_index)"`
TargetURL string `xorm:"TEXT"`
Description string `xorm:"TEXT"`
ContextHash string `xorm:"char(40) index"`
ContextHash string `xorm:"VARCHAR(64) index"`
Context string `xorm:"TEXT"`
Creator *user_model.User `xorm:"-"`
CreatorID int64
@ -114,7 +113,8 @@ WHEN NOT MATCHED
// GetNextCommitStatusIndex retried 3 times to generate a resource index
func GetNextCommitStatusIndex(ctx context.Context, repoID int64, sha string) (int64, error) {
if !git.IsValidSHAPattern(sha) {
_, err := git.NewIDFromString(sha)
if err != nil {
return 0, git.ErrInvalidSHA{SHA: sha}
}
@ -220,57 +220,42 @@ func CalcCommitStatus(statuses []*CommitStatus) *CommitStatus {
// CommitStatusOptions holds the options for query commit statuses
type CommitStatusOptions struct {
db.ListOptions
RepoID int64
SHA string
State string
SortType string
}
// GetCommitStatuses returns all statuses for a given commit.
func GetCommitStatuses(ctx context.Context, repo *repo_model.Repository, sha string, opts *CommitStatusOptions) ([]*CommitStatus, int64, error) {
if opts.Page <= 0 {
opts.Page = 1
}
if opts.PageSize <= 0 {
opts.Page = setting.ItemsPerPage
func (opts *CommitStatusOptions) ToConds() builder.Cond {
var cond builder.Cond = builder.Eq{
"repo_id": opts.RepoID,
"sha": opts.SHA,
}
countSession := listCommitStatusesStatement(ctx, repo, sha, opts)
countSession = db.SetSessionPagination(countSession, opts)
maxResults, err := countSession.Count(new(CommitStatus))
if err != nil {
log.Error("Count PRs: %v", err)
return nil, maxResults, err
}
statuses := make([]*CommitStatus, 0, opts.PageSize)
findSession := listCommitStatusesStatement(ctx, repo, sha, opts)
findSession = db.SetSessionPagination(findSession, opts)
sortCommitStatusesSession(findSession, opts.SortType)
return statuses, maxResults, findSession.Find(&statuses)
}
func listCommitStatusesStatement(ctx context.Context, repo *repo_model.Repository, sha string, opts *CommitStatusOptions) *xorm.Session {
sess := db.GetEngine(ctx).Where("repo_id = ?", repo.ID).And("sha = ?", sha)
switch opts.State {
case "pending", "success", "error", "failure", "warning":
sess.And("state = ?", opts.State)
cond = cond.And(builder.Eq{
"state": opts.State,
})
}
return sess
return cond
}
func sortCommitStatusesSession(sess *xorm.Session, sortType string) {
switch sortType {
func (opts *CommitStatusOptions) ToOrders() string {
switch opts.SortType {
case "oldest":
sess.Asc("created_unix")
return "created_unix ASC"
case "recentupdate":
sess.Desc("updated_unix")
return "updated_unix DESC"
case "leastupdate":
sess.Asc("updated_unix")
return "updated_unix ASC"
case "leastindex":
sess.Desc("index")
return "`index` DESC"
case "highestindex":
sess.Asc("index")
return "`index` ASC"
default:
sess.Desc("created_unix")
return "created_unix DESC"
}
}
@ -323,7 +308,9 @@ func GetLatestCommitStatusForPairs(ctx context.Context, repoIDsToLatestCommitSHA
Select("max( id ) as id, repo_id").
GroupBy("context_hash, repo_id").OrderBy("max( id ) desc")
sess = db.SetSessionPagination(sess, &listOptions)
if !listOptions.IsListAll() {
sess = db.SetSessionPagination(sess, &listOptions)
}
err := sess.Find(&results)
if err != nil {
@ -423,7 +410,7 @@ func FindRepoRecentCommitStatusContexts(ctx context.Context, repoID int64, befor
type NewCommitStatusOptions struct {
Repo *repo_model.Repository
Creator *user_model.User
SHA string
SHA git.ObjectID
CommitStatus *CommitStatus
}
@ -438,10 +425,6 @@ func NewCommitStatus(ctx context.Context, opts NewCommitStatusOptions) error {
return fmt.Errorf("NewCommitStatus[%s, %s]: no user specified", repoPath, opts.SHA)
}
if _, err := git.NewIDFromString(opts.SHA); err != nil {
return fmt.Errorf("NewCommitStatus[%s, %s]: invalid sha: %w", repoPath, opts.SHA, err)
}
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %w", opts.Repo.ID, opts.Creator.ID, opts.SHA, err)
@ -449,7 +432,7 @@ func NewCommitStatus(ctx context.Context, opts NewCommitStatusOptions) error {
defer committer.Close()
// Get the next Status Index
idx, err := GetNextCommitStatusIndex(ctx, opts.Repo.ID, opts.SHA)
idx, err := GetNextCommitStatusIndex(ctx, opts.Repo.ID, opts.SHA.String())
if err != nil {
return fmt.Errorf("generate commit status index failed: %w", err)
}
@ -457,7 +440,7 @@ func NewCommitStatus(ctx context.Context, opts NewCommitStatusOptions) error {
opts.CommitStatus.Description = strings.TrimSpace(opts.CommitStatus.Description)
opts.CommitStatus.Context = strings.TrimSpace(opts.CommitStatus.Context)
opts.CommitStatus.TargetURL = strings.TrimSpace(opts.CommitStatus.TargetURL)
opts.CommitStatus.SHA = opts.SHA
opts.CommitStatus.SHA = opts.SHA.String()
opts.CommitStatus.CreatorID = opts.Creator.ID
opts.CommitStatus.RepoID = opts.Repo.ID
opts.CommitStatus.Index = idx

View File

@ -22,7 +22,11 @@ func TestGetCommitStatuses(t *testing.T) {
sha1 := "1234123412341234123412341234123412341234"
statuses, maxResults, err := git_model.GetCommitStatuses(db.DefaultContext, repo1, sha1, &git_model.CommitStatusOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 50}})
statuses, maxResults, err := db.FindAndCount[git_model.CommitStatus](db.DefaultContext, &git_model.CommitStatusOptions{
ListOptions: db.ListOptions{Page: 1, PageSize: 50},
RepoID: repo1.ID,
SHA: sha1,
})
assert.NoError(t, err)
assert.Equal(t, int(maxResults), 5)
assert.Len(t, statuses, 5)
@ -46,4 +50,128 @@ func TestGetCommitStatuses(t *testing.T) {
assert.Equal(t, "deploy/awesomeness", statuses[4].Context)
assert.Equal(t, structs.CommitStatusError, statuses[4].State)
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[4].APIURL(db.DefaultContext))
statuses, maxResults, err = db.FindAndCount[git_model.CommitStatus](db.DefaultContext, &git_model.CommitStatusOptions{
ListOptions: db.ListOptions{Page: 2, PageSize: 50},
RepoID: repo1.ID,
SHA: sha1,
})
assert.NoError(t, err)
assert.Equal(t, int(maxResults), 5)
assert.Empty(t, statuses)
}
func Test_CalcCommitStatus(t *testing.T) {
kases := []struct {
statuses []*git_model.CommitStatus
expected *git_model.CommitStatus
}{
{
statuses: []*git_model.CommitStatus{
{
State: structs.CommitStatusPending,
},
},
expected: &git_model.CommitStatus{
State: structs.CommitStatusPending,
},
},
{
statuses: []*git_model.CommitStatus{
{
State: structs.CommitStatusSuccess,
},
{
State: structs.CommitStatusPending,
},
},
expected: &git_model.CommitStatus{
State: structs.CommitStatusPending,
},
},
{
statuses: []*git_model.CommitStatus{
{
State: structs.CommitStatusSuccess,
},
{
State: structs.CommitStatusPending,
},
{
State: structs.CommitStatusSuccess,
},
},
expected: &git_model.CommitStatus{
State: structs.CommitStatusPending,
},
},
{
statuses: []*git_model.CommitStatus{
{
State: structs.CommitStatusError,
},
{
State: structs.CommitStatusPending,
},
{
State: structs.CommitStatusSuccess,
},
},
expected: &git_model.CommitStatus{
State: structs.CommitStatusError,
},
},
{
statuses: []*git_model.CommitStatus{
{
State: structs.CommitStatusWarning,
},
{
State: structs.CommitStatusPending,
},
{
State: structs.CommitStatusSuccess,
},
},
expected: &git_model.CommitStatus{
State: structs.CommitStatusWarning,
},
},
{
statuses: []*git_model.CommitStatus{
{
State: structs.CommitStatusSuccess,
},
{
State: structs.CommitStatusSuccess,
},
{
State: structs.CommitStatusSuccess,
},
},
expected: &git_model.CommitStatus{
State: structs.CommitStatusSuccess,
},
},
{
statuses: []*git_model.CommitStatus{
{
State: structs.CommitStatusFailure,
},
{
State: structs.CommitStatusError,
},
{
State: structs.CommitStatusWarning,
},
},
expected: &git_model.CommitStatus{
State: structs.CommitStatusError,
},
},
}
for _, kase := range kases {
assert.Equal(t, kase.expected, git_model.CalcCommitStatus(kase.statuses))
}
}

View File

@ -135,7 +135,7 @@ var ErrLFSObjectNotExist = db.ErrNotExist{Resource: "LFS Meta object"}
// NewLFSMetaObject stores a given populated LFSMetaObject structure in the database
// if it is not already present.
func NewLFSMetaObject(ctx context.Context, m *LFSMetaObject) (*LFSMetaObject, error) {
func NewLFSMetaObject(ctx context.Context, repoID int64, p lfs.Pointer) (*LFSMetaObject, error) {
var err error
ctx, committer, err := db.TxContext(ctx)
@ -144,16 +144,15 @@ func NewLFSMetaObject(ctx context.Context, m *LFSMetaObject) (*LFSMetaObject, er
}
defer committer.Close()
has, err := db.GetByBean(ctx, m)
m, exist, err := db.Get[LFSMetaObject](ctx, builder.Eq{"repository_id": repoID, "oid": p.Oid})
if err != nil {
return nil, err
}
if has {
} else if exist {
m.Existing = true
return m, committer.Commit()
}
m = &LFSMetaObject{Pointer: p, RepositoryID: repoID}
if err = db.Insert(ctx, m); err != nil {
return nil, err
}

View File

@ -17,13 +17,13 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"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/timeutil"
"code.gitea.io/gitea/modules/util"
"github.com/gobwas/glob"
"github.com/gobwas/glob/syntax"
"xorm.io/builder"
)
var ErrBranchIsProtected = errors.New("branch is protected")
@ -54,6 +54,7 @@ type ProtectedBranch struct {
BlockOnOfficialReviewRequests bool `xorm:"NOT NULL DEFAULT false"`
BlockOnOutdatedBranch bool `xorm:"NOT NULL DEFAULT false"`
DismissStaleApprovals bool `xorm:"NOT NULL DEFAULT false"`
IgnoreStaleApprovals bool `xorm:"NOT NULL DEFAULT false"`
RequireSignedCommits bool `xorm:"NOT NULL DEFAULT false"`
ProtectedFilePatterns string `xorm:"TEXT"`
UnprotectedFilePatterns string `xorm:"TEXT"`
@ -126,7 +127,7 @@ func (protectBranch *ProtectedBranch) CanUserPush(ctx context.Context, user *use
return writeAccess
}
if base.Int64sContains(protectBranch.WhitelistUserIDs, user.ID) {
if slices.Contains(protectBranch.WhitelistUserIDs, user.ID) {
return true
}
@ -149,7 +150,7 @@ func IsUserMergeWhitelisted(ctx context.Context, protectBranch *ProtectedBranch,
return permissionInRepo.CanWrite(unit.TypeCode)
}
if base.Int64sContains(protectBranch.MergeWhitelistUserIDs, userID) {
if slices.Contains(protectBranch.MergeWhitelistUserIDs, userID) {
return true
}
@ -181,7 +182,7 @@ func IsUserOfficialReviewer(ctx context.Context, protectBranch *ProtectedBranch,
return writeAccess, nil
}
if base.Int64sContains(protectBranch.ApprovalsWhitelistUserIDs, user.ID) {
if slices.Contains(protectBranch.ApprovalsWhitelistUserIDs, user.ID) {
return true, nil
}
@ -274,12 +275,11 @@ func (protectBranch *ProtectedBranch) IsUnprotectedFile(patterns []glob.Glob, pa
// GetProtectedBranchRuleByName getting protected branch rule by name
func GetProtectedBranchRuleByName(ctx context.Context, repoID int64, ruleName string) (*ProtectedBranch, error) {
rel := &ProtectedBranch{RepoID: repoID, RuleName: ruleName}
has, err := db.GetByBean(ctx, rel)
// branch_name is legacy name, it actually is rule name
rel, exist, err := db.Get[ProtectedBranch](ctx, builder.Eq{"repo_id": repoID, "branch_name": ruleName})
if err != nil {
return nil, err
}
if !has {
} else if !exist {
return nil, nil
}
return rel, nil
@ -287,12 +287,10 @@ func GetProtectedBranchRuleByName(ctx context.Context, repoID int64, ruleName st
// GetProtectedBranchRuleByID getting protected branch rule by rule ID
func GetProtectedBranchRuleByID(ctx context.Context, repoID, ruleID int64) (*ProtectedBranch, error) {
rel := &ProtectedBranch{ID: ruleID, RepoID: repoID}
has, err := db.GetByBean(ctx, rel)
rel, exist, err := db.Get[ProtectedBranch](ctx, builder.Eq{"repo_id": repoID, "id": ruleID})
if err != nil {
return nil, err
}
if !has {
} else if !exist {
return nil, nil
}
return rel, nil

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