mirror of
https://github.com/go-gitea/gitea.git
synced 2025-11-08 05:02:38 +09:00
Compare commits
73 Commits
v1.20.0-rc
...
v1.20.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e627f161c2 | ||
|
|
de8127e78b | ||
|
|
f7e271ff85 | ||
|
|
186f07bbf7 | ||
|
|
45b1f4dd3b | ||
|
|
026e745b9e | ||
|
|
c334be8284 | ||
|
|
353dcc5ad4 | ||
|
|
7811027ca1 | ||
|
|
abe9c641ce | ||
|
|
052e65e63f | ||
|
|
c1a10be07e | ||
|
|
2b79d3fd52 | ||
|
|
b4460cf541 | ||
|
|
a1bc2aa05e | ||
|
|
d713cf6150 | ||
|
|
012b804a9a | ||
|
|
372b622c2b | ||
|
|
06bcdfe77a | ||
|
|
a5a3c81412 | ||
|
|
ea2c9de3c4 | ||
|
|
348a6bf70d | ||
|
|
68a3961bf1 | ||
|
|
91dadedddf | ||
|
|
32eaba1b40 | ||
|
|
582dcaa12e | ||
|
|
917ca5ded9 | ||
|
|
e595dfeec7 | ||
|
|
03cacf971e | ||
|
|
68e0c802f7 | ||
|
|
09668b2e2e | ||
|
|
04eea29ecb | ||
|
|
511be9fe6e | ||
|
|
24e64fe372 | ||
|
|
4e310133f9 | ||
|
|
491f36d32a | ||
|
|
9111d2d037 | ||
|
|
5510ed34f1 | ||
|
|
39fce5750d | ||
|
|
1f90376041 | ||
|
|
0af6542a34 | ||
|
|
69bdcf41f3 | ||
|
|
e610b0389a | ||
|
|
13ffa287b1 | ||
|
|
e5b684e567 | ||
|
|
64ed262e18 | ||
|
|
f51c8e0008 | ||
|
|
d8a59d5f12 | ||
|
|
1ddfe03131 | ||
|
|
24cf06592e | ||
|
|
0b6f7fb607 | ||
|
|
c27a3af728 | ||
|
|
12aca3ef20 | ||
|
|
2390a46d0f | ||
|
|
51b6a78791 | ||
|
|
e6f62eea70 | ||
|
|
8981f6d0fc | ||
|
|
b2b5c80cb2 | ||
|
|
77db40e084 | ||
|
|
7222bac4e3 | ||
|
|
0eb4ab4246 | ||
|
|
102dcfa3a0 | ||
|
|
614d6df2d8 | ||
|
|
345a25d016 | ||
|
|
e8a7cd4a1d | ||
|
|
6c43b9f6f6 | ||
|
|
40744f8976 | ||
|
|
9d69a4758e | ||
|
|
53747a58a0 | ||
|
|
00ba826360 | ||
|
|
9bbb4d8d6d | ||
|
|
5703a0d3e3 | ||
|
|
85bad22ff8 |
1164
CHANGELOG.md
1164
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -25,7 +25,7 @@ import (
|
||||
|
||||
var optionLogVerbose bool
|
||||
|
||||
func logVerbose(msg string, args ...interface{}) {
|
||||
func logVerbose(msg string, args ...any) {
|
||||
if optionLogVerbose {
|
||||
log.Printf(msg, args...)
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`,
|
||||
},
|
||||
}
|
||||
|
||||
func publicKey(priv interface{}) interface{} {
|
||||
func publicKey(priv any) any {
|
||||
switch k := priv.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return &k.PublicKey
|
||||
@@ -74,7 +74,7 @@ func publicKey(priv interface{}) interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
func pemBlockForKey(priv interface{}) *pem.Block {
|
||||
func pemBlockForKey(priv any) *pem.Block {
|
||||
switch k := priv.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
|
||||
@@ -94,7 +94,7 @@ func runCert(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var priv interface{}
|
||||
var priv any
|
||||
var err error
|
||||
switch c.String("ecdsa-curve") {
|
||||
case "":
|
||||
|
||||
18
cmd/cmd.go
18
cmd/cmd.go
@@ -106,5 +106,21 @@ func setupConsoleLogger(level log.Level, colorize bool, out io.Writer) {
|
||||
WriterOption: log.WriterConsoleOption{Stderr: out == os.Stderr},
|
||||
}
|
||||
writer := log.NewEventWriterConsole("console-default", writeMode)
|
||||
log.GetManager().GetLogger(log.DEFAULT).RemoveAllWriters().AddWriters(writer)
|
||||
log.GetManager().GetLogger(log.DEFAULT).ReplaceAllWriters(writer)
|
||||
}
|
||||
|
||||
// PrepareConsoleLoggerLevel by default, use INFO level for console logger, but some sub-commands (for git/ssh protocol) shouldn't output any log to stdout.
|
||||
// Any log appears in git stdout pipe will break the git protocol, eg: client can't push and hangs forever.
|
||||
func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(*cli.Context) error {
|
||||
return func(c *cli.Context) error {
|
||||
level := defaultLevel
|
||||
if c.Bool("quiet") || c.GlobalBoolT("quiet") {
|
||||
level = log.FATAL
|
||||
}
|
||||
if c.Bool("debug") || c.GlobalBool("debug") || c.Bool("verbose") || c.GlobalBool("verbose") {
|
||||
level = log.TRACE
|
||||
}
|
||||
log.SetConsoleLogger(log.DEFAULT, "console-default", level)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ func setupDoctorDefaultLogger(ctx *cli.Context, colorize bool) {
|
||||
log.FallbackErrorf("unable to create file log writer: %v", err)
|
||||
return
|
||||
}
|
||||
log.GetManager().GetLogger(log.DEFAULT).RemoveAllWriters().AddWriters(writer)
|
||||
log.GetManager().GetLogger(log.DEFAULT).ReplaceAllWriters(writer)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ It can be used for backup and capture Gitea server image to send to maintainer`,
|
||||
},
|
||||
}
|
||||
|
||||
func fatal(format string, args ...interface{}) {
|
||||
func fatal(format string, args ...any) {
|
||||
fmt.Fprintf(os.Stderr, format+"\n", args...)
|
||||
log.Fatal(format, args...)
|
||||
}
|
||||
@@ -236,7 +236,7 @@ func runDump(ctx *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var iface interface{}
|
||||
var iface any
|
||||
if fileName == "-" {
|
||||
iface, err = archiver.ByExtension(fmt.Sprintf(".%s", outType))
|
||||
} else {
|
||||
|
||||
@@ -22,9 +22,9 @@ import (
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// Cmdembedded represents the available extract sub-command.
|
||||
// CmdEmbedded represents the available extract sub-command.
|
||||
var (
|
||||
Cmdembedded = cli.Command{
|
||||
CmdEmbedded = cli.Command{
|
||||
Name: "embedded",
|
||||
Usage: "Extract embedded resources",
|
||||
Description: "A command for extracting embedded resources, like templates and images",
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@@ -32,6 +33,7 @@ var (
|
||||
Name: "hook",
|
||||
Usage: "Delegate commands to corresponding Git hooks",
|
||||
Description: "This should only be called by Git",
|
||||
Before: PrepareConsoleLoggerLevel(log.FATAL),
|
||||
Subcommands: []cli.Command{
|
||||
subcmdHookPreReceive,
|
||||
subcmdHookUpdate,
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
var CmdKeys = cli.Command{
|
||||
Name: "keys",
|
||||
Usage: "This command queries the Gitea database to get the authorized command for a given ssh key fingerprint",
|
||||
Before: PrepareConsoleLoggerLevel(log.FATAL),
|
||||
Action: runKeys,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
|
||||
@@ -178,7 +178,7 @@ func runAddConnLogger(c *cli.Context) error {
|
||||
defer cancel()
|
||||
|
||||
setup(ctx, c.Bool("debug"))
|
||||
vals := map[string]interface{}{}
|
||||
vals := map[string]any{}
|
||||
mode := "conn"
|
||||
vals["net"] = "tcp"
|
||||
if c.IsSet("protocol") {
|
||||
@@ -208,7 +208,7 @@ func runAddFileLogger(c *cli.Context) error {
|
||||
defer cancel()
|
||||
|
||||
setup(ctx, c.Bool("debug"))
|
||||
vals := map[string]interface{}{}
|
||||
vals := map[string]any{}
|
||||
mode := "file"
|
||||
if c.IsSet("filename") {
|
||||
vals["filename"] = c.String("filename")
|
||||
@@ -236,7 +236,7 @@ func runAddFileLogger(c *cli.Context) error {
|
||||
return commonAddLogger(c, mode, vals)
|
||||
}
|
||||
|
||||
func commonAddLogger(c *cli.Context, mode string, vals map[string]interface{}) error {
|
||||
func commonAddLogger(c *cli.Context, mode string, vals map[string]any) error {
|
||||
if len(c.String("level")) > 0 {
|
||||
vals["level"] = log.LevelFromString(c.String("level")).String()
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ var CmdServ = cli.Command{
|
||||
Name: "serv",
|
||||
Usage: "This command should only be called by SSH shell",
|
||||
Description: "Serv provides access auth for repositories",
|
||||
Before: PrepareConsoleLoggerLevel(log.FATAL),
|
||||
Action: runServ,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
@@ -94,7 +95,7 @@ var (
|
||||
|
||||
// fail prints message to stdout, it's mainly used for git serv and git hook commands.
|
||||
// The output will be passed to git client and shown to user.
|
||||
func fail(ctx context.Context, userMessage, logMsgFmt string, args ...interface{}) error {
|
||||
func fail(ctx context.Context, userMessage, logMsgFmt string, args ...any) error {
|
||||
if userMessage == "" {
|
||||
userMessage = "Internal Server Error (no specific error)"
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ var CmdWeb = cli.Command{
|
||||
Usage: "Start Gitea web server",
|
||||
Description: `Gitea web server is the only thing you need to run,
|
||||
and it takes care of all the other things for you`,
|
||||
Before: PrepareConsoleLoggerLevel(log.INFO),
|
||||
Action: runWeb,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
@@ -206,11 +207,6 @@ func servePprof() {
|
||||
}
|
||||
|
||||
func runWeb(ctx *cli.Context) error {
|
||||
if ctx.Bool("verbose") {
|
||||
setupConsoleLogger(log.TRACE, log.CanColorStdout, os.Stdout)
|
||||
} else if ctx.Bool("quiet") {
|
||||
setupConsoleLogger(log.FATAL, log.CanColorStdout, os.Stdout)
|
||||
}
|
||||
defer func() {
|
||||
if panicked := recover(); panicked != nil {
|
||||
log.Fatal("PANIC: %v\n%s", panicked, log.Stack(2))
|
||||
|
||||
@@ -5,7 +5,6 @@ package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@@ -13,9 +12,6 @@ import (
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// EnvironmentPrefix environment variables prefixed with this represent ini values to write
|
||||
const EnvironmentPrefix = "GITEA"
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "environment-to-ini"
|
||||
@@ -70,15 +66,6 @@ func main() {
|
||||
Value: "",
|
||||
Usage: "Destination file to write to",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "clear",
|
||||
Usage: "Clears the matched variables from the environment",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "prefix, p",
|
||||
Value: EnvironmentPrefix,
|
||||
Usage: "Environment prefix to look for - will be suffixed by __ (2 underscores)",
|
||||
},
|
||||
}
|
||||
app.Action = runEnvironmentToIni
|
||||
err := app.Run(os.Args)
|
||||
@@ -99,9 +86,7 @@ func runEnvironmentToIni(c *cli.Context) error {
|
||||
log.Fatal("Failed to load custom conf '%s': %v", setting.CustomConf, err)
|
||||
}
|
||||
|
||||
prefixGitea := c.String("prefix") + "__"
|
||||
suffixFile := "__FILE"
|
||||
changed := setting.EnvironmentToConfig(cfg, prefixGitea, suffixFile, os.Environ())
|
||||
changed := setting.EnvironmentToConfig(cfg, os.Environ())
|
||||
|
||||
// try to save the config file
|
||||
destination := c.String("out")
|
||||
@@ -116,19 +101,5 @@ func runEnvironmentToIni(c *cli.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
// clear Gitea's specific environment variables if requested
|
||||
if c.Bool("clear") {
|
||||
for _, kv := range os.Environ() {
|
||||
idx := strings.IndexByte(kv, '=')
|
||||
if idx < 0 {
|
||||
continue
|
||||
}
|
||||
eKey := kv[:idx]
|
||||
if strings.HasPrefix(eKey, prefixGitea) {
|
||||
_ = os.Unsetenv(eKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2541,8 +2541,8 @@ LEVEL = Info
|
||||
;; Enable/Disable actions capabilities
|
||||
;ENABLED = false
|
||||
;;
|
||||
;; Default address to get action plugins, e.g. the default value means downloading from "https://gitea.com/actions/checkout" for "uses: actions/checkout@v3"
|
||||
;DEFAULT_ACTIONS_URL = https://gitea.com
|
||||
;; Default platform to get action plugins, `github` for `https://github.com`, `self` for the current Gitea instance.
|
||||
;DEFAULT_ACTIONS_URL = github
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
@@ -2,7 +2,15 @@
|
||||
|
||||
if [ ! -d /data/git/.ssh ]; then
|
||||
mkdir -p /data/git/.ssh
|
||||
chmod 700 /data/git/.ssh
|
||||
fi
|
||||
|
||||
# Set the correct permissions on the .ssh directory and authorized_keys file,
|
||||
# or sshd will refuse to use them and lead to clone/push/pull failures.
|
||||
# It could happen when users have copied their data to a new volume and changed the file permission by accident,
|
||||
# and it would be very hard to troubleshoot unless users know how to check the logs of sshd which is started by s6.
|
||||
chmod 700 /data/git/.ssh
|
||||
if [ -f /data/git/.ssh/authorized_keys ]; then
|
||||
chmod 600 /data/git/.ssh/authorized_keys
|
||||
fi
|
||||
|
||||
if [ ! -f /data/git/.ssh/environment ]; then
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#
|
||||
# And place the original in /usr/lib/gitea with working files in /data/gitea
|
||||
GITEA="/app/gitea/gitea"
|
||||
WORK_DIR="/app/gitea"
|
||||
WORK_DIR="/data/gitea"
|
||||
CUSTOM_PATH="/data/gitea"
|
||||
|
||||
# Provide docker defaults
|
||||
|
||||
@@ -108,6 +108,14 @@ Admin operations:
|
||||
- `--all`, `-A`: Force a password change for all users
|
||||
- `--exclude username`, `-e username`: Exclude the given user. Can be set multiple times.
|
||||
- `--unset`: Revoke forced password change for the given users
|
||||
- `generate-access-token`:
|
||||
- Options:
|
||||
- `--username value`, `-u value`: Username. Required.
|
||||
- `--token-name value`, `-t value`: Token name. Required.
|
||||
- `--scopes value`: Comma-separated list of scopes. Scopes follow the format `[read|write]:<block>` or `all` where `<block>` is one of the available visual groups you can see when opening the API page showing the available routes (for example `repo`).
|
||||
- Examples:
|
||||
- `gitea admin user generate-access-token --username myname --token-name mytoken`
|
||||
- `gitea admin user generate-access-token --help`
|
||||
- `regenerate`
|
||||
- Options:
|
||||
- `hooks`: Regenerate Git Hooks for all repositories
|
||||
|
||||
@@ -1374,39 +1374,22 @@ PROXY_HOSTS = *.github.com
|
||||
## Actions (`actions`)
|
||||
|
||||
- `ENABLED`: **false**: Enable/Disable actions capabilities
|
||||
- `DEFAULT_ACTIONS_URL`: **https://gitea.com**: Default address to get action plugins, e.g. the default value means downloading from "<https://gitea.com/actions/checkout>" for "uses: actions/checkout@v3"
|
||||
- `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`
|
||||
|
||||
`DEFAULT_ACTIONS_URL` indicates where should we find the relative path action plugin. i.e. when use an action in a workflow file like
|
||||
`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`.
|
||||
|
||||
```yaml
|
||||
name: versions
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- releases/*
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
```
|
||||
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.
|
||||
Therefore, please use `self` only if you understand what you are doing.
|
||||
|
||||
Now we need to know how to get actions/checkout, this configuration is the default git server to get it. That means we will get the repository via git clone ${DEFAULT_ACTIONS_URL}/actions/checkout and fetch tag v3.
|
||||
|
||||
To help people who don't want to mirror these actions in their git instances, the default value is https://gitea.com
|
||||
To help people run actions totally in their network, they can change the value and copy all necessary action repositories into their git server.
|
||||
|
||||
Of course we should support the form in future PRs like
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: gitea.com/actions/checkout@v3
|
||||
```
|
||||
|
||||
although Github don't support this form.
|
||||
In earlier versions (<= 1.19), `DEFAULT_ACTIONS_URL` cound 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`.
|
||||
|
||||
## Other (`other`)
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ services:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
ports:
|
||||
- "3000:3000"
|
||||
- "222:22"
|
||||
- "2222:2222"
|
||||
+ depends_on:
|
||||
+ - db
|
||||
+
|
||||
@@ -288,7 +288,7 @@ docker-compose up -d
|
||||
|
||||
In addition to the environment variables above, any settings in `app.ini` can be set
|
||||
or overridden with an environment variable of the form: `GITEA__SECTION_NAME__KEY_NAME`.
|
||||
These settings are applied each time the docker container starts.
|
||||
These settings are applied each time the docker container starts, and won't be passed into Gitea's sub-processes.
|
||||
Full information [here](https://github.com/go-gitea/gitea/tree/main/contrib/environment-to-ini).
|
||||
|
||||
These environment variables can be passed to the docker container in `docker-compose.yml`.
|
||||
|
||||
@@ -289,7 +289,7 @@ docker-compose up -d
|
||||
|
||||
In addition to the environment variables above, any settings in `app.ini` can be set
|
||||
or overridden with an environment variable of the form: `GITEA__SECTION_NAME__KEY_NAME`.
|
||||
These settings are applied each time the docker container starts.
|
||||
These settings are applied each time the docker container starts, and won't be passed into Gitea's sub-processes.
|
||||
Full information [here](https://github.com/go-gitea/gitea/tree/master/contrib/environment-to-ini).
|
||||
|
||||
These environment variables can be passed to the docker container in `docker-compose.yml`.
|
||||
|
||||
@@ -164,3 +164,23 @@ Although we would like to provide more options, our limited manpower means that
|
||||
However, both Gitea and act runner are completely open source, so anyone can create a new/better implementation.
|
||||
We support your choice, no matter how you decide.
|
||||
In case you fork act runner to create your own version: Please contribute the changes back if you can and if you think your changes will help others as well.
|
||||
|
||||
## What workflow trigger events does Gitea support?
|
||||
|
||||
All events listed in this table are supported events and are compatible with GitHub.
|
||||
For events supported only by GitHub, see GitHub's [documentation](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows).
|
||||
|
||||
| trigger event | activity types |
|
||||
|-----------------------------|--------------------------------------------------------------------------------------------------------------------------|
|
||||
| create | not applicable |
|
||||
| delete | not applicable |
|
||||
| fork | not applicable |
|
||||
| gollum | not applicable |
|
||||
| push | not applicable |
|
||||
| issues | `opened`, `edited`, `closed`, `reopened`, `assigned`, `unassigned`, `milestoned`, `demilestoned`, `labeled`, `unlabeled` |
|
||||
| issue_comment | `created`, `edited`, `deleted` |
|
||||
| pull_request | `opened`, `edited`, `closed`, `reopened`, `assigned`, `unassigned`, `synchronize`, `labeled`, `unlabeled` |
|
||||
| pull_request_review | `submitted`, `edited` |
|
||||
| pull_request_review_comment | `created`, `edited` |
|
||||
| release | `published`, `edited` |
|
||||
| registry_package | `published` |
|
||||
|
||||
@@ -164,3 +164,23 @@ defaults:
|
||||
然而,无论您如何决定,Gitea 和act runner都是完全开源的,所以任何人都可以创建一个新的/更好的实现。
|
||||
我们支持您的选择,无论您如何决定。
|
||||
如果您选择分支act runner来创建自己的版本,请在您认为您的更改对其他人也有帮助的情况下贡献这些更改。
|
||||
|
||||
## Gitea 支持哪些工作流触发事件?
|
||||
|
||||
表格中列出的所有事件都是支持的,并且与 GitHub 兼容。
|
||||
对于仅 GitHub 支持的事件,请参阅 GitHub 的[文档](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows)。
|
||||
|
||||
| 触发事件 | 活动类型 |
|
||||
|-----------------------------|--------------------------------------------------------------------------------------------------------------------------|
|
||||
| create | 不适用 |
|
||||
| delete | 不适用 |
|
||||
| fork | 不适用 |
|
||||
| gollum | 不适用 |
|
||||
| push | 不适用 |
|
||||
| issues | `opened`, `edited`, `closed`, `reopened`, `assigned`, `unassigned`, `milestoned`, `demilestoned`, `labeled`, `unlabeled` |
|
||||
| issue_comment | `created`, `edited`, `deleted` |
|
||||
| pull_request | `opened`, `edited`, `closed`, `reopened`, `assigned`, `unassigned`, `synchronize`, `labeled`, `unlabeled` |
|
||||
| pull_request_review | `submitted`, `edited` |
|
||||
| pull_request_review_comment | `created`, `edited` |
|
||||
| release | `published`, `edited` |
|
||||
| registry_package | `published` |
|
||||
|
||||
45
main.go
45
main.go
@@ -47,7 +47,9 @@ func init() {
|
||||
// ./gitea -h
|
||||
// ./gitea web help
|
||||
// ./gitea web -h (due to cli lib limitation, this won't call our cmdHelp, so no extra info)
|
||||
// ./gitea admin help auth
|
||||
// ./gitea admin
|
||||
// ./gitea admin help
|
||||
// ./gitea admin auth help
|
||||
// ./gitea -c /tmp/app.ini -h
|
||||
// ./gitea -c /tmp/app.ini help
|
||||
// ./gitea help -c /tmp/app.ini
|
||||
@@ -85,28 +87,36 @@ func main() {
|
||||
app.Description = `By default, Gitea will start serving using the web-server with no argument, which can alternatively be run by running the subcommand "web".`
|
||||
app.Version = Version + formatBuiltWith()
|
||||
app.EnableBashCompletion = true
|
||||
app.Commands = []cli.Command{
|
||||
|
||||
// these sub-commands need to use config file
|
||||
subCmdWithIni := []cli.Command{
|
||||
cmd.CmdWeb,
|
||||
cmd.CmdServ,
|
||||
cmd.CmdHook,
|
||||
cmd.CmdDump,
|
||||
cmd.CmdCert,
|
||||
cmd.CmdAdmin,
|
||||
cmd.CmdGenerate,
|
||||
cmd.CmdMigrate,
|
||||
cmd.CmdKeys,
|
||||
cmd.CmdConvert,
|
||||
cmd.CmdDoctor,
|
||||
cmd.CmdManager,
|
||||
cmd.Cmdembedded,
|
||||
cmd.CmdEmbedded,
|
||||
cmd.CmdMigrateStorage,
|
||||
cmd.CmdDocs,
|
||||
cmd.CmdDumpRepository,
|
||||
cmd.CmdRestoreRepository,
|
||||
cmd.CmdActions,
|
||||
cmdHelp, // TODO: the "help" sub-command was used to show the more information for "work path" and "custom config", in the future, it should avoid doing so
|
||||
}
|
||||
// these sub-commands do not need the config file, and they do not depend on any path or environment variable.
|
||||
subCmdStandalone := []cli.Command{
|
||||
cmd.CmdCert,
|
||||
cmd.CmdGenerate,
|
||||
cmd.CmdDocs,
|
||||
}
|
||||
|
||||
// default configuration flags
|
||||
// shared configuration flags, they are for global and for each sub-command at the same time
|
||||
// eg: such command is valid: "./gitea --config /tmp/app.ini web --config /tmp/app.ini", while it's discouraged indeed
|
||||
// keep in mind that the short flags like "-C", "-c" and "-w" are globally polluted, they can't be used for sub-commands anymore.
|
||||
globalFlags := []cli.Flag{
|
||||
cli.HelpFlag,
|
||||
cli.StringFlag{
|
||||
@@ -126,13 +136,15 @@ func main() {
|
||||
|
||||
// Set the default to be equivalent to cmdWeb and add the default flags
|
||||
app.Flags = append(app.Flags, globalFlags...)
|
||||
app.Flags = append(app.Flags, cmd.CmdWeb.Flags...)
|
||||
app.Flags = append(app.Flags, cmd.CmdWeb.Flags...) // TODO: the web flags polluted the global flags, they are not really global flags
|
||||
app.Action = prepareWorkPathAndCustomConf(cmd.CmdWeb.Action)
|
||||
app.HideHelp = true // use our own help action to show helps (with more information like default config)
|
||||
app.Commands = append(app.Commands, cmdHelp)
|
||||
for i := range app.Commands {
|
||||
prepareSubcommands(&app.Commands[i], globalFlags)
|
||||
app.Before = cmd.PrepareConsoleLoggerLevel(log.INFO)
|
||||
for i := range subCmdWithIni {
|
||||
prepareSubcommands(&subCmdWithIni[i], globalFlags)
|
||||
}
|
||||
app.Commands = append(app.Commands, subCmdWithIni...)
|
||||
app.Commands = append(app.Commands, subCmdStandalone...)
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
@@ -156,11 +168,7 @@ func prepareSubcommands(command *cli.Command, defaultFlags []cli.Flag) {
|
||||
|
||||
// prepareWorkPathAndCustomConf wraps the Action to prepare the work path and custom config
|
||||
// It can't use "Before", because each level's sub-command's Before will be called one by one, so the "init" would be done multiple times
|
||||
func prepareWorkPathAndCustomConf(a any) func(ctx *cli.Context) error {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
action := a.(func(*cli.Context) error)
|
||||
func prepareWorkPathAndCustomConf(action any) func(ctx *cli.Context) error {
|
||||
return func(ctx *cli.Context) error {
|
||||
var args setting.ArgWorkPathAndCustomConf
|
||||
curCtx := ctx
|
||||
@@ -177,10 +185,11 @@ func prepareWorkPathAndCustomConf(a any) func(ctx *cli.Context) error {
|
||||
curCtx = curCtx.Parent()
|
||||
}
|
||||
setting.InitWorkPathAndCommonConfig(os.Getenv, args)
|
||||
if ctx.Bool("help") {
|
||||
if ctx.Bool("help") || action == nil {
|
||||
// the default behavior of "urfave/cli": "nil action" means "show help"
|
||||
return cmdHelp.Action.(func(ctx *cli.Context) error)(ctx)
|
||||
}
|
||||
return action(ctx)
|
||||
return action.(func(*cli.Context) error)(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -346,6 +346,9 @@ func UpdateTask(ctx context.Context, task *ActionTask, cols ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateTaskByState updates the task by the state.
|
||||
// It will always update the task if the state is not final, even there is no change.
|
||||
// So it will update ActionTask.Updated to avoid the task being judged as a zombie task.
|
||||
func UpdateTaskByState(ctx context.Context, state *runnerv1.TaskState) (*ActionTask, error) {
|
||||
stepStates := map[int64]*runnerv1.StepState{}
|
||||
for _, v := range state.Steps {
|
||||
@@ -386,6 +389,12 @@ func UpdateTaskByState(ctx context.Context, state *runnerv1.TaskState) (*ActionT
|
||||
}, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Force update ActionTask.Updated to avoid the task being judged as a zombie task
|
||||
task.Updated = timeutil.TimeStampNow()
|
||||
if err := UpdateTask(ctx, task, "updated"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := task.LoadAttributes(ctx); err != nil {
|
||||
|
||||
@@ -44,7 +44,7 @@ func init() {
|
||||
// TranslatableMessage represents JSON struct that can be translated with a Locale
|
||||
type TranslatableMessage struct {
|
||||
Format string
|
||||
Args []interface{} `json:"omitempty"`
|
||||
Args []any `json:"omitempty"`
|
||||
}
|
||||
|
||||
// LoadRepo loads repository of the task
|
||||
|
||||
@@ -47,7 +47,7 @@ var sshOpLocker sync.Mutex
|
||||
// AuthorizedStringForKey creates the authorized keys string appropriate for the provided key
|
||||
func AuthorizedStringForKey(key *PublicKey) string {
|
||||
sb := &strings.Builder{}
|
||||
_ = setting.SSH.AuthorizedKeysCommandTemplateTemplate.Execute(sb, map[string]interface{}{
|
||||
_ = setting.SSH.AuthorizedKeysCommandTemplateTemplate.Execute(sb, map[string]any{
|
||||
"AppPath": util.ShellEscape(setting.AppPath),
|
||||
"AppWorkPath": util.ShellEscape(setting.AppWorkPath),
|
||||
"CustomConf": util.ShellEscape(setting.CustomConf),
|
||||
@@ -175,7 +175,7 @@ func RewriteAllPublicKeys() error {
|
||||
|
||||
// RegeneratePublicKeys regenerates the authorized_keys file
|
||||
func RegeneratePublicKeys(ctx context.Context, t io.StringWriter) error {
|
||||
if err := db.GetEngine(ctx).Where("type != ?", KeyTypePrincipal).Iterate(new(PublicKey), func(idx int, bean interface{}) (err error) {
|
||||
if err := db.GetEngine(ctx).Where("type != ?", KeyTypePrincipal).Iterate(new(PublicKey), func(idx int, bean any) (err error) {
|
||||
_, err = t.WriteString((bean.(*PublicKey)).AuthorizedString())
|
||||
return err
|
||||
}); err != nil {
|
||||
|
||||
@@ -97,7 +97,7 @@ func RewriteAllPrincipalKeys(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func regeneratePrincipalKeys(ctx context.Context, t io.StringWriter) error {
|
||||
if err := db.GetEngine(ctx).Where("type = ?", KeyTypePrincipal).Iterate(new(PublicKey), func(idx int, bean interface{}) (err error) {
|
||||
if err := db.GetEngine(ctx).Where("type = ?", KeyTypePrincipal).Iterate(new(PublicKey), func(idx int, bean any) (err error) {
|
||||
_, err = t.WriteString((bean.(*PublicKey)).AuthorizedString())
|
||||
return err
|
||||
}); err != nil {
|
||||
|
||||
@@ -52,7 +52,7 @@ func (ctx *Context) Engine() Engine {
|
||||
}
|
||||
|
||||
// Value shadows Value for context.Context but allows us to get ourselves and an Engined object
|
||||
func (ctx *Context) Value(key interface{}) interface{} {
|
||||
func (ctx *Context) Value(key any) any {
|
||||
if key == enginedContextKey {
|
||||
return ctx
|
||||
}
|
||||
@@ -163,28 +163,28 @@ func txWithNoCheck(parentCtx context.Context, f func(ctx context.Context) error)
|
||||
}
|
||||
|
||||
// Insert inserts records into database
|
||||
func Insert(ctx context.Context, beans ...interface{}) error {
|
||||
func Insert(ctx context.Context, beans ...any) error {
|
||||
_, err := GetEngine(ctx).Insert(beans...)
|
||||
return err
|
||||
}
|
||||
|
||||
// Exec executes a sql with args
|
||||
func Exec(ctx context.Context, sqlAndArgs ...interface{}) (sql.Result, error) {
|
||||
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 interface{}) (bool, error) {
|
||||
func GetByBean(ctx context.Context, bean any) (bool, error) {
|
||||
return GetEngine(ctx).Get(bean)
|
||||
}
|
||||
|
||||
// DeleteByBean deletes all records according non-empty fields of the bean as conditions.
|
||||
func DeleteByBean(ctx context.Context, bean interface{}) (int64, error) {
|
||||
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 interface{}) (int64, error) {
|
||||
func DeleteByID(ctx context.Context, id int64, bean any) (int64, error) {
|
||||
return GetEngine(ctx).ID(id).NoAutoTime().Delete(bean)
|
||||
}
|
||||
|
||||
@@ -203,13 +203,13 @@ func FindIDs(ctx context.Context, tableName, idCol string, cond builder.Cond) ([
|
||||
|
||||
// DecrByIDs decreases the given column for entities of the "bean" type with one of the given ids by one
|
||||
// Timestamps of the entities won't be updated
|
||||
func DecrByIDs(ctx context.Context, ids []int64, decrCol string, bean interface{}) error {
|
||||
func DecrByIDs(ctx context.Context, ids []int64, decrCol string, bean any) error {
|
||||
_, err := GetEngine(ctx).Decr(decrCol).In("id", ids).NoAutoCondition().NoAutoTime().Update(bean)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteBeans deletes all given beans, beans must contain delete conditions.
|
||||
func DeleteBeans(ctx context.Context, beans ...interface{}) (err error) {
|
||||
func DeleteBeans(ctx context.Context, beans ...any) (err error) {
|
||||
e := GetEngine(ctx)
|
||||
for i := range beans {
|
||||
if _, err = e.Delete(beans[i]); err != nil {
|
||||
@@ -220,7 +220,7 @@ func DeleteBeans(ctx context.Context, beans ...interface{}) (err error) {
|
||||
}
|
||||
|
||||
// TruncateBeans deletes all given beans, beans may contain delete conditions.
|
||||
func TruncateBeans(ctx context.Context, beans ...interface{}) (err error) {
|
||||
func TruncateBeans(ctx context.Context, beans ...any) (err error) {
|
||||
e := GetEngine(ctx)
|
||||
for i := range beans {
|
||||
if _, err = e.Truncate(beans[i]); err != nil {
|
||||
@@ -231,12 +231,12 @@ func TruncateBeans(ctx context.Context, beans ...interface{}) (err error) {
|
||||
}
|
||||
|
||||
// CountByBean counts the number of database records according non-empty fields of the bean as conditions.
|
||||
func CountByBean(ctx context.Context, bean interface{}) (int64, error) {
|
||||
func CountByBean(ctx context.Context, bean any) (int64, error) {
|
||||
return GetEngine(ctx).Count(bean)
|
||||
}
|
||||
|
||||
// TableName returns the table name according a bean object
|
||||
func TableName(bean interface{}) string {
|
||||
func TableName(bean any) string {
|
||||
return x.TableName(bean)
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
|
||||
var (
|
||||
x *xorm.Engine
|
||||
tables []interface{}
|
||||
tables []any
|
||||
initFuncs []func() error
|
||||
|
||||
// HasEngine specifies if we have a xorm.Engine
|
||||
@@ -34,41 +34,41 @@ var (
|
||||
|
||||
// Engine represents a xorm engine or session.
|
||||
type Engine interface {
|
||||
Table(tableNameOrBean interface{}) *xorm.Session
|
||||
Count(...interface{}) (int64, error)
|
||||
Decr(column string, arg ...interface{}) *xorm.Session
|
||||
Delete(...interface{}) (int64, error)
|
||||
Truncate(...interface{}) (int64, error)
|
||||
Exec(...interface{}) (sql.Result, error)
|
||||
Find(interface{}, ...interface{}) error
|
||||
Get(beans ...interface{}) (bool, error)
|
||||
ID(interface{}) *xorm.Session
|
||||
In(string, ...interface{}) *xorm.Session
|
||||
Incr(column string, arg ...interface{}) *xorm.Session
|
||||
Insert(...interface{}) (int64, error)
|
||||
Iterate(interface{}, xorm.IterFunc) error
|
||||
Join(joinOperator string, tablename, condition interface{}, args ...interface{}) *xorm.Session
|
||||
SQL(interface{}, ...interface{}) *xorm.Session
|
||||
Where(interface{}, ...interface{}) *xorm.Session
|
||||
Table(tableNameOrBean any) *xorm.Session
|
||||
Count(...any) (int64, error)
|
||||
Decr(column string, arg ...any) *xorm.Session
|
||||
Delete(...any) (int64, error)
|
||||
Truncate(...any) (int64, error)
|
||||
Exec(...any) (sql.Result, error)
|
||||
Find(any, ...any) error
|
||||
Get(beans ...any) (bool, error)
|
||||
ID(any) *xorm.Session
|
||||
In(string, ...any) *xorm.Session
|
||||
Incr(column string, arg ...any) *xorm.Session
|
||||
Insert(...any) (int64, error)
|
||||
Iterate(any, xorm.IterFunc) error
|
||||
Join(joinOperator string, tablename, condition any, args ...any) *xorm.Session
|
||||
SQL(any, ...any) *xorm.Session
|
||||
Where(any, ...any) *xorm.Session
|
||||
Asc(colNames ...string) *xorm.Session
|
||||
Desc(colNames ...string) *xorm.Session
|
||||
Limit(limit int, start ...int) *xorm.Session
|
||||
NoAutoTime() *xorm.Session
|
||||
SumInt(bean interface{}, columnName string) (res int64, err error)
|
||||
Sync2(...interface{}) error
|
||||
SumInt(bean any, columnName string) (res int64, err error)
|
||||
Sync2(...any) error
|
||||
Select(string) *xorm.Session
|
||||
NotIn(string, ...interface{}) *xorm.Session
|
||||
OrderBy(interface{}, ...interface{}) *xorm.Session
|
||||
Exist(...interface{}) (bool, error)
|
||||
NotIn(string, ...any) *xorm.Session
|
||||
OrderBy(any, ...any) *xorm.Session
|
||||
Exist(...any) (bool, error)
|
||||
Distinct(...string) *xorm.Session
|
||||
Query(...interface{}) ([]map[string][]byte, error)
|
||||
Query(...any) ([]map[string][]byte, error)
|
||||
Cols(...string) *xorm.Session
|
||||
Context(ctx context.Context) *xorm.Session
|
||||
Ping() error
|
||||
}
|
||||
|
||||
// TableInfo returns table's information via an object
|
||||
func TableInfo(v interface{}) (*schemas.Table, error) {
|
||||
func TableInfo(v any) (*schemas.Table, error) {
|
||||
return x.TableInfo(v)
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ func DumpTables(tables []*schemas.Table, w io.Writer, tp ...schemas.DBType) erro
|
||||
}
|
||||
|
||||
// RegisterModel registers model, if initfunc provided, it will be invoked after data model sync
|
||||
func RegisterModel(bean interface{}, initFunc ...func() error) {
|
||||
func RegisterModel(bean any, initFunc ...func() error) {
|
||||
tables = append(tables, bean)
|
||||
if len(initFuncs) > 0 && initFunc[0] != nil {
|
||||
initFuncs = append(initFuncs, initFunc[0])
|
||||
@@ -209,14 +209,14 @@ func InitEngineWithMigration(ctx context.Context, migrateFunc func(*xorm.Engine)
|
||||
}
|
||||
|
||||
// NamesToBean return a list of beans or an error
|
||||
func NamesToBean(names ...string) ([]interface{}, error) {
|
||||
beans := []interface{}{}
|
||||
func NamesToBean(names ...string) ([]any, error) {
|
||||
beans := []any{}
|
||||
if len(names) == 0 {
|
||||
beans = append(beans, tables...)
|
||||
return beans, nil
|
||||
}
|
||||
// Need to map provided names to beans...
|
||||
beanMap := make(map[string]interface{})
|
||||
beanMap := make(map[string]any)
|
||||
for _, bean := range tables {
|
||||
|
||||
beanMap[strings.ToLower(reflect.Indirect(reflect.ValueOf(bean)).Type().Name())] = bean
|
||||
@@ -224,7 +224,7 @@ func NamesToBean(names ...string) ([]interface{}, error) {
|
||||
beanMap[strings.ToLower(x.TableName(bean, true))] = bean
|
||||
}
|
||||
|
||||
gotBean := make(map[interface{}]bool)
|
||||
gotBean := make(map[any]bool)
|
||||
for _, name := range names {
|
||||
bean, ok := beanMap[strings.ToLower(strings.TrimSpace(name))]
|
||||
if !ok {
|
||||
@@ -266,7 +266,7 @@ func DumpDatabase(filePath, dbType string) error {
|
||||
}
|
||||
|
||||
// MaxBatchInsertSize returns the table's max batch insert size
|
||||
func MaxBatchInsertSize(bean interface{}) int {
|
||||
func MaxBatchInsertSize(bean any) int {
|
||||
t, err := x.TableInfo(bean)
|
||||
if err != nil {
|
||||
return 50
|
||||
@@ -286,7 +286,7 @@ func DeleteAllRecords(tableName string) error {
|
||||
}
|
||||
|
||||
// GetMaxID will return max id of the table
|
||||
func GetMaxID(beanOrTableName interface{}) (maxID int64, err error) {
|
||||
func GetMaxID(beanOrTableName any) (maxID int64, err error) {
|
||||
_, err = x.Select("MAX(id)").Table(beanOrTableName).Get(&maxID)
|
||||
return maxID, err
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ func (err ErrCancelled) Error() string {
|
||||
}
|
||||
|
||||
// ErrCancelledf returns an ErrCancelled for the provided format and args
|
||||
func ErrCancelledf(format string, args ...interface{}) error {
|
||||
func ErrCancelledf(format string, args ...any) error {
|
||||
return ErrCancelled{
|
||||
fmt.Sprintf(format, args...),
|
||||
}
|
||||
|
||||
@@ -28,47 +28,47 @@ func NewXORMLogger(showSQL bool) xormlog.Logger {
|
||||
const stackLevel = 8
|
||||
|
||||
// Log a message with defined skip and at logging level
|
||||
func (l *XORMLogBridge) Log(skip int, level log.Level, format string, v ...interface{}) {
|
||||
func (l *XORMLogBridge) Log(skip int, level log.Level, format string, v ...any) {
|
||||
l.logger.Log(skip+1, level, format, v...)
|
||||
}
|
||||
|
||||
// Debug show debug log
|
||||
func (l *XORMLogBridge) Debug(v ...interface{}) {
|
||||
func (l *XORMLogBridge) Debug(v ...any) {
|
||||
l.Log(stackLevel, log.DEBUG, "%s", fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
// Debugf show debug log
|
||||
func (l *XORMLogBridge) Debugf(format string, v ...interface{}) {
|
||||
func (l *XORMLogBridge) Debugf(format string, v ...any) {
|
||||
l.Log(stackLevel, log.DEBUG, format, v...)
|
||||
}
|
||||
|
||||
// Error show error log
|
||||
func (l *XORMLogBridge) Error(v ...interface{}) {
|
||||
func (l *XORMLogBridge) Error(v ...any) {
|
||||
l.Log(stackLevel, log.ERROR, "%s", fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
// Errorf show error log
|
||||
func (l *XORMLogBridge) Errorf(format string, v ...interface{}) {
|
||||
func (l *XORMLogBridge) Errorf(format string, v ...any) {
|
||||
l.Log(stackLevel, log.ERROR, format, v...)
|
||||
}
|
||||
|
||||
// Info show information level log
|
||||
func (l *XORMLogBridge) Info(v ...interface{}) {
|
||||
func (l *XORMLogBridge) Info(v ...any) {
|
||||
l.Log(stackLevel, log.INFO, "%s", fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
// Infof show information level log
|
||||
func (l *XORMLogBridge) Infof(format string, v ...interface{}) {
|
||||
func (l *XORMLogBridge) Infof(format string, v ...any) {
|
||||
l.Log(stackLevel, log.INFO, format, v...)
|
||||
}
|
||||
|
||||
// Warn show warning log
|
||||
func (l *XORMLogBridge) Warn(v ...interface{}) {
|
||||
func (l *XORMLogBridge) Warn(v ...any) {
|
||||
l.Log(stackLevel, log.WARN, "%s", fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
// Warnf show warnning log
|
||||
func (l *XORMLogBridge) Warnf(format string, v ...interface{}) {
|
||||
func (l *XORMLogBridge) Warnf(format string, v ...any) {
|
||||
l.Log(stackLevel, log.WARN, format, v...)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@@ -21,6 +22,7 @@ var defaultFileBlockSize int64 = 32 * 1024
|
||||
type File interface {
|
||||
io.ReadWriteCloser
|
||||
io.Seeker
|
||||
fs.File
|
||||
}
|
||||
|
||||
type file struct {
|
||||
@@ -193,10 +195,26 @@ func (f *file) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *file) Stat() (os.FileInfo, error) {
|
||||
if f.metaID == 0 {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
fileMeta, err := findFileMetaByID(f.ctx, f.metaID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fileMeta, nil
|
||||
}
|
||||
|
||||
func timeToFileTimestamp(t time.Time) int64 {
|
||||
return t.UnixMicro()
|
||||
}
|
||||
|
||||
func fileTimestampToTime(timestamp int64) time.Time {
|
||||
return time.UnixMicro(timestamp)
|
||||
}
|
||||
|
||||
func (f *file) loadMetaByPath() (*dbfsMeta, error) {
|
||||
var fileMeta dbfsMeta
|
||||
if ok, err := db.GetEngine(f.ctx).Where("full_path = ?", f.fullPath).Get(&fileMeta); err != nil {
|
||||
|
||||
@@ -5,7 +5,10 @@ package dbfs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
)
|
||||
@@ -100,3 +103,29 @@ func Remove(ctx context.Context, name string) error {
|
||||
defer f.Close()
|
||||
return f.delete()
|
||||
}
|
||||
|
||||
var _ fs.FileInfo = (*dbfsMeta)(nil)
|
||||
|
||||
func (m *dbfsMeta) Name() string {
|
||||
return path.Base(m.FullPath)
|
||||
}
|
||||
|
||||
func (m *dbfsMeta) Size() int64 {
|
||||
return m.FileSize
|
||||
}
|
||||
|
||||
func (m *dbfsMeta) Mode() fs.FileMode {
|
||||
return os.ModePerm
|
||||
}
|
||||
|
||||
func (m *dbfsMeta) ModTime() time.Time {
|
||||
return fileTimestampToTime(m.ModifyTimestamp)
|
||||
}
|
||||
|
||||
func (m *dbfsMeta) IsDir() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *dbfsMeta) Sys() any {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -111,6 +111,19 @@ func TestDbfsBasic(t *testing.T) {
|
||||
|
||||
_, err = OpenFile(db.DefaultContext, "test2.txt", os.O_RDONLY)
|
||||
assert.Error(t, err)
|
||||
|
||||
// test stat
|
||||
f, err = OpenFile(db.DefaultContext, "test/test.txt", os.O_RDWR|os.O_CREATE)
|
||||
assert.NoError(t, err)
|
||||
stat, err := f.Stat()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "test.txt", stat.Name())
|
||||
assert.EqualValues(t, 0, stat.Size())
|
||||
_, err = f.Write([]byte("0123456789"))
|
||||
assert.NoError(t, err)
|
||||
stat, err = f.Stat()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 10, stat.Size())
|
||||
}
|
||||
|
||||
func TestDbfsReadWrite(t *testing.T) {
|
||||
|
||||
49
models/fixtures/mirror.yml
Normal file
49
models/fixtures/mirror.yml
Normal file
@@ -0,0 +1,49 @@
|
||||
-
|
||||
id: 1
|
||||
repo_id: 5
|
||||
interval: 3600
|
||||
enable_prune: false
|
||||
updated_unix: 0
|
||||
next_update_unix: 0
|
||||
lfs_enabled: false
|
||||
lfs_endpoint: ""
|
||||
|
||||
-
|
||||
id: 2
|
||||
repo_id: 25
|
||||
interval: 3600
|
||||
enable_prune: false
|
||||
updated_unix: 0
|
||||
next_update_unix: 0
|
||||
lfs_enabled: false
|
||||
lfs_endpoint: ""
|
||||
|
||||
-
|
||||
id: 3
|
||||
repo_id: 26
|
||||
interval: 3600
|
||||
enable_prune: false
|
||||
updated_unix: 0
|
||||
next_update_unix: 0
|
||||
lfs_enabled: false
|
||||
lfs_endpoint: ""
|
||||
|
||||
-
|
||||
id: 4
|
||||
repo_id: 27
|
||||
interval: 3600
|
||||
enable_prune: false
|
||||
updated_unix: 0
|
||||
next_update_unix: 0
|
||||
lfs_enabled: false
|
||||
lfs_endpoint: ""
|
||||
|
||||
-
|
||||
id: 5
|
||||
repo_id: 28
|
||||
interval: 3600
|
||||
enable_prune: false
|
||||
updated_unix: 0
|
||||
next_update_unix: 0
|
||||
lfs_enabled: false
|
||||
lfs_endpoint: ""
|
||||
@@ -81,3 +81,21 @@
|
||||
uid: 5
|
||||
org_id: 23
|
||||
is_public: false
|
||||
|
||||
-
|
||||
id: 15
|
||||
uid: 1
|
||||
org_id: 35
|
||||
is_public: true
|
||||
|
||||
-
|
||||
id: 16
|
||||
uid: 1
|
||||
org_id: 36
|
||||
is_public: true
|
||||
|
||||
-
|
||||
id: 17
|
||||
uid: 5
|
||||
org_id: 36
|
||||
is_public: true
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
creator_id: 2
|
||||
board_type: 1
|
||||
type: 2
|
||||
created_unix: 1688973030
|
||||
updated_unix: 1688973030
|
||||
|
||||
-
|
||||
id: 2
|
||||
@@ -17,6 +19,8 @@
|
||||
creator_id: 3
|
||||
board_type: 1
|
||||
type: 2
|
||||
created_unix: 1688973010
|
||||
updated_unix: 1688973010
|
||||
|
||||
-
|
||||
id: 3
|
||||
@@ -27,6 +31,8 @@
|
||||
creator_id: 5
|
||||
board_type: 1
|
||||
type: 2
|
||||
created_unix: 1688973020
|
||||
updated_unix: 1688973020
|
||||
|
||||
-
|
||||
id: 4
|
||||
@@ -37,3 +43,5 @@
|
||||
creator_id: 2
|
||||
board_type: 1
|
||||
type: 2
|
||||
created_unix: 1688973000
|
||||
updated_unix: 1688973000
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
num_projects: 0
|
||||
num_closed_projects: 0
|
||||
is_private: true
|
||||
is_empty: true
|
||||
is_empty: false
|
||||
is_archived: false
|
||||
is_mirror: true
|
||||
status: 0
|
||||
|
||||
@@ -184,3 +184,36 @@
|
||||
num_members: 1
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: true
|
||||
|
||||
-
|
||||
id: 18
|
||||
org_id: 35
|
||||
lower_name: owners
|
||||
name: Owners
|
||||
authorize: 4 # owner
|
||||
num_repos: 0
|
||||
num_members: 1
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: true
|
||||
|
||||
-
|
||||
id: 19
|
||||
org_id: 36
|
||||
lower_name: owners
|
||||
name: Owners
|
||||
authorize: 4 # owner
|
||||
num_repos: 0
|
||||
num_members: 1
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: true
|
||||
|
||||
-
|
||||
id: 20
|
||||
org_id: 36
|
||||
lower_name: team20writepackage
|
||||
name: team20writepackage
|
||||
authorize: 1
|
||||
num_repos: 0
|
||||
num_members: 1
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: true
|
||||
|
||||
@@ -273,4 +273,10 @@
|
||||
id: 46
|
||||
team_id: 17
|
||||
type: 9 # package
|
||||
access_mode: 0
|
||||
access_mode: 2
|
||||
|
||||
-
|
||||
id: 47
|
||||
team_id: 20
|
||||
type: 9 # package
|
||||
access_mode: 2
|
||||
|
||||
@@ -105,3 +105,21 @@
|
||||
org_id: 23
|
||||
team_id: 17
|
||||
uid: 5
|
||||
|
||||
-
|
||||
id: 19
|
||||
org_id: 35
|
||||
team_id: 18
|
||||
uid: 1
|
||||
|
||||
-
|
||||
id: 20
|
||||
org_id: 36
|
||||
team_id: 19
|
||||
uid: 1
|
||||
|
||||
-
|
||||
id: 21
|
||||
org_id: 36
|
||||
team_id: 20
|
||||
uid: 5
|
||||
|
||||
@@ -1258,3 +1258,77 @@
|
||||
repo_admin_change_team_access: false
|
||||
theme: ""
|
||||
keep_activity_private: false
|
||||
|
||||
-
|
||||
id: 35
|
||||
lower_name: private_org35
|
||||
name: private_org35
|
||||
full_name: Private Org 35
|
||||
email: private_org35@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: ZogKvWdyEx:password
|
||||
passwd_hash_algo: dummy
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: private_org35
|
||||
type: 1
|
||||
salt: ZogKvWdyEx
|
||||
max_repo_creation: -1
|
||||
is_active: true
|
||||
is_admin: false
|
||||
is_restricted: false
|
||||
allow_git_hook: false
|
||||
allow_import_local: false
|
||||
allow_create_organization: true
|
||||
prohibit_login: false
|
||||
avatar: avatar35
|
||||
avatar_email: private_org35@example.com
|
||||
use_custom_avatar: false
|
||||
num_followers: 0
|
||||
num_following: 0
|
||||
num_stars: 0
|
||||
num_repos: 0
|
||||
num_teams: 1
|
||||
num_members: 1
|
||||
visibility: 2
|
||||
repo_admin_change_team_access: false
|
||||
theme: ""
|
||||
keep_activity_private: false
|
||||
|
||||
-
|
||||
id: 36
|
||||
lower_name: limited_org36
|
||||
name: limited_org36
|
||||
full_name: Limited Org 36
|
||||
email: limited_org36@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: ZogKvWdyEx:password
|
||||
passwd_hash_algo: dummy
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: limited_org36
|
||||
type: 1
|
||||
salt: ZogKvWdyEx
|
||||
max_repo_creation: -1
|
||||
is_active: true
|
||||
is_admin: false
|
||||
is_restricted: false
|
||||
allow_git_hook: false
|
||||
allow_import_local: false
|
||||
allow_create_organization: true
|
||||
prohibit_login: false
|
||||
avatar: avatar22
|
||||
avatar_email: limited_org36@example.com
|
||||
use_custom_avatar: false
|
||||
num_followers: 0
|
||||
num_following: 0
|
||||
num_stars: 0
|
||||
num_repos: 0
|
||||
num_teams: 2
|
||||
num_members: 2
|
||||
visibility: 1
|
||||
repo_admin_change_team_access: false
|
||||
theme: ""
|
||||
keep_activity_private: false
|
||||
|
||||
@@ -172,7 +172,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
|
||||
// 3. Update all not merged pull request base branch name
|
||||
_, err = sess.Table("pull_request").Where("base_repo_id=? AND base_branch=? AND has_merged=?",
|
||||
repo.ID, from, false).
|
||||
Update(map[string]interface{}{"base_branch": to})
|
||||
Update(map[string]any{"base_branch": to})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ func LFSAutoAssociate(ctx context.Context, metas []*LFSMetaObject, user *user_mo
|
||||
|
||||
sess := db.GetEngine(ctx)
|
||||
|
||||
oids := make([]interface{}, len(metas))
|
||||
oids := make([]any, len(metas))
|
||||
oidMap := make(map[string]*LFSMetaObject, len(metas))
|
||||
for i, meta := range metas {
|
||||
oids[i] = meta.Oid
|
||||
|
||||
@@ -1131,7 +1131,7 @@ func DeleteComment(ctx context.Context, comment *Comment) error {
|
||||
}
|
||||
if _, err := e.Table("action").
|
||||
Where("comment_id = ?", comment.ID).
|
||||
Update(map[string]interface{}{
|
||||
Update(map[string]any{
|
||||
"is_deleted": true,
|
||||
}); err != nil {
|
||||
return err
|
||||
@@ -1156,7 +1156,7 @@ func UpdateCommentsMigrationsByType(tp structs.GitServiceType, originalAuthorID
|
||||
}),
|
||||
)).
|
||||
And("comment.original_author_id = ?", originalAuthorID).
|
||||
Update(map[string]interface{}{
|
||||
Update(map[string]any{
|
||||
"poster_id": posterID,
|
||||
"original_author": "",
|
||||
"original_author_id": 0,
|
||||
|
||||
@@ -714,7 +714,7 @@ func (issue *Issue) Pin(ctx context.Context, user *user_model.User) error {
|
||||
|
||||
_, err = db.GetEngine(ctx).Table("issue").
|
||||
Where("id = ?", issue.ID).
|
||||
Update(map[string]interface{}{
|
||||
Update(map[string]any{
|
||||
"pin_order": maxPin + 1,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -750,7 +750,7 @@ func (issue *Issue) Unpin(ctx context.Context, user *user_model.User) error {
|
||||
|
||||
_, err = db.GetEngine(ctx).Table("issue").
|
||||
Where("id = ?", issue.ID).
|
||||
Update(map[string]interface{}{
|
||||
Update(map[string]any{
|
||||
"pin_order": 0,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -822,7 +822,7 @@ func (issue *Issue) MovePin(ctx context.Context, newPosition int) error {
|
||||
|
||||
_, err = db.GetEngine(dbctx).Table("issue").
|
||||
Where("id = ?", issue.ID).
|
||||
Update(map[string]interface{}{
|
||||
Update(map[string]any{
|
||||
"pin_order": newPosition,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -229,39 +229,41 @@ func (issues IssueList) loadMilestones(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (issues IssueList) getProjectIDs() []int64 {
|
||||
ids := make(container.Set[int64], len(issues))
|
||||
for _, issue := range issues {
|
||||
ids.Add(issue.ProjectID())
|
||||
}
|
||||
return ids.Values()
|
||||
}
|
||||
func (issues IssueList) LoadProjects(ctx context.Context) error {
|
||||
issueIDs := issues.getIssueIDs()
|
||||
projectMaps := make(map[int64]*project_model.Project, len(issues))
|
||||
left := len(issueIDs)
|
||||
|
||||
func (issues IssueList) loadProjects(ctx context.Context) error {
|
||||
projectIDs := issues.getProjectIDs()
|
||||
if len(projectIDs) == 0 {
|
||||
return nil
|
||||
type projectWithIssueID struct {
|
||||
*project_model.Project `xorm:"extends"`
|
||||
IssueID int64
|
||||
}
|
||||
|
||||
projectMaps := make(map[int64]*project_model.Project, len(projectIDs))
|
||||
left := len(projectIDs)
|
||||
for left > 0 {
|
||||
limit := db.DefaultMaxInSize
|
||||
if left < limit {
|
||||
limit = left
|
||||
}
|
||||
|
||||
projects := make([]*projectWithIssueID, 0, limit)
|
||||
err := db.GetEngine(ctx).
|
||||
In("id", projectIDs[:limit]).
|
||||
Find(&projectMaps)
|
||||
Table("project").
|
||||
Select("project.*, project_issue.issue_id").
|
||||
Join("INNER", "project_issue", "project.id = project_issue.project_id").
|
||||
In("project_issue.issue_id", issueIDs[:limit]).
|
||||
Find(&projects)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, project := range projects {
|
||||
projectMaps[project.IssueID] = project.Project
|
||||
}
|
||||
left -= limit
|
||||
projectIDs = projectIDs[limit:]
|
||||
issueIDs = issueIDs[limit:]
|
||||
}
|
||||
|
||||
for _, issue := range issues {
|
||||
issue.Project = projectMaps[issue.ProjectID()]
|
||||
issue.Project = projectMaps[issue.ID]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -541,7 +543,7 @@ func (issues IssueList) loadAttributes(ctx context.Context) error {
|
||||
return fmt.Errorf("issue.loadAttributes: loadMilestones: %w", err)
|
||||
}
|
||||
|
||||
if err := issues.loadProjects(ctx); err != nil {
|
||||
if err := issues.LoadProjects(ctx); err != nil {
|
||||
return fmt.Errorf("issue.loadAttributes: loadProjects: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -66,8 +66,10 @@ func TestIssueList_LoadAttributes(t *testing.T) {
|
||||
}
|
||||
if issue.ID == int64(1) {
|
||||
assert.Equal(t, int64(400), issue.TotalTrackedTime)
|
||||
} else if issue.ID == int64(2) {
|
||||
assert.Equal(t, int64(3682), issue.TotalTrackedTime)
|
||||
assert.NotNil(t, issue.Project)
|
||||
assert.Equal(t, int64(1), issue.Project.ID)
|
||||
} else {
|
||||
assert.Nil(t, issue.Project)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,22 +16,18 @@ import (
|
||||
func (issue *Issue) LoadProject(ctx context.Context) (err error) {
|
||||
if issue.Project == nil {
|
||||
var p project_model.Project
|
||||
if _, err = db.GetEngine(ctx).Table("project").
|
||||
has, err := db.GetEngine(ctx).Table("project").
|
||||
Join("INNER", "project_issue", "project.id=project_issue.project_id").
|
||||
Where("project_issue.issue_id = ?", issue.ID).
|
||||
Get(&p); err != nil {
|
||||
Where("project_issue.issue_id = ?", issue.ID).Get(&p)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
issue.Project = &p
|
||||
}
|
||||
issue.Project = &p
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ProjectID return project id if issue was assigned to one
|
||||
func (issue *Issue) ProjectID() int64 {
|
||||
return issue.projectID(db.DefaultContext)
|
||||
}
|
||||
|
||||
func (issue *Issue) projectID(ctx context.Context) int64 {
|
||||
var ip project_model.ProjectIssue
|
||||
has, err := db.GetEngine(ctx).Where("issue_id=?", issue.ID).Get(&ip)
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -539,3 +540,47 @@ func TestCountIssues(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 18, count)
|
||||
}
|
||||
|
||||
func TestIssueLoadAttributes(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
setting.Service.EnableTimetracking = true
|
||||
|
||||
issueList := issues_model.IssueList{
|
||||
unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}),
|
||||
unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}),
|
||||
}
|
||||
|
||||
for _, issue := range issueList {
|
||||
assert.NoError(t, issue.LoadAttributes(db.DefaultContext))
|
||||
assert.EqualValues(t, issue.RepoID, issue.Repo.ID)
|
||||
for _, label := range issue.Labels {
|
||||
assert.EqualValues(t, issue.RepoID, label.RepoID)
|
||||
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label.ID})
|
||||
}
|
||||
if issue.PosterID > 0 {
|
||||
assert.EqualValues(t, issue.PosterID, issue.Poster.ID)
|
||||
}
|
||||
if issue.AssigneeID > 0 {
|
||||
assert.EqualValues(t, issue.AssigneeID, issue.Assignee.ID)
|
||||
}
|
||||
if issue.MilestoneID > 0 {
|
||||
assert.EqualValues(t, issue.MilestoneID, issue.Milestone.ID)
|
||||
}
|
||||
if issue.IsPull {
|
||||
assert.EqualValues(t, issue.ID, issue.PullRequest.IssueID)
|
||||
}
|
||||
for _, attachment := range issue.Attachments {
|
||||
assert.EqualValues(t, issue.ID, attachment.IssueID)
|
||||
}
|
||||
for _, comment := range issue.Comments {
|
||||
assert.EqualValues(t, issue.ID, comment.IssueID)
|
||||
}
|
||||
if issue.ID == int64(1) {
|
||||
assert.Equal(t, int64(400), issue.TotalTrackedTime)
|
||||
assert.NotNil(t, issue.Project)
|
||||
assert.Equal(t, int64(1), issue.Project.ID)
|
||||
} else {
|
||||
assert.Nil(t, issue.Project)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,7 +511,7 @@ func UpdateIssueDeadline(issue *Issue, deadlineUnix timeutil.TimeStamp, doer *us
|
||||
}
|
||||
|
||||
// DeleteInIssue delete records in beans with external key issue_id = ?
|
||||
func DeleteInIssue(ctx context.Context, issueID int64, beans ...interface{}) error {
|
||||
func DeleteInIssue(ctx context.Context, issueID int64, beans ...any) error {
|
||||
e := db.GetEngine(ctx)
|
||||
for _, bean := range beans {
|
||||
if _, err := e.In("issue_id", issueID).Delete(bean); err != nil {
|
||||
@@ -673,7 +673,7 @@ func UpdateIssuesMigrationsByType(gitServiceType api.GitServiceType, originalAut
|
||||
_, err := db.GetEngine(db.DefaultContext).Table("issue").
|
||||
Where("repo_id IN (SELECT id FROM repository WHERE original_service_type = ?)", gitServiceType).
|
||||
And("original_author_id = ?", originalAuthorID).
|
||||
Update(map[string]interface{}{
|
||||
Update(map[string]any{
|
||||
"poster_id": posterID,
|
||||
"original_author": "",
|
||||
"original_author_id": 0,
|
||||
@@ -686,7 +686,7 @@ func UpdateReactionsMigrationsByType(gitServiceType api.GitServiceType, original
|
||||
_, err := db.GetEngine(db.DefaultContext).Table("reaction").
|
||||
Where("original_author_id = ?", originalAuthorID).
|
||||
And(migratedIssueCond(gitServiceType)).
|
||||
Update(map[string]interface{}{
|
||||
Update(map[string]any{
|
||||
"user_id": userID,
|
||||
"original_author": "",
|
||||
"original_author_id": 0,
|
||||
|
||||
@@ -1090,7 +1090,7 @@ func UpdateReviewsMigrationsByType(tp structs.GitServiceType, originalAuthorID s
|
||||
_, err := db.GetEngine(db.DefaultContext).Table("review").
|
||||
Where("original_author_id = ?", originalAuthorID).
|
||||
And(migratedIssueCond(tp)).
|
||||
Update(map[string]interface{}{
|
||||
Update(map[string]any{
|
||||
"reviewer_id": posterID,
|
||||
"original_author": "",
|
||||
"original_author_id": 0,
|
||||
|
||||
@@ -27,7 +27,7 @@ import (
|
||||
|
||||
// RecreateTables will recreate the tables for the provided beans using the newly provided bean definition and move all data to that new table
|
||||
// WARNING: YOU MUST PROVIDE THE FULL BEAN DEFINITION
|
||||
func RecreateTables(beans ...interface{}) func(*xorm.Engine) error {
|
||||
func RecreateTables(beans ...any) func(*xorm.Engine) error {
|
||||
return func(x *xorm.Engine) error {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
@@ -48,7 +48,7 @@ func RecreateTables(beans ...interface{}) func(*xorm.Engine) error {
|
||||
// RecreateTable will recreate the table using the newly provided bean definition and move all data to that new table
|
||||
// WARNING: YOU MUST PROVIDE THE FULL BEAN DEFINITION
|
||||
// WARNING: YOU MUST COMMIT THE SESSION AT THE END
|
||||
func RecreateTable(sess *xorm.Session, bean interface{}) error {
|
||||
func RecreateTable(sess *xorm.Session, bean any) error {
|
||||
// TODO: This will not work if there are foreign keys
|
||||
|
||||
tableName := sess.Engine().TableName(bean)
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
// Provide models to be sync'd with the database - in particular any models you expect fixtures to be loaded from.
|
||||
//
|
||||
// fixtures in `models/migrations/fixtures/<TestName>` will be loaded automatically
|
||||
func PrepareTestEnv(t *testing.T, skip int, syncModels ...interface{}) (*xorm.Engine, func()) {
|
||||
func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, func()) {
|
||||
t.Helper()
|
||||
ourSkip := 2
|
||||
ourSkip += skip
|
||||
|
||||
@@ -59,11 +59,11 @@ func UpdateMigrationServiceTypes(x *xorm.Engine) error {
|
||||
}
|
||||
|
||||
type ExternalLoginUser struct {
|
||||
ExternalID string `xorm:"pk NOT NULL"`
|
||||
UserID int64 `xorm:"INDEX NOT NULL"`
|
||||
LoginSourceID int64 `xorm:"pk NOT NULL"`
|
||||
RawData map[string]interface{} `xorm:"TEXT JSON"`
|
||||
Provider string `xorm:"index VARCHAR(25)"`
|
||||
ExternalID string `xorm:"pk NOT NULL"`
|
||||
UserID int64 `xorm:"INDEX NOT NULL"`
|
||||
LoginSourceID int64 `xorm:"pk NOT NULL"`
|
||||
RawData map[string]any `xorm:"TEXT JSON"`
|
||||
Provider string `xorm:"index VARCHAR(25)"`
|
||||
Email string
|
||||
Name string
|
||||
FirstName string
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
func UnwrapLDAPSourceCfg(x *xorm.Engine) error {
|
||||
jsonUnmarshalHandleDoubleEncode := func(bs []byte, v interface{}) error {
|
||||
jsonUnmarshalHandleDoubleEncode := func(bs []byte, v any) error {
|
||||
err := json.Unmarshal(bs, v)
|
||||
if err != nil {
|
||||
ok := true
|
||||
@@ -54,7 +54,7 @@ func UnwrapLDAPSourceCfg(x *xorm.Engine) error {
|
||||
const dldapType = 5
|
||||
|
||||
type WrappedSource struct {
|
||||
Source map[string]interface{}
|
||||
Source map[string]any
|
||||
}
|
||||
|
||||
// change lower_email as unique
|
||||
@@ -77,7 +77,7 @@ func UnwrapLDAPSourceCfg(x *xorm.Engine) error {
|
||||
|
||||
for _, source := range sources {
|
||||
wrapped := &WrappedSource{
|
||||
Source: map[string]interface{}{},
|
||||
Source: map[string]any{},
|
||||
}
|
||||
err := jsonUnmarshalHandleDoubleEncode([]byte(source.Cfg), &wrapped)
|
||||
if err != nil {
|
||||
|
||||
@@ -62,8 +62,8 @@ func Test_UnwrapLDAPSourceCfg(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, source := range sources {
|
||||
converted := map[string]interface{}{}
|
||||
expected := map[string]interface{}{}
|
||||
converted := map[string]any{}
|
||||
expected := map[string]any{}
|
||||
|
||||
if err := json.Unmarshal([]byte(source.Cfg), &converted); err != nil {
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -79,7 +79,7 @@ func Test_AddHeaderAuthorizationEncryptedColWebhook(t *testing.T) {
|
||||
return
|
||||
}
|
||||
for _, h := range hookTasks {
|
||||
var m map[string]interface{}
|
||||
var m map[string]any
|
||||
err := json.Unmarshal([]byte(h.PayloadContent), &m)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, m["access_token"])
|
||||
|
||||
@@ -81,11 +81,11 @@ func AddIssueDependencies(x *xorm.Engine) (err error) {
|
||||
// RepoUnit describes all units of a repository
|
||||
type RepoUnit struct {
|
||||
ID int64
|
||||
RepoID int64 `xorm:"INDEX(s)"`
|
||||
Type int `xorm:"INDEX(s)"`
|
||||
Config map[string]interface{} `xorm:"JSON"`
|
||||
CreatedUnix int64 `xorm:"INDEX CREATED"`
|
||||
Created time.Time `xorm:"-"`
|
||||
RepoID int64 `xorm:"INDEX(s)"`
|
||||
Type int `xorm:"INDEX(s)"`
|
||||
Config map[string]any `xorm:"JSON"`
|
||||
CreatedUnix int64 `xorm:"INDEX CREATED"`
|
||||
Created time.Time `xorm:"-"`
|
||||
}
|
||||
|
||||
// Updating existing issue units
|
||||
@@ -96,7 +96,7 @@ func AddIssueDependencies(x *xorm.Engine) (err error) {
|
||||
}
|
||||
for _, unit := range units {
|
||||
if unit.Config == nil {
|
||||
unit.Config = make(map[string]interface{})
|
||||
unit.Config = make(map[string]any)
|
||||
}
|
||||
if _, ok := unit.Config["EnableDependencies"]; !ok {
|
||||
unit.Config["EnableDependencies"] = setting.Service.DefaultEnableDependencies
|
||||
|
||||
@@ -15,10 +15,10 @@ func AddPullRequestRebaseWithMerge(x *xorm.Engine) error {
|
||||
// RepoUnit describes all units of a repository
|
||||
type RepoUnit struct {
|
||||
ID int64
|
||||
RepoID int64 `xorm:"INDEX(s)"`
|
||||
Type int `xorm:"INDEX(s)"`
|
||||
Config map[string]interface{} `xorm:"JSON"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
|
||||
RepoID int64 `xorm:"INDEX(s)"`
|
||||
Type int `xorm:"INDEX(s)"`
|
||||
Config map[string]any `xorm:"JSON"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -46,7 +46,7 @@ func AddPullRequestRebaseWithMerge(x *xorm.Engine) error {
|
||||
}
|
||||
for _, unit := range units {
|
||||
if unit.Config == nil {
|
||||
unit.Config = make(map[string]interface{})
|
||||
unit.Config = make(map[string]any)
|
||||
}
|
||||
// Allow the new merge style if all other merge styles are allowed
|
||||
allowMergeRebase := true
|
||||
|
||||
@@ -532,27 +532,6 @@ func GetOrgsCanCreateRepoByUserID(userID int64) ([]*Organization, error) {
|
||||
Find(&orgs)
|
||||
}
|
||||
|
||||
// GetOrgUsersByUserID returns all organization-user relations by user ID.
|
||||
func GetOrgUsersByUserID(uid int64, opts *SearchOrganizationsOptions) ([]*OrgUser, error) {
|
||||
ous := make([]*OrgUser, 0, 10)
|
||||
sess := db.GetEngine(db.DefaultContext).
|
||||
Join("LEFT", "`user`", "`org_user`.org_id=`user`.id").
|
||||
Where("`org_user`.uid=?", uid)
|
||||
if !opts.All {
|
||||
// Only show public organizations
|
||||
sess.And("is_public=?", true)
|
||||
}
|
||||
|
||||
if opts.PageSize != 0 {
|
||||
sess = db.SetSessionPagination(sess, opts)
|
||||
}
|
||||
|
||||
err := sess.
|
||||
Asc("`user`.name").
|
||||
Find(&ous)
|
||||
return ous, err
|
||||
}
|
||||
|
||||
// GetOrgUsersByOrgID returns all organization-user relations by organization ID.
|
||||
func GetOrgUsersByOrgID(ctx context.Context, opts *FindOrgMembersOpts) ([]*OrgUser, error) {
|
||||
sess := db.GetEngine(ctx).Where("org_id=?", opts.OrgID)
|
||||
|
||||
@@ -207,42 +207,6 @@ func TestFindOrgs(t *testing.T) {
|
||||
assert.EqualValues(t, 1, total)
|
||||
}
|
||||
|
||||
func TestGetOrgUsersByUserID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
orgUsers, err := organization.GetOrgUsersByUserID(5, &organization.SearchOrganizationsOptions{All: true})
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, orgUsers, 3) {
|
||||
assert.Equal(t, organization.OrgUser{
|
||||
ID: orgUsers[0].ID,
|
||||
OrgID: 23,
|
||||
UID: 5,
|
||||
IsPublic: false,
|
||||
}, *orgUsers[0])
|
||||
assert.Equal(t, organization.OrgUser{
|
||||
ID: orgUsers[1].ID,
|
||||
OrgID: 6,
|
||||
UID: 5,
|
||||
IsPublic: true,
|
||||
}, *orgUsers[1])
|
||||
assert.Equal(t, organization.OrgUser{
|
||||
ID: orgUsers[2].ID,
|
||||
OrgID: 7,
|
||||
UID: 5,
|
||||
IsPublic: false,
|
||||
}, *orgUsers[2])
|
||||
}
|
||||
|
||||
publicOrgUsers, err := organization.GetOrgUsersByUserID(5, &organization.SearchOrganizationsOptions{All: false})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, publicOrgUsers, 1)
|
||||
assert.Equal(t, *orgUsers[1], *publicOrgUsers[0])
|
||||
|
||||
orgUsers, err = organization.GetOrgUsersByUserID(1, &organization.SearchOrganizationsOptions{All: true})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, orgUsers, 0)
|
||||
}
|
||||
|
||||
func TestGetOrgUsersByOrgID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ type PackageDescriptor struct {
|
||||
Creator *user_model.User
|
||||
PackageProperties PackagePropertyList
|
||||
VersionProperties PackagePropertyList
|
||||
Metadata interface{}
|
||||
Metadata any
|
||||
Files []*PackageFileDescriptor
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var metadata interface{}
|
||||
var metadata any
|
||||
switch p.Type {
|
||||
case TypeAlpine:
|
||||
metadata = &alpine.VersionMetadata{}
|
||||
|
||||
@@ -5,11 +5,18 @@ package packages
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// ErrPackageBlobNotExist indicates a package blob not exist error
|
||||
@@ -98,3 +105,42 @@ func GetTotalUnreferencedBlobSize(ctx context.Context) (int64, error) {
|
||||
Where("package_file.id IS NULL").
|
||||
SumInt(&PackageBlob{}, "size")
|
||||
}
|
||||
|
||||
// IsBlobAccessibleForUser tests if the user has access to the blob
|
||||
func IsBlobAccessibleForUser(ctx context.Context, blobID int64, user *user_model.User) (bool, error) {
|
||||
if user.IsAdmin {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
maxTeamAuthorize := builder.
|
||||
Select("max(team.authorize)").
|
||||
From("team").
|
||||
InnerJoin("team_user", "team_user.team_id = team.id").
|
||||
Where(builder.Eq{"team_user.uid": user.ID}.And(builder.Expr("team_user.org_id = `user`.id")))
|
||||
|
||||
maxTeamUnitAccessMode := builder.
|
||||
Select("max(team_unit.access_mode)").
|
||||
From("team").
|
||||
InnerJoin("team_user", "team_user.team_id = team.id").
|
||||
InnerJoin("team_unit", "team_unit.team_id = team.id").
|
||||
Where(builder.Eq{"team_user.uid": user.ID, "team_unit.type": unit.TypePackages}.And(builder.Expr("team_user.org_id = `user`.id")))
|
||||
|
||||
cond := builder.Eq{"package_blob.id": blobID}.And(
|
||||
// owner = user
|
||||
builder.Eq{"`user`.id": user.ID}.
|
||||
// user can see owner
|
||||
Or(builder.Eq{"`user`.visibility": structs.VisibleTypePublic}.Or(builder.Eq{"`user`.visibility": structs.VisibleTypeLimited})).
|
||||
// owner is an organization and user has access to it
|
||||
Or(builder.Eq{"`user`.type": user_model.UserTypeOrganization}.
|
||||
And(builder.Lte{strconv.Itoa(int(perm.AccessModeRead)): maxTeamAuthorize}.Or(builder.Lte{strconv.Itoa(int(perm.AccessModeRead)): maxTeamUnitAccessMode}))),
|
||||
)
|
||||
|
||||
return db.GetEngine(ctx).
|
||||
Table("package_blob").
|
||||
Join("INNER", "package_file", "package_file.blob_id = package_blob.id").
|
||||
Join("INNER", "package_version", "package_version.id = package_file.version_id").
|
||||
Join("INNER", "package", "package.id = package_version.package_id").
|
||||
Join("INNER", "user", "`user`.id = package.owner_id").
|
||||
Where(cond).
|
||||
Exist(&PackageBlob{})
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ type SearchOptions struct {
|
||||
RepoID int64
|
||||
Page int
|
||||
IsClosed util.OptionalBool
|
||||
SortType string
|
||||
OrderBy db.SearchOrderBy
|
||||
Type Type
|
||||
}
|
||||
|
||||
@@ -226,26 +226,28 @@ func CountProjects(ctx context.Context, opts SearchOptions) (int64, error) {
|
||||
return db.GetEngine(ctx).Where(opts.toConds()).Count(new(Project))
|
||||
}
|
||||
|
||||
func GetSearchOrderByBySortType(sortType string) db.SearchOrderBy {
|
||||
switch sortType {
|
||||
case "oldest":
|
||||
return db.SearchOrderByOldest
|
||||
case "recentupdate":
|
||||
return db.SearchOrderByRecentUpdated
|
||||
case "leastupdate":
|
||||
return db.SearchOrderByLeastUpdated
|
||||
default:
|
||||
return db.SearchOrderByNewest
|
||||
}
|
||||
}
|
||||
|
||||
// FindProjects returns a list of all projects that have been created in the repository
|
||||
func FindProjects(ctx context.Context, opts SearchOptions) ([]*Project, int64, error) {
|
||||
e := db.GetEngine(ctx).Where(opts.toConds())
|
||||
e := db.GetEngine(ctx).Where(opts.toConds()).OrderBy(opts.OrderBy.String())
|
||||
projects := make([]*Project, 0, setting.UI.IssuePagingNum)
|
||||
|
||||
if opts.Page > 0 {
|
||||
e = e.Limit(setting.UI.IssuePagingNum, (opts.Page-1)*setting.UI.IssuePagingNum)
|
||||
}
|
||||
|
||||
switch opts.SortType {
|
||||
case "oldest":
|
||||
e.Desc("created_unix")
|
||||
case "recentupdate":
|
||||
e.Desc("updated_unix")
|
||||
case "leastupdate":
|
||||
e.Asc("updated_unix")
|
||||
default:
|
||||
e.Asc("created_unix")
|
||||
}
|
||||
|
||||
count, err := e.FindAndCount(&projects)
|
||||
return projects, count, err
|
||||
}
|
||||
|
||||
@@ -82,3 +82,42 @@ func TestProject(t *testing.T) {
|
||||
|
||||
assert.True(t, projectFromDB.IsClosed)
|
||||
}
|
||||
|
||||
func TestProjectsSort(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
tests := []struct {
|
||||
sortType string
|
||||
wants []int64
|
||||
}{
|
||||
{
|
||||
sortType: "default",
|
||||
wants: []int64{1, 3, 2, 4},
|
||||
},
|
||||
{
|
||||
sortType: "oldest",
|
||||
wants: []int64{4, 2, 3, 1},
|
||||
},
|
||||
{
|
||||
sortType: "recentupdate",
|
||||
wants: []int64{1, 3, 2, 4},
|
||||
},
|
||||
{
|
||||
sortType: "leastupdate",
|
||||
wants: []int64{4, 2, 3, 1},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
projects, count, err := FindProjects(db.DefaultContext, SearchOptions{
|
||||
OrderBy: GetSearchOrderByBySortType(tt.sortType),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, int64(4), count)
|
||||
if assert.Len(t, projects, 4) {
|
||||
for i := range projects {
|
||||
assert.EqualValues(t, tt.wants[i], projects[i].ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,7 +456,7 @@ func repoStatsCorrectNumClosedPulls(ctx context.Context, id int64) error {
|
||||
return repo_model.UpdateRepoIssueNumbers(ctx, id, true, true)
|
||||
}
|
||||
|
||||
func statsQuery(args ...interface{}) func(context.Context) ([]map[string][]byte, error) {
|
||||
func statsQuery(args ...any) func(context.Context) ([]map[string][]byte, error) {
|
||||
return func(ctx context.Context) ([]map[string][]byte, error) {
|
||||
return db.GetEngine(ctx).Query(args...)
|
||||
}
|
||||
|
||||
@@ -65,13 +65,6 @@ func (a *Attachment) DownloadURL() string {
|
||||
return setting.AppURL + "attachments/" + url.PathEscape(a.UUID)
|
||||
}
|
||||
|
||||
// _____ __ __ .__ __
|
||||
// / _ \_/ |__/ |______ ____ | |__ _____ ____ _____/ |_
|
||||
// / /_\ \ __\ __\__ \ _/ ___\| | \ / \_/ __ \ / \ __\
|
||||
// / | \ | | | / __ \\ \___| Y \ Y Y \ ___/| | \ |
|
||||
// \____|__ /__| |__| (____ /\___ >___| /__|_| /\___ >___| /__|
|
||||
// \/ \/ \/ \/ \/ \/ \/
|
||||
|
||||
// ErrAttachmentNotExist represents a "AttachmentNotExist" kind of error.
|
||||
type ErrAttachmentNotExist struct {
|
||||
ID int64
|
||||
|
||||
@@ -33,6 +33,19 @@ func TestRepository_GetCollaborators(t *testing.T) {
|
||||
test(2)
|
||||
test(3)
|
||||
test(4)
|
||||
|
||||
// Test db.ListOptions
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 22})
|
||||
|
||||
collaborators1, err := repo_model.GetCollaborators(db.DefaultContext, repo.ID, db.ListOptions{PageSize: 1, Page: 1})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, collaborators1, 1)
|
||||
|
||||
collaborators2, err := repo_model.GetCollaborators(db.DefaultContext, repo.ID, db.ListOptions{PageSize: 1, Page: 2})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, collaborators2, 1)
|
||||
|
||||
assert.NotEqualValues(t, collaborators1[0].ID, collaborators2[0].ID)
|
||||
}
|
||||
|
||||
func TestRepository_IsCollaborator(t *testing.T) {
|
||||
@@ -66,5 +79,80 @@ func TestRepository_ChangeCollaborationAccessMode(t *testing.T) {
|
||||
|
||||
assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, unittest.NonexistentID, perm.AccessModeAdmin))
|
||||
|
||||
// Disvard invalid input.
|
||||
assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, 4, perm.AccessMode(unittest.NonexistentID)))
|
||||
|
||||
unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repo.ID})
|
||||
}
|
||||
|
||||
func TestRepository_CountCollaborators(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||
count, err := repo_model.CountCollaborators(repo1.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 2, count)
|
||||
|
||||
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 22})
|
||||
count, err = repo_model.CountCollaborators(repo2.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 2, count)
|
||||
|
||||
// Non-existent repository.
|
||||
count, err = repo_model.CountCollaborators(unittest.NonexistentID)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 0, count)
|
||||
}
|
||||
|
||||
func TestRepository_IsOwnerMemberCollaborator(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
|
||||
|
||||
// Organisation owner.
|
||||
actual, err := repo_model.IsOwnerMemberCollaborator(repo1, 2)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, actual)
|
||||
|
||||
// Team member.
|
||||
actual, err = repo_model.IsOwnerMemberCollaborator(repo1, 4)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, actual)
|
||||
|
||||
// Normal user.
|
||||
actual, err = repo_model.IsOwnerMemberCollaborator(repo1, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, actual)
|
||||
|
||||
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||
|
||||
// Collaborator.
|
||||
actual, err = repo_model.IsOwnerMemberCollaborator(repo2, 4)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, actual)
|
||||
|
||||
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 15})
|
||||
|
||||
// Repository owner.
|
||||
actual, err = repo_model.IsOwnerMemberCollaborator(repo3, 2)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, actual)
|
||||
}
|
||||
|
||||
func TestRepo_GetCollaboration(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||
|
||||
// Existing collaboration.
|
||||
collab, err := repo_model.GetCollaboration(db.DefaultContext, repo.ID, 4)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, collab)
|
||||
assert.EqualValues(t, 4, collab.UserID)
|
||||
assert.EqualValues(t, 4, collab.RepoID)
|
||||
|
||||
// Non-existing collaboration.
|
||||
collab, err = repo_model.GetCollaboration(db.DefaultContext, repo.ID, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, collab)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ type MergeStyle string
|
||||
const (
|
||||
// MergeStyleMerge create merge commit
|
||||
MergeStyleMerge MergeStyle = "merge"
|
||||
// MergeStyleRebase rebase before merging
|
||||
// MergeStyleRebase rebase before merging, and fast-forward
|
||||
MergeStyleRebase MergeStyle = "rebase"
|
||||
// MergeStyleRebaseMerge rebase before merging with merge commit (--no-ff)
|
||||
MergeStyleRebaseMerge MergeStyle = "rebase-merge"
|
||||
|
||||
@@ -105,7 +105,7 @@ func DeleteMirrorByRepoID(repoID int64) error {
|
||||
}
|
||||
|
||||
// MirrorsIterate iterates all mirror repositories.
|
||||
func MirrorsIterate(limit int, f func(idx int, bean interface{}) error) error {
|
||||
func MirrorsIterate(limit int, f func(idx int, bean any) error) error {
|
||||
sess := db.GetEngine(db.DefaultContext).
|
||||
Where("next_update_unix<=?", time.Now().Unix()).
|
||||
And("next_update_unix!=0").
|
||||
|
||||
@@ -127,7 +127,7 @@ func GetPushMirrorsSyncedOnCommit(ctx context.Context, repoID int64) ([]*PushMir
|
||||
}
|
||||
|
||||
// PushMirrorsIterate iterates all push-mirror repositories.
|
||||
func PushMirrorsIterate(ctx context.Context, limit int, f func(idx int, bean interface{}) error) error {
|
||||
func PushMirrorsIterate(ctx context.Context, limit int, f func(idx int, bean any) error) error {
|
||||
sess := db.GetEngine(ctx).
|
||||
Where("last_update + (`interval` / ?) <= ?", time.Second, time.Now().Unix()).
|
||||
And("`interval` != 0").
|
||||
|
||||
@@ -41,7 +41,7 @@ func TestPushMirrorsIterate(t *testing.T) {
|
||||
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
|
||||
repo_model.PushMirrorsIterate(db.DefaultContext, 1, func(idx int, bean interface{}) error {
|
||||
repo_model.PushMirrorsIterate(db.DefaultContext, 1, func(idx int, bean any) error {
|
||||
m, ok := bean.(*repo_model.PushMirror)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "test-1", m.RemoteName)
|
||||
|
||||
@@ -442,7 +442,7 @@ func UpdateReleasesMigrationsByType(gitServiceType structs.GitServiceType, origi
|
||||
_, err := db.GetEngine(db.DefaultContext).Table("release").
|
||||
Where("repo_id IN (SELECT id FROM repository WHERE original_service_type = ?)", gitServiceType).
|
||||
And("original_author_id = ?", originalAuthorID).
|
||||
Update(map[string]interface{}{
|
||||
Update(map[string]any{
|
||||
"publisher_id": posterID,
|
||||
"original_author": "",
|
||||
"original_author_id": 0,
|
||||
|
||||
@@ -560,7 +560,7 @@ func searchRepositoryByCondition(ctx context.Context, opts *SearchRepoOptions, c
|
||||
opts.OrderBy = db.SearchOrderByAlphabetically
|
||||
}
|
||||
|
||||
args := make([]interface{}, 0)
|
||||
args := make([]any, 0)
|
||||
if opts.PriorityOwnerID > 0 {
|
||||
opts.OrderBy = db.SearchOrderBy(fmt.Sprintf("CASE WHEN owner_id = ? THEN 0 ELSE owner_id END, %s", opts.OrderBy))
|
||||
args = append(args, opts.PriorityOwnerID)
|
||||
|
||||
@@ -43,7 +43,7 @@ func (n *Notice) TrStr() string {
|
||||
}
|
||||
|
||||
// CreateNotice creates new system notice.
|
||||
func CreateNotice(ctx context.Context, tp NoticeType, desc string, args ...interface{}) error {
|
||||
func CreateNotice(ctx context.Context, tp NoticeType, desc string, args ...any) error {
|
||||
if len(args) > 0 {
|
||||
desc = fmt.Sprintf(desc, args...)
|
||||
}
|
||||
@@ -55,7 +55,7 @@ func CreateNotice(ctx context.Context, tp NoticeType, desc string, args ...inter
|
||||
}
|
||||
|
||||
// CreateRepositoryNotice creates new system notice with type NoticeRepository.
|
||||
func CreateRepositoryNotice(desc string, args ...interface{}) error {
|
||||
func CreateRepositoryNotice(desc string, args ...any) error {
|
||||
// Note we use the db.DefaultContext here rather than passing in a context as the context may be cancelled
|
||||
return CreateNotice(db.DefaultContext, NoticeRepository, desc, args...)
|
||||
}
|
||||
|
||||
@@ -21,10 +21,10 @@ const (
|
||||
modelsCommentTypeComment = 0
|
||||
)
|
||||
|
||||
var consistencyCheckMap = make(map[string]func(t assert.TestingT, bean interface{}))
|
||||
var consistencyCheckMap = make(map[string]func(t assert.TestingT, bean any))
|
||||
|
||||
// CheckConsistencyFor test that all matching database entries are consistent
|
||||
func CheckConsistencyFor(t assert.TestingT, beansToCheck ...interface{}) {
|
||||
func CheckConsistencyFor(t assert.TestingT, beansToCheck ...any) {
|
||||
for _, bean := range beansToCheck {
|
||||
sliceType := reflect.SliceOf(reflect.TypeOf(bean))
|
||||
sliceValue := reflect.MakeSlice(sliceType, 0, 10)
|
||||
@@ -42,7 +42,7 @@ func CheckConsistencyFor(t assert.TestingT, beansToCheck ...interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
func checkForConsistency(t assert.TestingT, bean interface{}) {
|
||||
func checkForConsistency(t assert.TestingT, bean any) {
|
||||
tb, err := db.TableInfo(bean)
|
||||
assert.NoError(t, err)
|
||||
f := consistencyCheckMap[tb.Name]
|
||||
@@ -63,7 +63,7 @@ func init() {
|
||||
return i
|
||||
}
|
||||
|
||||
checkForUserConsistency := func(t assert.TestingT, bean interface{}) {
|
||||
checkForUserConsistency := func(t assert.TestingT, bean any) {
|
||||
user := reflectionWrap(bean)
|
||||
AssertCountByCond(t, "repository", builder.Eq{"owner_id": user.int("ID")}, user.int("NumRepos"))
|
||||
AssertCountByCond(t, "star", builder.Eq{"uid": user.int("ID")}, user.int("NumStars"))
|
||||
@@ -77,7 +77,7 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
checkForRepoConsistency := func(t assert.TestingT, bean interface{}) {
|
||||
checkForRepoConsistency := func(t assert.TestingT, bean any) {
|
||||
repo := reflectionWrap(bean)
|
||||
assert.Equal(t, repo.str("LowerName"), strings.ToLower(repo.str("Name")), "repo: %+v", repo)
|
||||
AssertCountByCond(t, "star", builder.Eq{"repo_id": repo.int("ID")}, repo.int("NumStars"))
|
||||
@@ -113,7 +113,7 @@ func init() {
|
||||
"Unexpected number of closed milestones for repo id: %d", repo.int("ID"))
|
||||
}
|
||||
|
||||
checkForIssueConsistency := func(t assert.TestingT, bean interface{}) {
|
||||
checkForIssueConsistency := func(t assert.TestingT, bean any) {
|
||||
issue := reflectionWrap(bean)
|
||||
typeComment := modelsCommentTypeComment
|
||||
actual := GetCountByCond(t, "comment", builder.Eq{"`type`": typeComment, "issue_id": issue.int("ID")})
|
||||
@@ -124,14 +124,14 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
checkForPullRequestConsistency := func(t assert.TestingT, bean interface{}) {
|
||||
checkForPullRequestConsistency := func(t assert.TestingT, bean any) {
|
||||
pr := reflectionWrap(bean)
|
||||
issueRow := AssertExistsAndLoadMap(t, "issue", builder.Eq{"id": pr.int("IssueID")})
|
||||
assert.True(t, parseBool(issueRow["is_pull"]))
|
||||
assert.EqualValues(t, parseInt(issueRow["index"]), pr.int("Index"), "Unexpected index for pull request id: %d", pr.int("ID"))
|
||||
}
|
||||
|
||||
checkForMilestoneConsistency := func(t assert.TestingT, bean interface{}) {
|
||||
checkForMilestoneConsistency := func(t assert.TestingT, bean any) {
|
||||
milestone := reflectionWrap(bean)
|
||||
AssertCountByCond(t, "issue", builder.Eq{"milestone_id": milestone.int("ID")}, milestone.int("NumIssues"))
|
||||
|
||||
@@ -145,7 +145,7 @@ func init() {
|
||||
assert.Equal(t, completeness, milestone.int("Completeness"))
|
||||
}
|
||||
|
||||
checkForLabelConsistency := func(t assert.TestingT, bean interface{}) {
|
||||
checkForLabelConsistency := func(t assert.TestingT, bean any) {
|
||||
label := reflectionWrap(bean)
|
||||
issueLabels, err := db.GetEngine(db.DefaultContext).Table("issue_label").
|
||||
Where(builder.Eq{"label_id": label.int("ID")}).
|
||||
@@ -166,13 +166,13 @@ func init() {
|
||||
assert.EqualValues(t, expected, label.int("NumClosedIssues"), "Unexpected number of closed issues for label id: %d", label.int("ID"))
|
||||
}
|
||||
|
||||
checkForTeamConsistency := func(t assert.TestingT, bean interface{}) {
|
||||
checkForTeamConsistency := func(t assert.TestingT, bean any) {
|
||||
team := reflectionWrap(bean)
|
||||
AssertCountByCond(t, "team_user", builder.Eq{"team_id": team.int("ID")}, team.int("NumMembers"))
|
||||
AssertCountByCond(t, "team_repo", builder.Eq{"team_id": team.int("ID")}, team.int("NumRepos"))
|
||||
}
|
||||
|
||||
checkForActionConsistency := func(t assert.TestingT, bean interface{}) {
|
||||
checkForActionConsistency := func(t assert.TestingT, bean any) {
|
||||
action := reflectionWrap(bean)
|
||||
if action.int("RepoID") != 1700 { // dangling intentional
|
||||
repoRow := AssertExistsAndLoadMap(t, "repository", builder.Eq{"id": action.int("RepoID")})
|
||||
|
||||
@@ -23,7 +23,7 @@ type reflectionValue struct {
|
||||
v reflect.Value
|
||||
}
|
||||
|
||||
func reflectionWrap(v interface{}) *reflectionValue {
|
||||
func reflectionWrap(v any) *reflectionValue {
|
||||
return &reflectionValue{v: reflect.ValueOf(v)}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ func FixturesDir() string {
|
||||
return fixturesDir
|
||||
}
|
||||
|
||||
func fatalTestError(fmtStr string, args ...interface{}) {
|
||||
func fatalTestError(fmtStr string, args ...any) {
|
||||
_, _ = fmt.Fprintf(os.Stderr, fmtStr, args...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -57,11 +57,11 @@ func (err ErrExternalLoginUserNotExist) Unwrap() error {
|
||||
|
||||
// ExternalLoginUser makes the connecting between some existing user and additional external login sources
|
||||
type ExternalLoginUser struct {
|
||||
ExternalID string `xorm:"pk NOT NULL"`
|
||||
UserID int64 `xorm:"INDEX NOT NULL"`
|
||||
LoginSourceID int64 `xorm:"pk NOT NULL"`
|
||||
RawData map[string]interface{} `xorm:"TEXT JSON"`
|
||||
Provider string `xorm:"index VARCHAR(25)"`
|
||||
ExternalID string `xorm:"pk NOT NULL"`
|
||||
UserID int64 `xorm:"INDEX NOT NULL"`
|
||||
LoginSourceID int64 `xorm:"pk NOT NULL"`
|
||||
RawData map[string]any `xorm:"TEXT JSON"`
|
||||
Provider string `xorm:"index VARCHAR(25)"`
|
||||
Email string
|
||||
Name string
|
||||
FirstName string
|
||||
|
||||
@@ -64,6 +64,10 @@ func NewUserRedirect(ctx context.Context, ID int64, oldUserName, newUserName str
|
||||
oldUserName = strings.ToLower(oldUserName)
|
||||
newUserName = strings.ToLower(newUserName)
|
||||
|
||||
if err := DeleteUserRedirect(ctx, oldUserName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := DeleteUserRedirect(ctx, newUserName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ func (t *HookTask) AfterLoad() {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *HookTask) simpleMarshalJSON(v interface{}) string {
|
||||
func (t *HookTask) simpleMarshalJSON(v any) string {
|
||||
p, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
log.Error("Marshal [%d]: %v", t.ID, err)
|
||||
|
||||
@@ -29,12 +29,28 @@ const (
|
||||
)
|
||||
|
||||
func WriteLogs(ctx context.Context, filename string, offset int64, rows []*runnerv1.LogRow) ([]int, error) {
|
||||
flag := os.O_WRONLY
|
||||
if offset == 0 {
|
||||
// Create file only if offset is 0, or it could result in content holes if the file doesn't exist.
|
||||
flag |= os.O_CREATE
|
||||
}
|
||||
name := DBFSPrefix + filename
|
||||
f, err := dbfs.OpenFile(ctx, name, os.O_WRONLY|os.O_CREATE)
|
||||
f, err := dbfs.OpenFile(ctx, name, flag)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dbfs OpenFile %q: %w", name, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
stat, err := f.Stat()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dbfs Stat %q: %w", name, err)
|
||||
}
|
||||
if stat.Size() < offset {
|
||||
// If the size is less than offset, refuse to write, or it could result in content holes.
|
||||
// However, if the size is greater than offset, we can still write to overwrite the content.
|
||||
return nil, fmt.Errorf("size of %q is less than offset", name)
|
||||
}
|
||||
|
||||
if _, err := f.Seek(offset, io.SeekStart); err != nil {
|
||||
return nil, fmt.Errorf("dbfs Seek %q: %w", name, err)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
model.OnDecodeNodeError = func(node yaml.Node, out interface{}, err error) {
|
||||
model.OnDecodeNodeError = func(node yaml.Node, out any, err error) {
|
||||
// Log the error instead of panic or fatal.
|
||||
// It will be a big job to refactor act/pkg/model to return decode error,
|
||||
// so we just log the error and return empty value, and improve it later.
|
||||
@@ -321,44 +321,47 @@ func matchIssuesEvent(commit *git.Commit, issuePayload *api.IssuePayload, evt *j
|
||||
}
|
||||
|
||||
func matchPullRequestEvent(commit *git.Commit, prPayload *api.PullRequestPayload, evt *jobparser.Event) bool {
|
||||
// with no special filter parameters
|
||||
if len(evt.Acts()) == 0 {
|
||||
acts := evt.Acts()
|
||||
activityTypeMatched := false
|
||||
matchTimes := 0
|
||||
|
||||
if vals, ok := acts["types"]; !ok {
|
||||
// defaultly, only pull request `opened`, `reopened` and `synchronized` will trigger workflow
|
||||
// See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request
|
||||
return prPayload.Action == api.HookIssueSynchronized || prPayload.Action == api.HookIssueOpened || prPayload.Action == api.HookIssueReOpened
|
||||
activityTypeMatched = prPayload.Action == api.HookIssueSynchronized || prPayload.Action == api.HookIssueOpened || prPayload.Action == api.HookIssueReOpened
|
||||
} else {
|
||||
// See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request
|
||||
// Actions with the same name:
|
||||
// opened, edited, closed, reopened, assigned, unassigned
|
||||
// Actions need to be converted:
|
||||
// synchronized -> synchronize
|
||||
// label_updated -> labeled
|
||||
// label_cleared -> unlabeled
|
||||
// Unsupported activity types:
|
||||
// converted_to_draft, ready_for_review, locked, unlocked, review_requested, review_request_removed, auto_merge_enabled, auto_merge_disabled
|
||||
|
||||
action := prPayload.Action
|
||||
switch action {
|
||||
case api.HookIssueSynchronized:
|
||||
action = "synchronize"
|
||||
case api.HookIssueLabelUpdated:
|
||||
action = "labeled"
|
||||
case api.HookIssueLabelCleared:
|
||||
action = "unlabeled"
|
||||
}
|
||||
log.Trace("matching pull_request %s with %v", action, vals)
|
||||
for _, val := range vals {
|
||||
if glob.MustCompile(val, '/').Match(string(action)) {
|
||||
activityTypeMatched = true
|
||||
matchTimes++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
matchTimes := 0
|
||||
// all acts conditions should be satisfied
|
||||
for cond, vals := range evt.Acts() {
|
||||
for cond, vals := range acts {
|
||||
switch cond {
|
||||
case "types":
|
||||
// See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request
|
||||
// Actions with the same name:
|
||||
// opened, edited, closed, reopened, assigned, unassigned
|
||||
// Actions need to be converted:
|
||||
// synchronized -> synchronize
|
||||
// label_updated -> labeled
|
||||
// label_cleared -> unlabeled
|
||||
// Unsupported activity types:
|
||||
// converted_to_draft, ready_for_review, locked, unlocked, review_requested, review_request_removed, auto_merge_enabled, auto_merge_disabled
|
||||
|
||||
action := prPayload.Action
|
||||
switch action {
|
||||
case api.HookIssueSynchronized:
|
||||
action = "synchronize"
|
||||
case api.HookIssueLabelUpdated:
|
||||
action = "labeled"
|
||||
case api.HookIssueLabelCleared:
|
||||
action = "unlabeled"
|
||||
}
|
||||
log.Trace("matching pull_request %s with %v", action, vals)
|
||||
for _, val := range vals {
|
||||
if glob.MustCompile(val, '/').Match(string(action)) {
|
||||
matchTimes++
|
||||
break
|
||||
}
|
||||
}
|
||||
case "branches":
|
||||
refName := git.RefName(prPayload.PullRequest.Base.Ref)
|
||||
patterns, err := workflowpattern.CompilePatterns(vals...)
|
||||
@@ -407,7 +410,7 @@ func matchPullRequestEvent(commit *git.Commit, prPayload *api.PullRequestPayload
|
||||
log.Warn("pull request event unsupported condition %q", cond)
|
||||
}
|
||||
}
|
||||
return matchTimes == len(evt.Acts())
|
||||
return activityTypeMatched && matchTimes == len(evt.Acts())
|
||||
}
|
||||
|
||||
func matchIssueCommentEvent(commit *git.Commit, issueCommentPayload *api.IssueCommentPayload, evt *jobparser.Event) bool {
|
||||
|
||||
@@ -57,6 +57,25 @@ func TestDetectMatched(t *testing.T) {
|
||||
yamlOn: "on: pull_request",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "HookEventPullRequest(pull_request) `closed` action doesn't match GithubEventPullRequest(pull_request) with no activity type",
|
||||
triggedEvent: webhook_module.HookEventPullRequest,
|
||||
payload: &api.PullRequestPayload{Action: api.HookIssueClosed},
|
||||
yamlOn: "on: pull_request",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "HookEventPullRequest(pull_request) `closed` action doesn't match GithubEventPullRequest(pull_request) with branches",
|
||||
triggedEvent: webhook_module.HookEventPullRequest,
|
||||
payload: &api.PullRequestPayload{
|
||||
Action: api.HookIssueClosed,
|
||||
PullRequest: &api.PullRequest{
|
||||
Base: &api.PRBranchInfo{},
|
||||
},
|
||||
},
|
||||
yamlOn: "on:\n pull_request:\n branches: [main]",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "HookEventPullRequest(pull_request) `label_updated` action matches githubEventPullRequest(pull_request) with `label` activity type",
|
||||
triggedEvent: webhook_module.HookEventPullRequest,
|
||||
|
||||
@@ -215,6 +215,7 @@ func (l *LayeredFS) WatchLocalChanges(ctx context.Context, callback func()) {
|
||||
log.Error("Unable to list directories for asset local file-system %q: %v", layer.localPath, err)
|
||||
continue
|
||||
}
|
||||
layerDirs = append(layerDirs, ".")
|
||||
for _, dir := range layerDirs {
|
||||
if err = watcher.Add(util.FilePathJoinAbs(layer.localPath, dir)); err != nil {
|
||||
log.Error("Unable to watch directory %s: %v", dir, err)
|
||||
|
||||
@@ -107,7 +107,7 @@ const TimeLimitCodeLength = 12 + 6 + 40
|
||||
|
||||
// CreateTimeLimitCode create a time limit code
|
||||
// code format: 12 length date time string + 6 minutes string + 40 sha1 encoded string
|
||||
func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string {
|
||||
func CreateTimeLimitCode(data string, minutes int, startInf any) string {
|
||||
format := "200601021504"
|
||||
|
||||
var start, end time.Time
|
||||
@@ -245,7 +245,7 @@ func SetupGiteaRoot() string {
|
||||
}
|
||||
|
||||
// FormatNumberSI format a number
|
||||
func FormatNumberSI(data interface{}) string {
|
||||
func FormatNumberSI(data any) string {
|
||||
var num int64
|
||||
if num1, ok := data.(int64); ok {
|
||||
num = num1
|
||||
|
||||
6
modules/cache/cache_redis.go
vendored
6
modules/cache/cache_redis.go
vendored
@@ -24,7 +24,7 @@ type RedisCacher struct {
|
||||
}
|
||||
|
||||
// toStr convert string/int/int64 interface to string. it's only used by the RedisCacher.Put internally
|
||||
func toStr(v interface{}) string {
|
||||
func toStr(v any) string {
|
||||
if v == nil {
|
||||
return ""
|
||||
}
|
||||
@@ -44,7 +44,7 @@ func toStr(v interface{}) string {
|
||||
|
||||
// Put puts value (string type) into cache with key and expire time.
|
||||
// If expired is 0, it lives forever.
|
||||
func (c *RedisCacher) Put(key string, val interface{}, expire int64) error {
|
||||
func (c *RedisCacher) Put(key string, val any, expire int64) error {
|
||||
// this function is not well-designed, it only puts string values into cache
|
||||
key = c.prefix + key
|
||||
if expire == 0 {
|
||||
@@ -65,7 +65,7 @@ func (c *RedisCacher) Put(key string, val interface{}, expire int64) error {
|
||||
}
|
||||
|
||||
// Get gets cached value by given key.
|
||||
func (c *RedisCacher) Get(key string) interface{} {
|
||||
func (c *RedisCacher) Get(key string) any {
|
||||
val, err := c.c.Get(graceful.GetManager().HammerContext(), c.prefix+key).Result()
|
||||
if err != nil {
|
||||
return nil
|
||||
|
||||
8
modules/cache/cache_twoqueue.go
vendored
8
modules/cache/cache_twoqueue.go
vendored
@@ -30,7 +30,7 @@ type TwoQueueCacheConfig struct {
|
||||
|
||||
// MemoryItem represents a memory cache item.
|
||||
type MemoryItem struct {
|
||||
Val interface{}
|
||||
Val any
|
||||
Created int64
|
||||
Timeout int64
|
||||
}
|
||||
@@ -43,7 +43,7 @@ func (item *MemoryItem) hasExpired() bool {
|
||||
var _ mc.Cache = &TwoQueueCache{}
|
||||
|
||||
// Put puts value into cache with key and expire time.
|
||||
func (c *TwoQueueCache) Put(key string, val interface{}, timeout int64) error {
|
||||
func (c *TwoQueueCache) Put(key string, val any, timeout int64) error {
|
||||
item := &MemoryItem{
|
||||
Val: val,
|
||||
Created: time.Now().Unix(),
|
||||
@@ -56,7 +56,7 @@ func (c *TwoQueueCache) Put(key string, val interface{}, timeout int64) error {
|
||||
}
|
||||
|
||||
// Get gets cached value by given key.
|
||||
func (c *TwoQueueCache) Get(key string) interface{} {
|
||||
func (c *TwoQueueCache) Get(key string) any {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
cached, ok := c.cache.Get(key)
|
||||
@@ -146,7 +146,7 @@ func (c *TwoQueueCache) Flush() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *TwoQueueCache) checkAndInvalidate(key interface{}) {
|
||||
func (c *TwoQueueCache) checkAndInvalidate(key any) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
cached, ok := c.cache.Peek(key)
|
||||
|
||||
@@ -90,7 +90,7 @@ Usage: %[1]s [-v] [-o output.go] ambiguous.json
|
||||
sort.Slice(tables, func(i, j int) bool {
|
||||
return tables[i].Locale < tables[j].Locale
|
||||
})
|
||||
data := map[string]interface{}{
|
||||
data := map[string]any{
|
||||
"Tables": tables,
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ Usage: %[1]s [-v] [-o output.go] ambiguous.json
|
||||
}
|
||||
}
|
||||
|
||||
func runTemplate(t *template.Template, filename string, data interface{}) error {
|
||||
func runTemplate(t *template.Template, filename string, data any) error {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
if err := t.Execute(buf, data); err != nil {
|
||||
return fmt.Errorf("unable to execute template: %w", err)
|
||||
@@ -172,17 +172,17 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{
|
||||
|
||||
`))
|
||||
|
||||
func logf(format string, args ...interface{}) {
|
||||
func logf(format string, args ...any) {
|
||||
fmt.Fprintf(os.Stderr, format+"\n", args...)
|
||||
}
|
||||
|
||||
func verbosef(format string, args ...interface{}) {
|
||||
func verbosef(format string, args ...any) {
|
||||
if verbose {
|
||||
logf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func fatalf(format string, args ...interface{}) {
|
||||
func fatalf(format string, args ...any) {
|
||||
logf("fatal: "+format+"\n", args...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ Usage: %[1]s [-v] [-o output.go]
|
||||
}
|
||||
}
|
||||
|
||||
func runTemplate(t *template.Template, filename string, data interface{}) error {
|
||||
func runTemplate(t *template.Template, filename string, data any) error {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
if err := t.Execute(buf, data); err != nil {
|
||||
return fmt.Errorf("unable to execute template: %w", err)
|
||||
@@ -105,17 +105,17 @@ var InvisibleRanges = &unicode.RangeTable{
|
||||
}
|
||||
`))
|
||||
|
||||
func logf(format string, args ...interface{}) {
|
||||
func logf(format string, args ...any) {
|
||||
fmt.Fprintf(os.Stderr, format+"\n", args...)
|
||||
}
|
||||
|
||||
func verbosef(format string, args ...interface{}) {
|
||||
func verbosef(format string, args ...any) {
|
||||
if verbose {
|
||||
logf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func fatalf(format string, args ...interface{}) {
|
||||
func fatalf(format string, args ...any) {
|
||||
logf("fatal: "+format+"\n", args...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ type routerLoggerOptions struct {
|
||||
Identity *string
|
||||
Start *time.Time
|
||||
ResponseWriter http.ResponseWriter
|
||||
Ctx map[string]interface{}
|
||||
Ctx map[string]any
|
||||
RequestID *string
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ func AccessLogger() func(http.Handler) http.Handler {
|
||||
Identity: &identity,
|
||||
Start: &start,
|
||||
ResponseWriter: rw,
|
||||
Ctx: map[string]interface{}{
|
||||
Ctx: map[string]any{
|
||||
"RemoteAddr": req.RemoteAddr,
|
||||
"RemoteHost": reqHost,
|
||||
"Req": req,
|
||||
|
||||
@@ -100,7 +100,7 @@ func (ctx *APIContext) ServerError(title string, err error) {
|
||||
|
||||
// Error responds with an error message to client with given obj as the message.
|
||||
// If status is 500, also it prints error to log.
|
||||
func (ctx *APIContext) Error(status int, title string, obj interface{}) {
|
||||
func (ctx *APIContext) Error(status int, title string, obj any) {
|
||||
var message string
|
||||
if err, ok := obj.(error); ok {
|
||||
message = err.Error()
|
||||
@@ -257,7 +257,7 @@ func APIContexter() func(http.Handler) http.Handler {
|
||||
|
||||
// NotFound handles 404s for APIContext
|
||||
// String will replace message, errors will be added to a slice
|
||||
func (ctx *APIContext) NotFound(objs ...interface{}) {
|
||||
func (ctx *APIContext) NotFound(objs ...any) {
|
||||
message := ctx.Tr("error.not_found")
|
||||
var errors []string
|
||||
for _, obj := range objs {
|
||||
@@ -273,7 +273,7 @@ func (ctx *APIContext) NotFound(objs ...interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusNotFound, map[string]interface{}{
|
||||
ctx.JSON(http.StatusNotFound, map[string]any{
|
||||
"message": message,
|
||||
"url": setting.API.SwaggerURL,
|
||||
"errors": errors,
|
||||
|
||||
@@ -124,7 +124,7 @@ func (b *Base) Error(status int, contents ...string) {
|
||||
}
|
||||
|
||||
// JSON render content as JSON
|
||||
func (b *Base) JSON(status int, content interface{}) {
|
||||
func (b *Base) JSON(status int, content any) {
|
||||
b.Resp.Header().Set("Content-Type", "application/json;charset=utf-8")
|
||||
b.Resp.WriteHeader(status)
|
||||
if err := json.NewEncoder(b.Resp).Encode(content); err != nil {
|
||||
|
||||
@@ -60,7 +60,7 @@ const (
|
||||
|
||||
// VerifyCaptcha verifies Captcha data
|
||||
// No-op if captchas are not enabled
|
||||
func VerifyCaptcha(ctx *Context, tpl base.TplName, form interface{}) {
|
||||
func VerifyCaptcha(ctx *Context, tpl base.TplName, form any) {
|
||||
if !setting.Service.EnableCaptcha {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
// Render represents a template render
|
||||
type Render interface {
|
||||
TemplateLookup(tmpl string) (templates.TemplateExecutor, error)
|
||||
HTML(w io.Writer, status int, name string, data interface{}) error
|
||||
HTML(w io.Writer, status int, name string, data any) error
|
||||
}
|
||||
|
||||
// Context represents context of a request.
|
||||
@@ -61,7 +61,7 @@ type Context struct {
|
||||
// TrHTMLEscapeArgs runs ".Locale.Tr()" but pre-escapes all arguments with html.EscapeString.
|
||||
// This is useful if the locale message is intended to only produce HTML content.
|
||||
func (ctx *Context) TrHTMLEscapeArgs(msg string, args ...string) string {
|
||||
trArgs := make([]interface{}, len(args))
|
||||
trArgs := make([]any, len(args))
|
||||
for i, arg := range args {
|
||||
trArgs[i] = html.EscapeString(arg)
|
||||
}
|
||||
|
||||
@@ -97,14 +97,14 @@ func (ctx *Context) HTML(status int, name base.TplName) {
|
||||
}
|
||||
|
||||
// RenderToString renders the template content to a string
|
||||
func (ctx *Context) RenderToString(name base.TplName, data map[string]interface{}) (string, error) {
|
||||
func (ctx *Context) RenderToString(name base.TplName, data map[string]any) (string, error) {
|
||||
var buf strings.Builder
|
||||
err := ctx.Render.HTML(&buf, http.StatusOK, string(name), data)
|
||||
return buf.String(), err
|
||||
}
|
||||
|
||||
// RenderWithErr used for page has form validation but need to prompt error to users.
|
||||
func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form interface{}) {
|
||||
func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form any) {
|
||||
if form != nil {
|
||||
middleware.AssignForm(form, ctx.Data)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ type packageAssignmentCtx struct {
|
||||
// PackageAssignment returns a middleware to handle Context.Package assignment
|
||||
func PackageAssignment() func(ctx *Context) {
|
||||
return func(ctx *Context) {
|
||||
errorFn := func(status int, title string, obj interface{}) {
|
||||
errorFn := func(status int, title string, obj any) {
|
||||
err, ok := obj.(error)
|
||||
if !ok {
|
||||
err = fmt.Errorf("%s", obj)
|
||||
@@ -57,7 +57,7 @@ func PackageAssignmentAPI() func(ctx *APIContext) {
|
||||
}
|
||||
}
|
||||
|
||||
func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, string, interface{})) *Package {
|
||||
func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, string, any)) *Package {
|
||||
pkg := &Package{
|
||||
Owner: ctx.ContextUser,
|
||||
}
|
||||
@@ -108,18 +108,28 @@ func determineAccessMode(ctx *Base, pkg *Package, doer *user_model.User) (perm.A
|
||||
|
||||
if doer != nil && !doer.IsGhost() {
|
||||
// 1. If user is logged in, check all team packages permissions
|
||||
teams, err := organization.GetUserOrgTeams(ctx, org.ID, doer.ID)
|
||||
var err error
|
||||
accessMode, err = org.GetOrgUserMaxAuthorizeLevel(doer.ID)
|
||||
if err != nil {
|
||||
return accessMode, err
|
||||
}
|
||||
for _, t := range teams {
|
||||
perm := t.UnitAccessMode(ctx, unit.TypePackages)
|
||||
if accessMode < perm {
|
||||
accessMode = perm
|
||||
// If access mode is less than write check every team for more permissions
|
||||
// The minimum possible access mode is read for org members
|
||||
if accessMode < perm.AccessModeWrite {
|
||||
teams, err := organization.GetUserOrgTeams(ctx, org.ID, doer.ID)
|
||||
if err != nil {
|
||||
return accessMode, err
|
||||
}
|
||||
for _, t := range teams {
|
||||
perm := t.UnitAccessMode(ctx, unit.TypePackages)
|
||||
if accessMode < perm {
|
||||
accessMode = perm
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if organization.HasOrgOrUserVisible(ctx, pkg.Owner, doer) {
|
||||
// 2. If user is non-login, check if org is visible to non-login user
|
||||
}
|
||||
if accessMode == perm.AccessModeNone && organization.HasOrgOrUserVisible(ctx, pkg.Owner, doer) {
|
||||
// 2. If user is unauthorized or no org member, check if org is visible
|
||||
accessMode = perm.AccessModeRead
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -32,7 +32,7 @@ func (p *Pagination) AddParam(ctx *Context, paramKey, ctxKey string) {
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
paramData := fmt.Sprintf("%v", ctx.Data[ctxKey]) // cast interface{} to string
|
||||
paramData := fmt.Sprintf("%v", ctx.Data[ctxKey]) // cast any to string
|
||||
urlParam := fmt.Sprintf("%s=%v", url.QueryEscape(paramKey), url.QueryEscape(paramData))
|
||||
p.urlParams = append(p.urlParams, urlParam)
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ func RequireRepoReaderOr(unitTypes ...unit.Type) func(ctx *Context) {
|
||||
}
|
||||
if log.IsTrace() {
|
||||
var format string
|
||||
var args []interface{}
|
||||
var args []any
|
||||
if ctx.IsSigned {
|
||||
format = "Permission Denied: User %-v cannot read ["
|
||||
args = append(args, ctx.Doer)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user