Merge branch 'main' into sync-issue-pr-and-more

This commit is contained in:
harryzcy 2023-07-21 21:06:21 -05:00 committed by GitHub
commit 78a00af8d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
73 changed files with 1209 additions and 803 deletions

File diff suppressed because one or more lines are too long

View File

@ -9,30 +9,31 @@ import (
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var (
// CmdActions represents the available actions sub-commands.
CmdActions = cli.Command{
CmdActions = &cli.Command{
Name: "actions",
Usage: "",
Description: "Commands for managing Gitea Actions",
Subcommands: []cli.Command{
Subcommands: []*cli.Command{
subcmdActionsGenRunnerToken,
},
}
subcmdActionsGenRunnerToken = cli.Command{
subcmdActionsGenRunnerToken = &cli.Command{
Name: "generate-runner-token",
Usage: "Generate a new token for a runner to use to register with the server",
Action: runGenerateActionsRunnerToken,
Aliases: []string{"grt"},
Flags: []cli.Flag{
cli.StringFlag{
Name: "scope, s",
Value: "",
Usage: "{owner}[/{repo}] - leave empty for a global runner",
&cli.StringFlag{
Name: "scope",
Aliases: []string{"s"},
Value: "",
Usage: "{owner}[/{repo}] - leave empty for a global runner",
},
},
}

View File

@ -26,15 +26,15 @@ import (
"code.gitea.io/gitea/services/auth/source/smtp"
repo_service "code.gitea.io/gitea/services/repository"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var (
// CmdAdmin represents the available admin sub-command.
CmdAdmin = cli.Command{
CmdAdmin = &cli.Command{
Name: "admin",
Usage: "Command line interface to perform common administrative operations",
Subcommands: []cli.Command{
Subcommands: []*cli.Command{
subcmdUser,
subcmdRepoSyncReleases,
subcmdRegenerate,
@ -43,37 +43,37 @@ var (
},
}
subcmdRepoSyncReleases = cli.Command{
subcmdRepoSyncReleases = &cli.Command{
Name: "repo-sync-releases",
Usage: "Synchronize repository releases with tags",
Action: runRepoSyncReleases,
}
subcmdRegenerate = cli.Command{
subcmdRegenerate = &cli.Command{
Name: "regenerate",
Usage: "Regenerate specific files",
Subcommands: []cli.Command{
Subcommands: []*cli.Command{
microcmdRegenHooks,
microcmdRegenKeys,
},
}
microcmdRegenHooks = cli.Command{
microcmdRegenHooks = &cli.Command{
Name: "hooks",
Usage: "Regenerate git-hooks",
Action: runRegenerateHooks,
}
microcmdRegenKeys = cli.Command{
microcmdRegenKeys = &cli.Command{
Name: "keys",
Usage: "Regenerate authorized_keys file",
Action: runRegenerateKeys,
}
subcmdAuth = cli.Command{
subcmdAuth = &cli.Command{
Name: "auth",
Usage: "Modify external auth providers",
Subcommands: []cli.Command{
Subcommands: []*cli.Command{
microcmdAuthAddOauth,
microcmdAuthUpdateOauth,
cmdAuthAddLdapBindDn,
@ -87,44 +87,44 @@ var (
},
}
microcmdAuthList = cli.Command{
microcmdAuthList = &cli.Command{
Name: "list",
Usage: "List auth sources",
Action: runListAuth,
Flags: []cli.Flag{
cli.IntFlag{
&cli.IntFlag{
Name: "min-width",
Usage: "Minimal cell width including any padding for the formatted table",
Value: 0,
},
cli.IntFlag{
&cli.IntFlag{
Name: "tab-width",
Usage: "width of tab characters in formatted table (equivalent number of spaces)",
Value: 8,
},
cli.IntFlag{
&cli.IntFlag{
Name: "padding",
Usage: "padding added to a cell before computing its width",
Value: 1,
},
cli.StringFlag{
&cli.StringFlag{
Name: "pad-char",
Usage: `ASCII char used for padding if padchar == '\\t', the Writer will assume that the width of a '\\t' in the formatted output is tabwidth, and cells are left-aligned independent of align_left (for correct-looking results, tabwidth must correspond to the tab width in the viewer displaying the result)`,
Value: "\t",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "vertical-bars",
Usage: "Set to true to print vertical bars between columns",
},
},
}
idFlag = cli.Int64Flag{
idFlag = &cli.Int64Flag{
Name: "id",
Usage: "ID of authentication source",
}
microcmdAuthDelete = cli.Command{
microcmdAuthDelete = &cli.Command{
Name: "delete",
Usage: "Delete specific auth source",
Flags: []cli.Flag{idFlag},
@ -132,207 +132,208 @@ var (
}
oauthCLIFlags = []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "name",
Value: "",
Usage: "Application Name",
},
cli.StringFlag{
&cli.StringFlag{
Name: "provider",
Value: "",
Usage: "OAuth2 Provider",
},
cli.StringFlag{
&cli.StringFlag{
Name: "key",
Value: "",
Usage: "Client ID (Key)",
},
cli.StringFlag{
&cli.StringFlag{
Name: "secret",
Value: "",
Usage: "Client Secret",
},
cli.StringFlag{
&cli.StringFlag{
Name: "auto-discover-url",
Value: "",
Usage: "OpenID Connect Auto Discovery URL (only required when using OpenID Connect as provider)",
},
cli.StringFlag{
&cli.StringFlag{
Name: "use-custom-urls",
Value: "false",
Usage: "Use custom URLs for GitLab/GitHub OAuth endpoints",
},
cli.StringFlag{
&cli.StringFlag{
Name: "custom-tenant-id",
Value: "",
Usage: "Use custom Tenant ID for OAuth endpoints",
},
cli.StringFlag{
&cli.StringFlag{
Name: "custom-auth-url",
Value: "",
Usage: "Use a custom Authorization URL (option for GitLab/GitHub)",
},
cli.StringFlag{
&cli.StringFlag{
Name: "custom-token-url",
Value: "",
Usage: "Use a custom Token URL (option for GitLab/GitHub)",
},
cli.StringFlag{
&cli.StringFlag{
Name: "custom-profile-url",
Value: "",
Usage: "Use a custom Profile URL (option for GitLab/GitHub)",
},
cli.StringFlag{
&cli.StringFlag{
Name: "custom-email-url",
Value: "",
Usage: "Use a custom Email URL (option for GitHub)",
},
cli.StringFlag{
&cli.StringFlag{
Name: "icon-url",
Value: "",
Usage: "Custom icon URL for OAuth2 login source",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "skip-local-2fa",
Usage: "Set to true to skip local 2fa for users authenticated by this source",
},
cli.StringSliceFlag{
&cli.StringSliceFlag{
Name: "scopes",
Value: nil,
Usage: "Scopes to request when to authenticate against this OAuth2 source",
},
cli.StringFlag{
&cli.StringFlag{
Name: "required-claim-name",
Value: "",
Usage: "Claim name that has to be set to allow users to login with this source",
},
cli.StringFlag{
&cli.StringFlag{
Name: "required-claim-value",
Value: "",
Usage: "Claim value that has to be set to allow users to login with this source",
},
cli.StringFlag{
&cli.StringFlag{
Name: "group-claim-name",
Value: "",
Usage: "Claim name providing group names for this source",
},
cli.StringFlag{
&cli.StringFlag{
Name: "admin-group",
Value: "",
Usage: "Group Claim value for administrator users",
},
cli.StringFlag{
&cli.StringFlag{
Name: "restricted-group",
Value: "",
Usage: "Group Claim value for restricted users",
},
cli.StringFlag{
&cli.StringFlag{
Name: "group-team-map",
Value: "",
Usage: "JSON mapping between groups and org teams",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "group-team-map-removal",
Usage: "Activate automatic team membership removal depending on groups",
},
}
microcmdAuthUpdateOauth = cli.Command{
microcmdAuthUpdateOauth = &cli.Command{
Name: "update-oauth",
Usage: "Update existing Oauth authentication source",
Action: runUpdateOauth,
Flags: append(oauthCLIFlags[:1], append([]cli.Flag{idFlag}, oauthCLIFlags[1:]...)...),
}
microcmdAuthAddOauth = cli.Command{
microcmdAuthAddOauth = &cli.Command{
Name: "add-oauth",
Usage: "Add new Oauth authentication source",
Action: runAddOauth,
Flags: oauthCLIFlags,
}
subcmdSendMail = cli.Command{
subcmdSendMail = &cli.Command{
Name: "sendmail",
Usage: "Send a message to all users",
Action: runSendMail,
Flags: []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "title",
Usage: `a title of a message`,
Value: "",
},
cli.StringFlag{
&cli.StringFlag{
Name: "content",
Usage: "a content of a message",
Value: "",
},
cli.BoolFlag{
Name: "force,f",
Usage: "A flag to bypass a confirmation step",
&cli.BoolFlag{
Name: "force",
Aliases: []string{"f"},
Usage: "A flag to bypass a confirmation step",
},
},
}
smtpCLIFlags = []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "name",
Value: "",
Usage: "Application Name",
},
cli.StringFlag{
&cli.StringFlag{
Name: "auth-type",
Value: "PLAIN",
Usage: "SMTP Authentication Type (PLAIN/LOGIN/CRAM-MD5) default PLAIN",
},
cli.StringFlag{
&cli.StringFlag{
Name: "host",
Value: "",
Usage: "SMTP Host",
},
cli.IntFlag{
&cli.IntFlag{
Name: "port",
Usage: "SMTP Port",
},
cli.BoolTFlag{
&cli.BoolFlag{
Name: "force-smtps",
Usage: "SMTPS is always used on port 465. Set this to force SMTPS on other ports.",
},
cli.BoolTFlag{
&cli.BoolFlag{
Name: "skip-verify",
Usage: "Skip TLS verify.",
},
cli.StringFlag{
&cli.StringFlag{
Name: "helo-hostname",
Value: "",
Usage: "Hostname sent with HELO. Leave blank to send current hostname",
},
cli.BoolTFlag{
&cli.BoolFlag{
Name: "disable-helo",
Usage: "Disable SMTP helo.",
},
cli.StringFlag{
&cli.StringFlag{
Name: "allowed-domains",
Value: "",
Usage: "Leave empty to allow all domains. Separate multiple domains with a comma (',')",
},
cli.BoolTFlag{
&cli.BoolFlag{
Name: "skip-local-2fa",
Usage: "Skip 2FA to log on.",
},
cli.BoolTFlag{
&cli.BoolFlag{
Name: "active",
Usage: "This Authentication Source is Activated.",
},
}
microcmdAuthAddSMTP = cli.Command{
microcmdAuthAddSMTP = &cli.Command{
Name: "add-smtp",
Usage: "Add new SMTP authentication source",
Action: runAddSMTP,
Flags: smtpCLIFlags,
}
microcmdAuthUpdateSMTP = cli.Command{
microcmdAuthUpdateSMTP = &cli.Command{
Name: "update-smtp",
Usage: "Update existing SMTP authentication source",
Action: runUpdateSMTP,
@ -611,19 +612,19 @@ func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
conf.AllowedDomains = c.String("allowed-domains")
}
if c.IsSet("force-smtps") {
conf.ForceSMTPS = c.BoolT("force-smtps")
conf.ForceSMTPS = c.Bool("force-smtps")
}
if c.IsSet("skip-verify") {
conf.SkipVerify = c.BoolT("skip-verify")
conf.SkipVerify = c.Bool("skip-verify")
}
if c.IsSet("helo-hostname") {
conf.HeloHostname = c.String("helo-hostname")
}
if c.IsSet("disable-helo") {
conf.DisableHelo = c.BoolT("disable-helo")
conf.DisableHelo = c.Bool("disable-helo")
}
if c.IsSet("skip-local-2fa") {
conf.SkipLocalTwoFA = c.BoolT("skip-local-2fa")
conf.SkipLocalTwoFA = c.Bool("skip-local-2fa")
}
return nil
}
@ -647,7 +648,7 @@ func runAddSMTP(c *cli.Context) error {
}
active := true
if c.IsSet("active") {
active = c.BoolT("active")
active = c.Bool("active")
}
var smtpConfig smtp.Source
@ -696,7 +697,7 @@ func runUpdateSMTP(c *cli.Context) error {
}
if c.IsSet("active") {
source.IsActive = c.BoolT("active")
source.IsActive = c.Bool("active")
}
source.Cfg = smtpConfig

View File

@ -11,7 +11,7 @@ import (
"code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/services/auth/source/ldap"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
type (
@ -25,117 +25,117 @@ type (
var (
commonLdapCLIFlags = []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "name",
Usage: "Authentication name.",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "not-active",
Usage: "Deactivate the authentication source.",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "active",
Usage: "Activate the authentication source.",
},
cli.StringFlag{
&cli.StringFlag{
Name: "security-protocol",
Usage: "Security protocol name.",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "skip-tls-verify",
Usage: "Disable TLS verification.",
},
cli.StringFlag{
&cli.StringFlag{
Name: "host",
Usage: "The address where the LDAP server can be reached.",
},
cli.IntFlag{
&cli.IntFlag{
Name: "port",
Usage: "The port to use when connecting to the LDAP server.",
},
cli.StringFlag{
&cli.StringFlag{
Name: "user-search-base",
Usage: "The LDAP base at which user accounts will be searched for.",
},
cli.StringFlag{
&cli.StringFlag{
Name: "user-filter",
Usage: "An LDAP filter declaring how to find the user record that is attempting to authenticate.",
},
cli.StringFlag{
&cli.StringFlag{
Name: "admin-filter",
Usage: "An LDAP filter specifying if a user should be given administrator privileges.",
},
cli.StringFlag{
&cli.StringFlag{
Name: "restricted-filter",
Usage: "An LDAP filter specifying if a user should be given restricted status.",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "allow-deactivate-all",
Usage: "Allow empty search results to deactivate all users.",
},
cli.StringFlag{
&cli.StringFlag{
Name: "username-attribute",
Usage: "The attribute of the users LDAP record containing the user name.",
},
cli.StringFlag{
&cli.StringFlag{
Name: "firstname-attribute",
Usage: "The attribute of the users LDAP record containing the users first name.",
},
cli.StringFlag{
&cli.StringFlag{
Name: "surname-attribute",
Usage: "The attribute of the users LDAP record containing the users surname.",
},
cli.StringFlag{
&cli.StringFlag{
Name: "email-attribute",
Usage: "The attribute of the users LDAP record containing the users email address.",
},
cli.StringFlag{
&cli.StringFlag{
Name: "public-ssh-key-attribute",
Usage: "The attribute of the users LDAP record containing the users public ssh key.",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "skip-local-2fa",
Usage: "Set to true to skip local 2fa for users authenticated by this source",
},
cli.StringFlag{
&cli.StringFlag{
Name: "avatar-attribute",
Usage: "The attribute of the users LDAP record containing the users avatar.",
},
}
ldapBindDnCLIFlags = append(commonLdapCLIFlags,
cli.StringFlag{
&cli.StringFlag{
Name: "bind-dn",
Usage: "The DN to bind to the LDAP server with when searching for the user.",
},
cli.StringFlag{
&cli.StringFlag{
Name: "bind-password",
Usage: "The password for the Bind DN, if any.",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "attributes-in-bind",
Usage: "Fetch attributes in bind DN context.",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "synchronize-users",
Usage: "Enable user synchronization.",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "disable-synchronize-users",
Usage: "Disable user synchronization.",
},
cli.UintFlag{
&cli.UintFlag{
Name: "page-size",
Usage: "Search page size.",
})
ldapSimpleAuthCLIFlags = append(commonLdapCLIFlags,
cli.StringFlag{
&cli.StringFlag{
Name: "user-dn",
Usage: "The users DN.",
})
cmdAuthAddLdapBindDn = cli.Command{
cmdAuthAddLdapBindDn = &cli.Command{
Name: "add-ldap",
Usage: "Add new LDAP (via Bind DN) authentication source",
Action: func(c *cli.Context) error {
@ -144,7 +144,7 @@ var (
Flags: ldapBindDnCLIFlags,
}
cmdAuthUpdateLdapBindDn = cli.Command{
cmdAuthUpdateLdapBindDn = &cli.Command{
Name: "update-ldap",
Usage: "Update existing LDAP (via Bind DN) authentication source",
Action: func(c *cli.Context) error {
@ -153,7 +153,7 @@ var (
Flags: append([]cli.Flag{idFlag}, ldapBindDnCLIFlags...),
}
cmdAuthAddLdapSimpleAuth = cli.Command{
cmdAuthAddLdapSimpleAuth = &cli.Command{
Name: "add-ldap-simple",
Usage: "Add new LDAP (simple auth) authentication source",
Action: func(c *cli.Context) error {
@ -162,7 +162,7 @@ var (
Flags: ldapSimpleAuthCLIFlags,
}
cmdAuthUpdateLdapSimpleAuth = cli.Command{
cmdAuthUpdateLdapSimpleAuth = &cli.Command{
Name: "update-ldap-simple",
Usage: "Update existing LDAP (simple auth) authentication source",
Action: func(c *cli.Context) error {

View File

@ -11,7 +11,7 @@ import (
"code.gitea.io/gitea/services/auth/source/ldap"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
func TestAddLdapBindDn(t *testing.T) {

View File

@ -4,13 +4,13 @@
package cmd
import (
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var subcmdUser = cli.Command{
var subcmdUser = &cli.Command{
Name: "user",
Usage: "Modify users",
Subcommands: []cli.Command{
Subcommands: []*cli.Command{
microcmdUserCreate,
microcmdUserList,
microcmdUserChangePassword,

View File

@ -12,23 +12,25 @@ import (
pwd "code.gitea.io/gitea/modules/auth/password"
"code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var microcmdUserChangePassword = cli.Command{
var microcmdUserChangePassword = &cli.Command{
Name: "change-password",
Usage: "Change a user's password",
Action: runChangePassword,
Flags: []cli.Flag{
cli.StringFlag{
Name: "username,u",
Value: "",
Usage: "The user to change password for",
&cli.StringFlag{
Name: "username",
Aliases: []string{"u"},
Value: "",
Usage: "The user to change password for",
},
cli.StringFlag{
Name: "password,p",
Value: "",
Usage: "New password to set for user",
&cli.StringFlag{
Name: "password",
Aliases: []string{"p"},
Value: "",
Usage: "New password to set for user",
},
},
}

View File

@ -14,52 +14,52 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var microcmdUserCreate = cli.Command{
var microcmdUserCreate = &cli.Command{
Name: "create",
Usage: "Create a new user in database",
Action: runCreateUser,
Flags: []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "name",
Usage: "Username. DEPRECATED: use username instead",
},
cli.StringFlag{
&cli.StringFlag{
Name: "username",
Usage: "Username",
},
cli.StringFlag{
&cli.StringFlag{
Name: "password",
Usage: "User password",
},
cli.StringFlag{
&cli.StringFlag{
Name: "email",
Usage: "User email address",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "admin",
Usage: "User is an admin",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "random-password",
Usage: "Generate a random password for the user",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "must-change-password",
Usage: "Set this option to false to prevent forcing the user to change their password after initial login, (Default: true)",
},
cli.IntFlag{
&cli.IntFlag{
Name: "random-password-length",
Usage: "Length of the random password to be generated",
Value: 12,
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "access-token",
Usage: "Generate access token for the user",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "restricted",
Usage: "Make a restricted user account",
},

View File

@ -11,26 +11,28 @@ import (
"code.gitea.io/gitea/modules/storage"
user_service "code.gitea.io/gitea/services/user"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var microcmdUserDelete = cli.Command{
var microcmdUserDelete = &cli.Command{
Name: "delete",
Usage: "Delete specific user by id, name or email",
Flags: []cli.Flag{
cli.Int64Flag{
&cli.Int64Flag{
Name: "id",
Usage: "ID of user of the user to delete",
},
cli.StringFlag{
Name: "username,u",
Usage: "Username of the user to delete",
&cli.StringFlag{
Name: "username",
Aliases: []string{"u"},
Usage: "Username of the user to delete",
},
cli.StringFlag{
Name: "email,e",
Usage: "Email of the user to delete",
&cli.StringFlag{
Name: "email",
Aliases: []string{"e"},
Usage: "Email of the user to delete",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "purge",
Usage: "Purge user, all their repositories, organizations and comments",
},

View File

@ -9,27 +9,29 @@ import (
auth_model "code.gitea.io/gitea/models/auth"
user_model "code.gitea.io/gitea/models/user"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var microcmdUserGenerateAccessToken = cli.Command{
var microcmdUserGenerateAccessToken = &cli.Command{
Name: "generate-access-token",
Usage: "Generate an access token for a specific user",
Flags: []cli.Flag{
cli.StringFlag{
Name: "username,u",
Usage: "Username",
&cli.StringFlag{
Name: "username",
Aliases: []string{"u"},
Usage: "Username",
},
cli.StringFlag{
Name: "token-name,t",
Usage: "Token name",
Value: "gitea-admin",
&cli.StringFlag{
Name: "token-name",
Aliases: []string{"t"},
Usage: "Token name",
Value: "gitea-admin",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "raw",
Usage: "Display only the token value",
},
cli.StringFlag{
&cli.StringFlag{
Name: "scopes",
Value: "",
Usage: "Comma separated list of scopes to apply to access token",

View File

@ -10,15 +10,15 @@ import (
user_model "code.gitea.io/gitea/models/user"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var microcmdUserList = cli.Command{
var microcmdUserList = &cli.Command{
Name: "list",
Usage: "List users",
Action: runListUsers,
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "admin",
Usage: "List only admin users",
},

View File

@ -9,23 +9,25 @@ import (
user_model "code.gitea.io/gitea/models/user"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var microcmdUserMustChangePassword = cli.Command{
var microcmdUserMustChangePassword = &cli.Command{
Name: "must-change-password",
Usage: "Set the must change password flag for the provided users or all users",
Action: runMustChangePassword,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "all,A",
Usage: "All users must change password, except those explicitly excluded with --exclude",
&cli.BoolFlag{
Name: "all",
Aliases: []string{"A"},
Usage: "All users must change password, except those explicitly excluded with --exclude",
},
cli.StringSliceFlag{
Name: "exclude,e",
Usage: "Do not change the must-change-password flag for these users",
&cli.StringSliceFlag{
Name: "exclude",
Aliases: []string{"e"},
Usage: "Do not change the must-change-password flag for these users",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "unset",
Usage: "Instead of setting the must-change-password flag, unset it",
},
@ -48,7 +50,7 @@ func runMustChangePassword(c *cli.Context) error {
return err
}
n, err := user_model.SetMustChangePassword(ctx, all, mustChangePassword, c.Args(), exclude)
n, err := user_model.SetMustChangePassword(ctx, all, mustChangePassword, c.Args().Slice(), exclude)
if err != nil {
return err
}

View File

@ -20,43 +20,43 @@ import (
"strings"
"time"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
// CmdCert represents the available cert sub-command.
var CmdCert = cli.Command{
var CmdCert = &cli.Command{
Name: "cert",
Usage: "Generate self-signed certificate",
Description: `Generate a self-signed X.509 certificate for a TLS server.
Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`,
Action: runCert,
Flags: []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "host",
Value: "",
Usage: "Comma-separated hostnames and IPs to generate a certificate for",
},
cli.StringFlag{
&cli.StringFlag{
Name: "ecdsa-curve",
Value: "",
Usage: "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521",
},
cli.IntFlag{
&cli.IntFlag{
Name: "rsa-bits",
Value: 2048,
Usage: "Size of RSA key to generate. Ignored if --ecdsa-curve is set",
},
cli.StringFlag{
&cli.StringFlag{
Name: "start-date",
Value: "",
Usage: "Creation date formatted as Jan 1 15:04:05 2011",
},
cli.DurationFlag{
&cli.DurationFlag{
Name: "duration",
Value: 365 * 24 * time.Hour,
Usage: "Duration that certificate is valid for",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "ca",
Usage: "whether this cert should be its own Certificate Authority",
},

View File

@ -20,7 +20,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
// argsSet checks that all the required arguments are set. args is a list of
@ -109,15 +109,24 @@ func setupConsoleLogger(level log.Level, colorize bool, out io.Writer) {
log.GetManager().GetLogger(log.DEFAULT).ReplaceAllWriters(writer)
}
func globalBool(c *cli.Context, name string) bool {
for _, ctx := range c.Lineage() {
if ctx.Bool(name) {
return true
}
}
return false
}
// 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") {
if globalBool(c, "quiet") {
level = log.FATAL
}
if c.Bool("debug") || c.GlobalBool("debug") || c.Bool("verbose") || c.GlobalBool("verbose") {
if globalBool(c, "debug") || globalBool(c, "verbose") {
level = log.TRACE
}
log.SetConsoleLogger(log.DEFAULT, "console-default", level)

View File

@ -10,11 +10,11 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
// CmdConvert represents the available convert sub-command.
var CmdConvert = cli.Command{
var CmdConvert = &cli.Command{
Name: "convert",
Usage: "Convert the database",
Description: "A command to convert an existing MySQL database from utf8 to utf8mb4 or MSSQL database from varchar to nvarchar",

View File

@ -8,11 +8,11 @@ import (
"os"
"strings"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
// CmdDocs represents the available docs sub-command.
var CmdDocs = cli.Command{
var CmdDocs = &cli.Command{
Name: "docs",
Usage: "Output CLI documentation",
Description: "A command to output Gitea's CLI documentation, optionally to a file.",
@ -23,8 +23,9 @@ var CmdDocs = cli.Command{
Usage: "Output man pages instead",
},
&cli.StringFlag{
Name: "output, o",
Usage: "Path to output to instead of stdout (will overwrite if exists)",
Name: "output",
Aliases: []string{"o"},
Usage: "Path to output to instead of stdout (will overwrite if exists)",
},
},
}

View File

@ -18,57 +18,58 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
"xorm.io/xorm"
)
// CmdDoctor represents the available doctor sub-command.
var CmdDoctor = cli.Command{
var CmdDoctor = &cli.Command{
Name: "doctor",
Usage: "Diagnose and optionally fix problems",
Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
Action: runDoctor,
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "list",
Usage: "List the available checks",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "default",
Usage: "Run the default checks (if neither --run or --all is set, this is the default behaviour)",
},
cli.StringSliceFlag{
&cli.StringSliceFlag{
Name: "run",
Usage: "Run the provided checks - (if --default is set, the default checks will also run)",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "all",
Usage: "Run all the available checks",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "fix",
Usage: "Automatically fix what we can",
},
cli.StringFlag{
&cli.StringFlag{
Name: "log-file",
Usage: `Name of the log file (default: "doctor.log"). Set to "-" to output to stdout, set to "" to disable`,
},
cli.BoolFlag{
Name: "color, H",
Usage: "Use color for outputted information",
&cli.BoolFlag{
Name: "color",
Aliases: []string{"H"},
Usage: "Use color for outputted information",
},
},
Subcommands: []cli.Command{
Subcommands: []*cli.Command{
cmdRecreateTable,
},
}
var cmdRecreateTable = cli.Command{
var cmdRecreateTable = &cli.Command{
Name: "recreate-table",
Usage: "Recreate tables from XORM definitions and copy the data.",
ArgsUsage: "[TABLE]... : (TABLEs to recreate - leave blank for all)",
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "debug",
Usage: "Print SQL commands sent",
},

View File

@ -22,7 +22,7 @@ import (
"gitea.com/go-chi/session"
"github.com/mholt/archiver/v3"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
func addReader(w archiver.Writer, r io.ReadCloser, info os.FileInfo, customName string, verbose bool) error {
@ -96,64 +96,71 @@ var outputTypeEnum = &outputType{
}
// CmdDump represents the available dump sub-command.
var CmdDump = cli.Command{
var CmdDump = &cli.Command{
Name: "dump",
Usage: "Dump Gitea files and database",
Description: `Dump compresses all related files and database into zip file.
It can be used for backup and capture Gitea server image to send to maintainer`,
Action: runDump,
Flags: []cli.Flag{
cli.StringFlag{
Name: "file, f",
Value: fmt.Sprintf("gitea-dump-%d.zip", time.Now().Unix()),
Usage: "Name of the dump file which will be created. Supply '-' for stdout. See type for available types.",
&cli.StringFlag{
Name: "file",
Aliases: []string{"f"},
Value: fmt.Sprintf("gitea-dump-%d.zip", time.Now().Unix()),
Usage: "Name of the dump file which will be created. Supply '-' for stdout. See type for available types.",
},
cli.BoolFlag{
Name: "verbose, V",
Usage: "Show process details",
&cli.BoolFlag{
Name: "verbose",
Aliases: []string{"V"},
Usage: "Show process details",
},
cli.BoolFlag{
Name: "quiet, q",
Usage: "Only display warnings and errors",
&cli.BoolFlag{
Name: "quiet",
Aliases: []string{"q"},
Usage: "Only display warnings and errors",
},
cli.StringFlag{
Name: "tempdir, t",
Value: os.TempDir(),
Usage: "Temporary dir path",
&cli.StringFlag{
Name: "tempdir",
Aliases: []string{"t"},
Value: os.TempDir(),
Usage: "Temporary dir path",
},
cli.StringFlag{
Name: "database, d",
Usage: "Specify the database SQL syntax",
&cli.StringFlag{
Name: "database",
Aliases: []string{"d"},
Usage: "Specify the database SQL syntax",
},
cli.BoolFlag{
Name: "skip-repository, R",
Usage: "Skip the repository dumping",
&cli.BoolFlag{
Name: "skip-repository",
Aliases: []string{"R"},
Usage: "Skip the repository dumping",
},
cli.BoolFlag{
Name: "skip-log, L",
Usage: "Skip the log dumping",
&cli.BoolFlag{
Name: "skip-log",
Aliases: []string{"L"},
Usage: "Skip the log dumping",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "skip-custom-dir",
Usage: "Skip custom directory",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "skip-lfs-data",
Usage: "Skip LFS data",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "skip-attachment-data",
Usage: "Skip attachment data",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "skip-package-data",
Usage: "Skip package data",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "skip-index",
Usage: "Skip bleve index data",
},
cli.GenericFlag{
&cli.GenericFlag{
Name: "type",
Value: outputTypeEnum,
Usage: fmt.Sprintf("Dump output format: %s", outputTypeEnum.Join()),

View File

@ -19,57 +19,58 @@ import (
"code.gitea.io/gitea/services/convert"
"code.gitea.io/gitea/services/migrations"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
// CmdDumpRepository represents the available dump repository sub-command.
var CmdDumpRepository = cli.Command{
var CmdDumpRepository = &cli.Command{
Name: "dump-repo",
Usage: "Dump the repository from git/github/gitea/gitlab",
Description: "This is a command for dumping the repository data.",
Action: runDumpRepository,
Flags: []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "git_service",
Value: "",
Usage: "Git service, git, github, gitea, gitlab. If clone_addr could be recognized, this could be ignored.",
},
cli.StringFlag{
Name: "repo_dir, r",
Value: "./data",
Usage: "Repository dir path to store the data",
&cli.StringFlag{
Name: "repo_dir",
Aliases: []string{"r"},
Value: "./data",
Usage: "Repository dir path to store the data",
},
cli.StringFlag{
&cli.StringFlag{
Name: "clone_addr",
Value: "",
Usage: "The URL will be clone, currently could be a git/github/gitea/gitlab http/https URL",
},
cli.StringFlag{
&cli.StringFlag{
Name: "auth_username",
Value: "",
Usage: "The username to visit the clone_addr",
},
cli.StringFlag{
&cli.StringFlag{
Name: "auth_password",
Value: "",
Usage: "The password to visit the clone_addr",
},
cli.StringFlag{
&cli.StringFlag{
Name: "auth_token",
Value: "",
Usage: "The personal token to visit the clone_addr",
},
cli.StringFlag{
&cli.StringFlag{
Name: "owner_name",
Value: "",
Usage: "The data will be stored on a directory with owner name if not empty",
},
cli.StringFlag{
&cli.StringFlag{
Name: "repo_name",
Value: "",
Usage: "The data will be stored on a directory with repository name if not empty",
},
cli.StringFlag{
&cli.StringFlag{
Name: "units",
Value: "",
Usage: `Which items will be migrated, one or more units should be separated as comma.

View File

@ -19,70 +19,74 @@ import (
"code.gitea.io/gitea/modules/util"
"github.com/gobwas/glob"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
// 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",
Subcommands: []cli.Command{
Subcommands: []*cli.Command{
subcmdList,
subcmdView,
subcmdExtract,
},
}
subcmdList = cli.Command{
subcmdList = &cli.Command{
Name: "list",
Usage: "List files matching the given pattern",
Action: runList,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "include-vendored,vendor",
Usage: "Include files under public/vendor as well",
&cli.BoolFlag{
Name: "include-vendored",
Aliases: []string{"vendor"},
Usage: "Include files under public/vendor as well",
},
},
}
subcmdView = cli.Command{
subcmdView = &cli.Command{
Name: "view",
Usage: "View a file matching the given pattern",
Action: runView,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "include-vendored,vendor",
Usage: "Include files under public/vendor as well",
&cli.BoolFlag{
Name: "include-vendored",
Aliases: []string{"vendor"},
Usage: "Include files under public/vendor as well",
},
},
}
subcmdExtract = cli.Command{
subcmdExtract = &cli.Command{
Name: "extract",
Usage: "Extract resources",
Action: runExtract,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "include-vendored,vendor",
Usage: "Include files under public/vendor as well",
&cli.BoolFlag{
Name: "include-vendored",
Aliases: []string{"vendor"},
Usage: "Include files under public/vendor as well",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "overwrite",
Usage: "Overwrite files if they already exist",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "rename",
Usage: "Rename files as {name}.bak if they already exist (overwrites previous .bak)",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "custom",
Usage: "Extract to the 'custom' directory as per app.ini",
},
cli.StringFlag{
Name: "destination,dest-dir",
Usage: "Extract to the specified directory",
&cli.StringFlag{
Name: "destination",
Aliases: []string{"dest-dir"},
Usage: "Extract to the specified directory",
},
},
}
@ -99,7 +103,7 @@ type assetFile struct {
func initEmbeddedExtractor(c *cli.Context) error {
setupConsoleLogger(log.ERROR, log.CanColorStderr, os.Stderr)
patterns, err := compileCollectPatterns(c.Args())
patterns, err := compileCollectPatterns(c.Args().Slice())
if err != nil {
return err
}
@ -175,7 +179,7 @@ func runExtractDo(c *cli.Context) error {
return err
}
if len(c.Args()) == 0 {
if c.NArg() == 0 {
return fmt.Errorf("a list of pattern of files to extract is mandatory (e.g. '**' for all)")
}

View File

@ -11,43 +11,43 @@ import (
"code.gitea.io/gitea/modules/generate"
"github.com/mattn/go-isatty"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var (
// CmdGenerate represents the available generate sub-command.
CmdGenerate = cli.Command{
CmdGenerate = &cli.Command{
Name: "generate",
Usage: "Command line interface for running generators",
Subcommands: []cli.Command{
Subcommands: []*cli.Command{
subcmdSecret,
},
}
subcmdSecret = cli.Command{
subcmdSecret = &cli.Command{
Name: "secret",
Usage: "Generate a secret token",
Subcommands: []cli.Command{
Subcommands: []*cli.Command{
microcmdGenerateInternalToken,
microcmdGenerateLfsJwtSecret,
microcmdGenerateSecretKey,
},
}
microcmdGenerateInternalToken = cli.Command{
microcmdGenerateInternalToken = &cli.Command{
Name: "INTERNAL_TOKEN",
Usage: "Generate a new INTERNAL_TOKEN",
Action: runGenerateInternalToken,
}
microcmdGenerateLfsJwtSecret = cli.Command{
microcmdGenerateLfsJwtSecret = &cli.Command{
Name: "JWT_SECRET",
Aliases: []string{"LFS_JWT_SECRET"},
Usage: "Generate a new JWT_SECRET",
Action: runGenerateLfsJwtSecret,
}
microcmdGenerateSecretKey = cli.Command{
microcmdGenerateSecretKey = &cli.Command{
Name: "SECRET_KEY",
Usage: "Generate a new SECRET_KEY",
Action: runGenerateSecretKey,

View File

@ -20,7 +20,7 @@ import (
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
const (
@ -29,12 +29,12 @@ const (
var (
// CmdHook represents the available hooks sub-command.
CmdHook = cli.Command{
CmdHook = &cli.Command{
Name: "hook",
Usage: "Delegate commands to corresponding Git hooks",
Description: "This should only be called by Git",
Before: PrepareConsoleLoggerLevel(log.FATAL),
Subcommands: []cli.Command{
Subcommands: []*cli.Command{
subcmdHookPreReceive,
subcmdHookUpdate,
subcmdHookPostReceive,
@ -42,47 +42,47 @@ var (
},
}
subcmdHookPreReceive = cli.Command{
subcmdHookPreReceive = &cli.Command{
Name: "pre-receive",
Usage: "Delegate pre-receive Git hook",
Description: "This command should only be called by Git",
Action: runHookPreReceive,
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "debug",
},
},
}
subcmdHookUpdate = cli.Command{
subcmdHookUpdate = &cli.Command{
Name: "update",
Usage: "Delegate update Git hook",
Description: "This command should only be called by Git",
Action: runHookUpdate,
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "debug",
},
},
}
subcmdHookPostReceive = cli.Command{
subcmdHookPostReceive = &cli.Command{
Name: "post-receive",
Usage: "Delegate post-receive Git hook",
Description: "This command should only be called by Git",
Action: runHookPostReceive,
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "debug",
},
},
}
// Note: new hook since git 2.29
subcmdHookProcReceive = cli.Command{
subcmdHookProcReceive = &cli.Command{
Name: "proc-receive",
Usage: "Delegate proc-receive Git hook",
Description: "This command should only be called by Git",
Action: runHookProcReceive,
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "debug",
},
},

View File

@ -11,35 +11,39 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
// CmdKeys represents the available keys sub-command
var CmdKeys = cli.Command{
var CmdKeys = &cli.Command{
Name: "keys",
Usage: "This command queries the Gitea database to get the authorized command for a given ssh key fingerprint",
Before: PrepareConsoleLoggerLevel(log.FATAL),
Action: runKeys,
Flags: []cli.Flag{
cli.StringFlag{
Name: "expected, e",
Value: "git",
Usage: "Expected user for whom provide key commands",
&cli.StringFlag{
Name: "expected",
Aliases: []string{"e"},
Value: "git",
Usage: "Expected user for whom provide key commands",
},
cli.StringFlag{
Name: "username, u",
Value: "",
Usage: "Username trying to log in by SSH",
&cli.StringFlag{
Name: "username",
Aliases: []string{"u"},
Value: "",
Usage: "Username trying to log in by SSH",
},
cli.StringFlag{
Name: "type, t",
Value: "",
Usage: "Type of the SSH key provided to the SSH Server (requires content to be provided too)",
&cli.StringFlag{
Name: "type",
Aliases: []string{"t"},
Value: "",
Usage: "Type of the SSH key provided to the SSH Server (requires content to be provided too)",
},
cli.StringFlag{
Name: "content, k",
Value: "",
Usage: "Base64 encoded content of the SSH key provided to the SSH Server (requires type to be provided too)",
&cli.StringFlag{
Name: "content",
Aliases: []string{"k"},
Value: "",
Usage: "Base64 encoded content of the SSH key provided to the SSH Server (requires type to be provided too)",
},
},
}
@ -73,6 +77,6 @@ func runKeys(c *cli.Context) error {
if extra.Error != nil {
return extra.Error
}
fmt.Println(strings.TrimSpace(authorizedString))
_, _ = fmt.Fprintln(c.App.Writer, strings.TrimSpace(authorizedString))
return nil
}

View File

@ -9,7 +9,7 @@ import (
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
func runSendMail(c *cli.Context) error {

196
cmd/main.go Normal file
View File

@ -0,0 +1,196 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"fmt"
"os"
"reflect"
"strings"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli/v2"
)
// cmdHelp is our own help subcommand with more information
func cmdHelp() *cli.Command {
c := &cli.Command{
Name: "help",
Aliases: []string{"h"},
Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]",
Action: func(c *cli.Context) (err error) {
args := c.Args()
if args.Present() {
err = cli.ShowCommandHelp(c, args.First())
} else {
err = cli.ShowAppHelp(c)
}
_, _ = fmt.Fprintf(c.App.Writer, `
DEFAULT CONFIGURATION:
AppPath: %s
WorkPath: %s
CustomPath: %s
ConfigFile: %s
`, setting.AppPath, setting.AppWorkPath, setting.CustomPath, setting.CustomConf)
return err
},
}
return c
}
var helpFlag = cli.HelpFlag
func init() {
// cli.HelpFlag = nil TODO: after https://github.com/urfave/cli/issues/1794 we can use this
}
func appGlobalFlags() []cli.Flag {
return []cli.Flag{
// make the builtin flags at the top
helpFlag,
cli.VersionFlag,
// 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.
&cli.StringFlag{
Name: "custom-path",
Aliases: []string{"C"},
Usage: "Set custom path (defaults to '{WorkPath}/custom')",
},
&cli.StringFlag{
Name: "config",
Aliases: []string{"c"},
Value: setting.CustomConf,
Usage: "Set custom config file (defaults to '{WorkPath}/custom/conf/app.ini')",
},
&cli.StringFlag{
Name: "work-path",
Aliases: []string{"w"},
Usage: "Set Gitea's working path (defaults to the Gitea's binary directory)",
},
}
}
func prepareSubcommandWithConfig(command *cli.Command, globalFlags []cli.Flag) {
command.Flags = append(append([]cli.Flag{}, globalFlags...), command.Flags...)
command.Action = prepareWorkPathAndCustomConf(command.Action)
command.HideHelp = true
if command.Name != "help" {
command.Subcommands = append(command.Subcommands, cmdHelp())
}
for i := range command.Subcommands {
prepareSubcommandWithConfig(command.Subcommands[i], globalFlags)
}
}
// 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(action cli.ActionFunc) func(ctx *cli.Context) error {
return func(ctx *cli.Context) error {
var args setting.ArgWorkPathAndCustomConf
ctxLineage := ctx.Lineage()
for i := len(ctxLineage) - 1; i >= 0; i-- {
curCtx := ctxLineage[i]
if curCtx.IsSet("work-path") && args.WorkPath == "" {
args.WorkPath = curCtx.String("work-path")
}
if curCtx.IsSet("custom-path") && args.CustomPath == "" {
args.CustomPath = curCtx.String("custom-path")
}
if curCtx.IsSet("config") && args.CustomConf == "" {
args.CustomConf = curCtx.String("config")
}
}
setting.InitWorkPathAndCommonConfig(os.Getenv, args)
if ctx.Bool("help") || action == nil {
// the default behavior of "urfave/cli": "nil action" means "show help"
return cmdHelp().Action(ctx)
}
return action(ctx)
}
}
func reflectGet(v any, fieldName string) any {
e := reflect.ValueOf(v).Elem()
return e.FieldByName(fieldName).Interface()
}
// https://cli.urfave.org/migrate-v1-to-v2/#flag-aliases-are-done-differently
// Sadly v2 doesn't warn you if a comma is in the name. (https://github.com/urfave/cli/issues/1103)
func checkCommandFlags(c any) bool {
var cmds []*cli.Command
if app, ok := c.(*cli.App); ok {
cmds = app.Commands
} else {
cmds = c.(*cli.Command).Subcommands
}
ok := true
for _, cmd := range cmds {
for _, flag := range cmd.Flags {
flagName := reflectGet(flag, "Name").(string)
if strings.Contains(flagName, ",") {
ok = false
log.Error("cli.Flag can't have comma in its Name: %q, use Aliases instead", flagName)
}
}
if !checkCommandFlags(cmd) {
ok = false
}
}
return ok
}
func NewMainApp() *cli.App {
app := cli.NewApp()
app.EnableBashCompletion = true
// these sub-commands need to use config file
subCmdWithConfig := []*cli.Command{
CmdWeb,
CmdServ,
CmdHook,
CmdDump,
CmdAdmin,
CmdMigrate,
CmdKeys,
CmdConvert,
CmdDoctor,
CmdManager,
CmdEmbedded,
CmdMigrateStorage,
CmdDumpRepository,
CmdRestoreRepository,
CmdActions,
cmdHelp(), // the "help" sub-command was used to show the more information for "work path" and "custom config"
}
// these sub-commands do not need the config file, and they do not depend on any path or environment variable.
subCmdStandalone := []*cli.Command{
CmdCert,
CmdGenerate,
CmdDocs,
}
app.DefaultCommand = CmdWeb.Name
globalFlags := appGlobalFlags()
app.Flags = append(app.Flags, globalFlags...)
app.HideHelp = true // use our own help action to show helps (with more information like default config)
app.Before = PrepareConsoleLoggerLevel(log.INFO)
for i := range subCmdWithConfig {
prepareSubcommandWithConfig(subCmdWithConfig[i], globalFlags)
}
app.Commands = append(app.Commands, subCmdWithConfig...)
app.Commands = append(app.Commands, subCmdStandalone...)
if !checkCommandFlags(app) {
panic("some flags are incorrect") // this is a runtime check to help developers
}
return app
}

View File

@ -4,9 +4,17 @@
package cmd
import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli/v2"
)
func TestMain(m *testing.M) {
@ -14,3 +22,110 @@ func TestMain(m *testing.M) {
GiteaRootPath: "..",
})
}
func makePathOutput(workPath, customPath, customConf string) string {
return fmt.Sprintf("WorkPath=%s\nCustomPath=%s\nCustomConf=%s", workPath, customPath, customConf)
}
func newTestApp() *cli.App {
app := NewMainApp()
testCmd := &cli.Command{
Name: "test-cmd",
Action: func(ctx *cli.Context) error {
_, _ = fmt.Fprint(app.Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
return nil
},
}
prepareSubcommandWithConfig(testCmd, appGlobalFlags())
app.Commands = append(app.Commands, testCmd)
app.DefaultCommand = testCmd.Name
return app
}
func TestCliCmd(t *testing.T) {
defaultWorkPath := filepath.Dir(setting.AppPath)
defaultCustomPath := filepath.Join(defaultWorkPath, "custom")
defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini")
cli.CommandHelpTemplate = "(command help template)"
cli.AppHelpTemplate = "(app help template)"
cli.SubcommandHelpTemplate = "(subcommand help template)"
cases := []struct {
env map[string]string
cmd string
exp string
}{
// main command help
{
cmd: "./gitea help",
exp: "DEFAULT CONFIGURATION:",
},
// parse paths
{
cmd: "./gitea test-cmd",
exp: makePathOutput(defaultWorkPath, defaultCustomPath, defaultCustomConf),
},
{
cmd: "./gitea -c /tmp/app.ini test-cmd",
exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"),
},
{
cmd: "./gitea test-cmd -c /tmp/app.ini",
exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"),
},
{
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
cmd: "./gitea test-cmd",
exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/custom/conf/app.ini"),
},
{
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
cmd: "./gitea test-cmd --work-path /tmp/other",
exp: makePathOutput("/tmp/other", "/tmp/other/custom", "/tmp/other/custom/conf/app.ini"),
},
{
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
cmd: "./gitea test-cmd --config /tmp/app-other.ini",
exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/app-other.ini"),
},
}
app := newTestApp()
var envBackup []string
for _, s := range os.Environ() {
if strings.HasPrefix(s, "GITEA_") && strings.Contains(s, "=") {
envBackup = append(envBackup, s)
}
}
clearGiteaEnv := func() {
for _, s := range os.Environ() {
if strings.HasPrefix(s, "GITEA_") {
_ = os.Unsetenv(s)
}
}
}
defer func() {
clearGiteaEnv()
for _, s := range envBackup {
k, v, _ := strings.Cut(s, "=")
_ = os.Setenv(k, v)
}
}()
for _, c := range cases {
clearGiteaEnv()
for k, v := range c.env {
_ = os.Setenv(k, v)
}
args := strings.Split(c.cmd, " ") // for test only, "split" is good enough
out := new(strings.Builder)
app.Writer = out
err := app.Run(args)
assert.NoError(t, err, c.cmd)
assert.NotEmpty(t, c.exp, c.cmd)
outStr := out.String()
assert.Contains(t, outStr, c.exp, c.cmd)
}
}

View File

@ -9,16 +9,16 @@ import (
"code.gitea.io/gitea/modules/private"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var (
// CmdManager represents the manager command
CmdManager = cli.Command{
CmdManager = &cli.Command{
Name: "manager",
Usage: "Manage the running gitea process",
Description: "This is a command for managing the running gitea process",
Subcommands: []cli.Command{
Subcommands: []*cli.Command{
subcmdShutdown,
subcmdRestart,
subcmdReloadTemplates,
@ -27,80 +27,80 @@ var (
subCmdProcesses,
},
}
subcmdShutdown = cli.Command{
subcmdShutdown = &cli.Command{
Name: "shutdown",
Usage: "Gracefully shutdown the running process",
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "debug",
},
},
Action: runShutdown,
}
subcmdRestart = cli.Command{
subcmdRestart = &cli.Command{
Name: "restart",
Usage: "Gracefully restart the running process - (not implemented for windows servers)",
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "debug",
},
},
Action: runRestart,
}
subcmdReloadTemplates = cli.Command{
subcmdReloadTemplates = &cli.Command{
Name: "reload-templates",
Usage: "Reload template files in the running process",
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "debug",
},
},
Action: runReloadTemplates,
}
subcmdFlushQueues = cli.Command{
subcmdFlushQueues = &cli.Command{
Name: "flush-queues",
Usage: "Flush queues in the running process",
Action: runFlushQueues,
Flags: []cli.Flag{
cli.DurationFlag{
&cli.DurationFlag{
Name: "timeout",
Value: 60 * time.Second,
Usage: "Timeout for the flushing process",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "non-blocking",
Usage: "Set to true to not wait for flush to complete before returning",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "debug",
},
},
}
subCmdProcesses = cli.Command{
subCmdProcesses = &cli.Command{
Name: "processes",
Usage: "Display running processes within the current process",
Action: runProcesses,
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "debug",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "flat",
Usage: "Show processes as flat table rather than as tree",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "no-system",
Usage: "Do not show system processes",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "stacktraces",
Usage: "Show stacktraces",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "json",
Usage: "Output as json",
},
cli.StringFlag{
&cli.StringFlag{
Name: "cancel",
Usage: "Process PID to cancel. (Only available for non-system processes.)",
},

View File

@ -10,49 +10,61 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var (
defaultLoggingFlags = []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "logger",
Usage: `Logger name - will default to "default"`,
}, cli.StringFlag{
},
&cli.StringFlag{
Name: "writer",
Usage: "Name of the log writer - will default to mode",
}, cli.StringFlag{
},
&cli.StringFlag{
Name: "level",
Usage: "Logging level for the new logger",
}, cli.StringFlag{
Name: "stacktrace-level, L",
Usage: "Stacktrace logging level",
}, cli.StringFlag{
Name: "flags, F",
Usage: "Flags for the logger",
}, cli.StringFlag{
Name: "expression, e",
Usage: "Matching expression for the logger",
}, cli.StringFlag{
Name: "prefix, p",
Usage: "Prefix for the logger",
}, cli.BoolFlag{
},
&cli.StringFlag{
Name: "stacktrace-level",
Aliases: []string{"L"},
Usage: "Stacktrace logging level",
},
&cli.StringFlag{
Name: "flags",
Aliases: []string{"F"},
Usage: "Flags for the logger",
},
&cli.StringFlag{
Name: "expression",
Aliases: []string{"e"},
Usage: "Matching expression for the logger",
},
&cli.StringFlag{
Name: "prefix",
Aliases: []string{"p"},
Usage: "Prefix for the logger",
},
&cli.BoolFlag{
Name: "color",
Usage: "Use color in the logs",
}, cli.BoolFlag{
},
&cli.BoolFlag{
Name: "debug",
},
}
subcmdLogging = cli.Command{
subcmdLogging = &cli.Command{
Name: "logging",
Usage: "Adjust logging commands",
Subcommands: []cli.Command{
Subcommands: []*cli.Command{
{
Name: "pause",
Usage: "Pause logging (Gitea will buffer logs up to a certain point and will drop them after that point)",
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "debug",
},
},
@ -61,7 +73,7 @@ var (
Name: "resume",
Usage: "Resume logging",
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "debug",
},
},
@ -70,7 +82,7 @@ var (
Name: "release-and-reopen",
Usage: "Cause Gitea to release and re-open files used for logging",
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "debug",
},
},
@ -80,9 +92,9 @@ var (
Usage: "Remove a logger",
ArgsUsage: "[name] Name of logger to remove",
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "debug",
}, cli.StringFlag{
}, &cli.StringFlag{
Name: "logger",
Usage: `Logger name - will default to "default"`,
},
@ -91,32 +103,45 @@ var (
}, {
Name: "add",
Usage: "Add a logger",
Subcommands: []cli.Command{
Subcommands: []*cli.Command{
{
Name: "file",
Usage: "Add a file logger",
Flags: append(defaultLoggingFlags, []cli.Flag{
cli.StringFlag{
Name: "filename, f",
Usage: "Filename for the logger - this must be set.",
}, cli.BoolTFlag{
Name: "rotate, r",
Usage: "Rotate logs",
}, cli.Int64Flag{
Name: "max-size, s",
Usage: "Maximum size in bytes before rotation",
}, cli.BoolTFlag{
Name: "daily, d",
Usage: "Rotate logs daily",
}, cli.IntFlag{
Name: "max-days, D",
Usage: "Maximum number of daily logs to keep",
}, cli.BoolTFlag{
Name: "compress, z",
Usage: "Compress rotated logs",
}, cli.IntFlag{
Name: "compression-level, Z",
Usage: "Compression level to use",
&cli.StringFlag{
Name: "filename",
Aliases: []string{"f"},
Usage: "Filename for the logger - this must be set.",
},
&cli.BoolFlag{
Name: "rotate",
Aliases: []string{"r"},
Usage: "Rotate logs",
},
&cli.Int64Flag{
Name: "max-size",
Aliases: []string{"s"},
Usage: "Maximum size in bytes before rotation",
},
&cli.BoolFlag{
Name: "daily",
Aliases: []string{"d"},
Usage: "Rotate logs daily",
},
&cli.IntFlag{
Name: "max-days",
Aliases: []string{"D"},
Usage: "Maximum number of daily logs to keep",
},
&cli.BoolFlag{
Name: "compress",
Aliases: []string{"z"},
Usage: "Compress rotated logs",
},
&cli.IntFlag{
Name: "compression-level",
Aliases: []string{"Z"},
Usage: "Compression level to use",
},
}...),
Action: runAddFileLogger,
@ -124,18 +149,25 @@ var (
Name: "conn",
Usage: "Add a net conn logger",
Flags: append(defaultLoggingFlags, []cli.Flag{
cli.BoolFlag{
Name: "reconnect-on-message, R",
Usage: "Reconnect to host for every message",
}, cli.BoolFlag{
Name: "reconnect, r",
Usage: "Reconnect to host when connection is dropped",
}, cli.StringFlag{
Name: "protocol, P",
Usage: "Set protocol to use: tcp, unix, or udp (defaults to tcp)",
}, cli.StringFlag{
Name: "address, a",
Usage: "Host address and port to connect to (defaults to :7020)",
&cli.BoolFlag{
Name: "reconnect-on-message",
Aliases: []string{"R"},
Usage: "Reconnect to host for every message",
},
&cli.BoolFlag{
Name: "reconnect",
Aliases: []string{"r"},
Usage: "Reconnect to host when connection is dropped",
},
&cli.StringFlag{
Name: "protocol",
Aliases: []string{"P"},
Usage: "Set protocol to use: tcp, unix, or udp (defaults to tcp)",
},
&cli.StringFlag{
Name: "address",
Aliases: []string{"a"},
Usage: "Host address and port to connect to (defaults to :7020)",
},
}...),
Action: runAddConnLogger,
@ -145,9 +177,10 @@ var (
Name: "log-sql",
Usage: "Set LogSQL",
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "debug",
}, cli.BoolFlag{
},
&cli.BoolFlag{
Name: "off",
Usage: "Switch off SQL logging",
},

View File

@ -11,11 +11,11 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
// CmdMigrate represents the available migrate sub-command.
var CmdMigrate = cli.Command{
var CmdMigrate = &cli.Command{
Name: "migrate",
Usage: "Migrate the database",
Description: "This is a command for migrating the database, so that you can run gitea admin create-user before starting the server.",

View File

@ -20,70 +20,73 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
// CmdMigrateStorage represents the available migrate storage sub-command.
var CmdMigrateStorage = cli.Command{
var CmdMigrateStorage = &cli.Command{
Name: "migrate-storage",
Usage: "Migrate the storage",
Description: "Copies stored files from storage configured in app.ini to parameter-configured storage",
Action: runMigrateStorage,
Flags: []cli.Flag{
cli.StringFlag{
Name: "type, t",
Value: "",
Usage: "Type of stored files to copy. Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages', 'actions-log'",
&cli.StringFlag{
Name: "type",
Aliases: []string{"t"},
Value: "",
Usage: "Type of stored files to copy. Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages', 'actions-log'",
},
cli.StringFlag{
Name: "storage, s",
Value: "",
Usage: "New storage type: local (default) or minio",
&cli.StringFlag{
Name: "storage",
Aliases: []string{"s"},
Value: "",
Usage: "New storage type: local (default) or minio",
},
cli.StringFlag{
Name: "path, p",
Value: "",
Usage: "New storage placement if store is local (leave blank for default)",
&cli.StringFlag{
Name: "path",
Aliases: []string{"p"},
Value: "",
Usage: "New storage placement if store is local (leave blank for default)",
},
cli.StringFlag{
&cli.StringFlag{
Name: "minio-endpoint",
Value: "",
Usage: "Minio storage endpoint",
},
cli.StringFlag{
&cli.StringFlag{
Name: "minio-access-key-id",
Value: "",
Usage: "Minio storage accessKeyID",
},
cli.StringFlag{
&cli.StringFlag{
Name: "minio-secret-access-key",
Value: "",
Usage: "Minio storage secretAccessKey",
},
cli.StringFlag{
&cli.StringFlag{
Name: "minio-bucket",
Value: "",
Usage: "Minio storage bucket",
},
cli.StringFlag{
&cli.StringFlag{
Name: "minio-location",
Value: "",
Usage: "Minio storage location to create bucket",
},
cli.StringFlag{
&cli.StringFlag{
Name: "minio-base-path",
Value: "",
Usage: "Minio storage base path on the bucket",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "minio-use-ssl",
Usage: "Enable SSL for minio",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "minio-insecure-skip-verify",
Usage: "Skip SSL verification",
},
cli.StringFlag{
&cli.StringFlag{
Name: "minio-checksum-algorithm",
Value: "",
Usage: "Minio checksum algorithm (default/md5)",

View File

@ -9,38 +9,39 @@ import (
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
// CmdRestoreRepository represents the available restore a repository sub-command.
var CmdRestoreRepository = cli.Command{
var CmdRestoreRepository = &cli.Command{
Name: "restore-repo",
Usage: "Restore the repository from disk",
Description: "This is a command for restoring the repository data.",
Action: runRestoreRepository,
Flags: []cli.Flag{
cli.StringFlag{
Name: "repo_dir, r",
Value: "./data",
Usage: "Repository dir path to restore from",
&cli.StringFlag{
Name: "repo_dir",
Aliases: []string{"r"},
Value: "./data",
Usage: "Repository dir path to restore from",
},
cli.StringFlag{
&cli.StringFlag{
Name: "owner_name",
Value: "",
Usage: "Restore destination owner name",
},
cli.StringFlag{
&cli.StringFlag{
Name: "repo_name",
Value: "",
Usage: "Restore destination repository name",
},
cli.StringFlag{
&cli.StringFlag{
Name: "units",
Value: "",
Usage: `Which items will be restored, one or more units should be separated as comma.
wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`,
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "validation",
Usage: "Sanity check the content of the files before trying to load them",
},

View File

@ -32,7 +32,7 @@ import (
"github.com/golang-jwt/jwt/v5"
"github.com/kballard/go-shellquote"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
const (
@ -40,17 +40,17 @@ const (
)
// CmdServ represents the available serv sub-command.
var CmdServ = cli.Command{
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{
&cli.BoolFlag{
Name: "enable-pprof",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "debug",
},
},
@ -119,7 +119,7 @@ func fail(ctx context.Context, userMessage, logMsgFmt string, args ...any) error
}
_ = private.SSHLog(ctx, true, logMsg)
}
return cli.NewExitError("", 1)
return cli.Exit("", 1)
}
// handleCliResponseExtra handles the extra response from the cli sub-commands
@ -130,7 +130,7 @@ func handleCliResponseExtra(extra private.ResponseExtra) error {
_, _ = fmt.Fprintln(os.Stdout, extra.UserMsg)
}
if extra.HasError() {
return cli.NewExitError(extra.Error, 1)
return cli.Exit(extra.Error, 1)
}
return nil
}
@ -147,20 +147,20 @@ func runServ(c *cli.Context) error {
return nil
}
if len(c.Args()) < 1 {
if c.NArg() < 1 {
if err := cli.ShowSubcommandHelp(c); err != nil {
fmt.Printf("error showing subcommand help: %v\n", err)
}
return nil
}
keys := strings.Split(c.Args()[0], "-")
keys := strings.Split(c.Args().First(), "-")
if len(keys) != 2 || keys[0] != "key" {
return fail(ctx, "Key ID format error", "Invalid key argument: %s", c.Args()[0])
return fail(ctx, "Key ID format error", "Invalid key argument: %s", c.Args().First())
}
keyID, err := strconv.ParseInt(keys[1], 10, 64)
if err != nil {
return fail(ctx, "Key ID parsing error", "Invalid key argument: %s", c.Args()[1])
return fail(ctx, "Key ID parsing error", "Invalid key argument: %s", c.Args().Get(1))
}
cmd := os.Getenv("SSH_ORIGINAL_COMMAND")

View File

@ -15,22 +15,24 @@ import (
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/public"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/routers"
"code.gitea.io/gitea/routers/install"
"github.com/felixge/fgprof"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
// PIDFile could be set from build tag
var PIDFile = "/run/gitea.pid"
// CmdWeb represents the available web sub-command.
var CmdWeb = cli.Command{
var CmdWeb = &cli.Command{
Name: "web",
Usage: "Start Gitea web server",
Description: `Gitea web server is the only thing you need to run,
@ -38,26 +40,29 @@ and it takes care of all the other things for you`,
Before: PrepareConsoleLoggerLevel(log.INFO),
Action: runWeb,
Flags: []cli.Flag{
cli.StringFlag{
Name: "port, p",
Value: "3000",
Usage: "Temporary port number to prevent conflict",
&cli.StringFlag{
Name: "port",
Aliases: []string{"p"},
Value: "3000",
Usage: "Temporary port number to prevent conflict",
},
cli.StringFlag{
&cli.StringFlag{
Name: "install-port",
Value: "3000",
Usage: "Temporary port number to run the install page on to prevent conflict",
},
cli.StringFlag{
Name: "pid, P",
Value: PIDFile,
Usage: "Custom pid file path",
&cli.StringFlag{
Name: "pid",
Aliases: []string{"P"},
Value: PIDFile,
Usage: "Custom pid file path",
},
cli.BoolFlag{
Name: "quiet, q",
Usage: "Only display Fatal logging errors until logging is set-up",
&cli.BoolFlag{
Name: "quiet",
Aliases: []string{"q"},
Usage: "Only display Fatal logging errors until logging is set-up",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "verbose",
Usage: "Set initial logging to TRACE level until logging is properly set-up",
},
@ -172,6 +177,20 @@ func serveInstalled(ctx *cli.Context) error {
}
}
// in old versions, user's custom web files are placed in "custom/public", and they were served as "http://domain.com/assets/xxx"
// now, Gitea only serves pre-defined files in the "custom/public" folder basing on the web root, the user should move their custom files to "custom/public/assets"
publicFiles, _ := public.AssetFS().ListFiles(".")
publicFilesSet := container.SetOf(publicFiles...)
publicFilesSet.Remove(".well-known")
publicFilesSet.Remove("assets")
publicFilesSet.Remove("robots.txt")
for _, fn := range publicFilesSet.Values() {
log.Error("Found legacy public asset %q in CustomPath. Please move it to %s/public/assets/%s", fn, setting.CustomPath, fn)
}
if _, err := os.Stat(filepath.Join(setting.CustomPath, "robots.txt")); err == nil {
log.Error(`Found legacy public asset "robots.txt" in CustomPath. Please move it to %s/public/robots.txt`, setting.CustomPath)
}
routers.InitWebInstalled(graceful.GetManager().HammerContext())
// We check that AppDataPath exists here (it should have been created during installation)

View File

@ -18,7 +18,7 @@ import (
"syscall"
"github.com/google/go-github/v53/github"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
"gopkg.in/yaml.v3"
)
@ -32,55 +32,55 @@ func main() {
app.ArgsUsage = "<PR-to-backport>"
app.Flags = []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "version",
Usage: "Version branch to backport on to",
},
cli.StringFlag{
&cli.StringFlag{
Name: "upstream",
Value: "origin",
Usage: "Upstream remote for the Gitea upstream",
},
cli.StringFlag{
&cli.StringFlag{
Name: "release-branch",
Value: "",
Usage: "Release branch to backport on. Will default to release/<version>",
},
cli.StringFlag{
&cli.StringFlag{
Name: "cherry-pick",
Usage: "SHA to cherry-pick as backport",
},
cli.StringFlag{
&cli.StringFlag{
Name: "backport-branch",
Usage: "Backport branch to backport on to (default: backport-<pr>-<version>",
},
cli.StringFlag{
&cli.StringFlag{
Name: "remote",
Value: "",
Usage: "Remote for your fork of the Gitea upstream",
},
cli.StringFlag{
&cli.StringFlag{
Name: "fork-user",
Value: "",
Usage: "Forked user name on Github",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "no-fetch",
Usage: "Set this flag to prevent fetch of remote branches",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "no-amend-message",
Usage: "Set this flag to prevent automatic amendment of the commit message",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "no-push",
Usage: "Set this flag to prevent pushing the backport up to your fork",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "no-xdg-open",
Usage: "Set this flag to not use xdg-open to open the PR URL",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "continue",
Usage: "Set this flag to continue from a git cherry-pick that has broken",
},
@ -151,7 +151,7 @@ func runBackport(c *cli.Context) error {
localReleaseBranch := path.Join(upstream, upstreamReleaseBranch)
args := c.Args()
args := c.Args().Slice()
if len(args) == 0 && pr == "" {
return fmt.Errorf("no PR number provided\nProvide a PR number to backport")
} else if len(args) != 1 && pr == "" {

View File

@ -9,7 +9,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
func main() {
@ -46,22 +46,22 @@ func main() {
and "GITEA__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found
on the configuration cheat sheet.`
app.Flags = []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "custom-path, C",
Value: setting.CustomPath,
Usage: "Custom path file path",
},
cli.StringFlag{
&cli.StringFlag{
Name: "config, c",
Value: setting.CustomConf,
Usage: "Custom configuration file path",
},
cli.StringFlag{
&cli.StringFlag{
Name: "work-path, w",
Value: setting.AppWorkPath,
Usage: "Set the gitea working path",
},
cli.StringFlag{
&cli.StringFlag{
Name: "out, o",
Value: "",
Usage: "Destination file to write to",

View File

@ -12,7 +12,7 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; These values are environment-dependent but form the basis of a lot of values. They will be
;; reported as part of the default configuration when running `gitea --help` or on start-up. The order they are emitted there is slightly different but we will list them here in the order they are set-up.
;; reported as part of the default configuration when running `gitea help` or on start-up. The order they are emitted there is slightly different but we will list them here in the order they are set-up.
;;
;; - _`AppPath`_: This is the absolute path of the running gitea binary.
;; - _`AppWorkPath`_: This refers to "working path" of the `gitea` binary. It is determined by using the first set thing in the following hierarchy:

View File

@ -40,7 +40,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
## Default Configuration (non-`app.ini` configuration)
These values are environment-dependent but form the basis of a lot of values. They will be
reported as part of the default configuration when running `gitea --help` or on start-up. The order they are emitted there is slightly different but we will list them here in the order they are set-up.
reported as part of the default configuration when running `gitea help` or on start-up. The order they are emitted there is slightly different but we will list them here in the order they are set-up.
- _`AppPath`_: This is the absolute path of the running gitea binary.
- _`AppWorkPath`_: This refers to "working path" of the `gitea` binary. It is determined by using the first set thing in the following hierarchy:

View File

@ -56,7 +56,11 @@ is set under the "Configuration" tab on the site administration page.
To make Gitea serve custom public files (like pages and images), use the folder
`$GITEA_CUSTOM/public/` as the webroot. Symbolic links will be followed.
At the moment, only files in the `public/assets/` folder are served.
At the moment, only the following files are served:
- `public/robots.txt`
- files in the `public/.well-known/` folder
- files in the `public/assets/` folder
For example, a file `image.png` stored in `$GITEA_CUSTOM/public/assets/`, can be accessed with
the url `http://gitea.domain.tld/assets/image.png`.

3
go.mod
View File

@ -98,7 +98,7 @@ require (
github.com/syndtr/goleveldb v1.0.0
github.com/tstranex/u2f v1.0.0
github.com/ulikunitz/xz v0.5.11
github.com/urfave/cli v1.22.14
github.com/urfave/cli/v2 v2.25.7
github.com/xanzy/go-gitlab v0.86.0
github.com/xeipuuv/gojsonschema v1.2.0
github.com/yohcop/openid-go v1.0.1
@ -278,6 +278,7 @@ require (
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect
go.etcd.io/bbolt v1.3.7 // indirect
go.mongodb.org/mongo-driver v1.12.0 // indirect

7
go.sum
View File

@ -80,7 +80,6 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/ClickHouse/ch-go v0.57.0 h1:X/QmUmFhpUvLgPSQb7fWOSi1wvqGn6tJ7w2a59c4xsg=
github.com/ClickHouse/ch-go v0.57.0/go.mod h1:DR3iBn7OrrDj+KeUp1LbdxLEUDbW+5Qwdl/qkc+PQ+Y=
@ -1162,8 +1161,8 @@ github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.37.1-0.20220607072126-8a320890c08d/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I=
@ -1198,6 +1197,8 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofm
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yohcop/openid-go v1.0.1 h1:DPRd3iPO5F6O5zX2e62XpVAbPT6wV51cuucH0z9g3js=
github.com/yohcop/openid-go v1.0.1/go.mod h1:b/AvD03P0KHj4yuihb+VtLD6bYYgsy0zqBzPCRjkCNs=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=

154
main.go
View File

@ -2,8 +2,7 @@
// Copyright 2016 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
// Gitea (git with a cup of tea) is a painless self-hosted Git Service.
package main // import "code.gitea.io/gitea"
package main
import (
"fmt"
@ -22,17 +21,13 @@ import (
_ "code.gitea.io/gitea/modules/markup/csv"
_ "code.gitea.io/gitea/modules/markup/markdown"
_ "code.gitea.io/gitea/modules/markup/orgmode"
"github.com/urfave/cli"
)
// these flags will be set by the build flags
var (
// Version holds the current Gitea version
Version = "development"
// Tags holds the build tags used
Tags = ""
// MakeVersion holds the current Make version if built with make
MakeVersion = ""
Version = "development" // program version for this build
Tags = "" // the Golang build tags
MakeVersion = "" // "make" program version if built with make
)
func init() {
@ -41,110 +36,12 @@ func init() {
setting.AppStartTime = time.Now().UTC()
}
// cmdHelp is our own help subcommand with more information
// test cases:
// ./gitea help
// ./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
// ./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
// GITEA_WORK_DIR=/tmp ./gitea help
// GITEA_WORK_DIR=/tmp ./gitea help --work-path /tmp/other
// GITEA_WORK_DIR=/tmp ./gitea help --config /tmp/app-other.ini
var cmdHelp = cli.Command{
Name: "help",
Aliases: []string{"h"},
Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]",
Action: func(c *cli.Context) (err error) {
args := c.Args()
if args.Present() {
err = cli.ShowCommandHelp(c, args.First())
} else {
err = cli.ShowAppHelp(c)
}
_, _ = fmt.Fprintf(c.App.Writer, `
DEFAULT CONFIGURATION:
AppPath: %s
WorkPath: %s
CustomPath: %s
ConfigFile: %s
`, setting.AppPath, setting.AppWorkPath, setting.CustomPath, setting.CustomConf)
return err
},
}
func main() {
app := cli.NewApp()
app := cmd.NewMainApp()
app.Name = "Gitea"
app.Usage = "A painless self-hosted Git service"
app.Description = `By default, Gitea will start serving using the web-server with no argument, which can alternatively be run by running the subcommand "web".`
app.Version = Version + formatBuiltWith()
app.EnableBashCompletion = true
// these sub-commands need to use config file
subCmdWithIni := []cli.Command{
cmd.CmdWeb,
cmd.CmdServ,
cmd.CmdHook,
cmd.CmdDump,
cmd.CmdAdmin,
cmd.CmdMigrate,
cmd.CmdKeys,
cmd.CmdConvert,
cmd.CmdDoctor,
cmd.CmdManager,
cmd.CmdEmbedded,
cmd.CmdMigrateStorage,
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,
}
// 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{
Name: "custom-path, C",
Usage: "Set custom path (defaults to '{WorkPath}/custom')",
},
cli.StringFlag{
Name: "config, c",
Value: setting.CustomConf,
Usage: "Set custom config file (defaults to '{WorkPath}/custom/conf/app.ini')",
},
cli.StringFlag{
Name: "work-path, w",
Usage: "Set Gitea's working path (defaults to the Gitea's binary directory)",
},
}
// 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...) // 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.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 {
@ -154,45 +51,6 @@ func main() {
log.GetManager().Close()
}
func prepareSubcommands(command *cli.Command, defaultFlags []cli.Flag) {
command.Flags = append(command.Flags, defaultFlags...)
command.Action = prepareWorkPathAndCustomConf(command.Action)
command.HideHelp = true
if command.Name != "help" {
command.Subcommands = append(command.Subcommands, cmdHelp)
}
for i := range command.Subcommands {
prepareSubcommands(&command.Subcommands[i], defaultFlags)
}
}
// 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(action any) func(ctx *cli.Context) error {
return func(ctx *cli.Context) error {
var args setting.ArgWorkPathAndCustomConf
curCtx := ctx
for curCtx != nil {
if curCtx.IsSet("work-path") && args.WorkPath == "" {
args.WorkPath = curCtx.String("work-path")
}
if curCtx.IsSet("custom-path") && args.CustomPath == "" {
args.CustomPath = curCtx.String("custom-path")
}
if curCtx.IsSet("config") && args.CustomConf == "" {
args.CustomConf = curCtx.String("config")
}
curCtx = curCtx.Parent()
}
setting.InitWorkPathAndCommonConfig(os.Getenv, args)
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.(func(*cli.Context) error)(ctx)
}
}
func formatBuiltWith() string {
version := runtime.Version()
if len(MakeVersion) > 0 {

View File

@ -31,10 +31,6 @@ func TestGetCommitStatuses(t *testing.T) {
assert.Equal(t, structs.CommitStatusPending, statuses[0].State)
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[0].APIURL(db.DefaultContext))
assert.Equal(t, "cov/awesomeness", statuses[1].Context)
assert.Equal(t, structs.CommitStatusWarning, statuses[1].State)
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[1].APIURL(db.DefaultContext))
assert.Equal(t, "cov/awesomeness", statuses[2].Context)
assert.Equal(t, structs.CommitStatusSuccess, statuses[2].State)
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[2].APIURL(db.DefaultContext))

View File

@ -513,6 +513,8 @@ var migrations = []Migration{
NewMigration("Add branch table", v1_21.AddBranchTable),
// v265 -> v266
NewMigration("Alter Actions Artifact table", v1_21.AlterActionArtifactTable),
// v266 -> v267
NewMigration("Reduce commit status", v1_21.ReduceCommitStatus),
}
// GetCurrentDBVersion returns the current db version

View File

@ -0,0 +1,26 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_21 //nolint
import (
"xorm.io/xorm"
)
func ReduceCommitStatus(x *xorm.Engine) error {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
if _, err := sess.Exec(`UPDATE commit_status SET state='pending' WHERE state='running'`); err != nil {
return err
}
if _, err := sess.Exec(`UPDATE commit_status SET state='failure' WHERE state='warning'`); err != nil {
return err
}
return sess.Commit()
}

View File

@ -660,13 +660,6 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
return cancel
}
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID)
if err != nil {
ctx.ServerError("GetTagNamesByRepoID", err)
return cancel
}
ctx.Data["Tags"] = tags
branchOpts := git_model.FindBranchOptions{
RepoID: ctx.Repo.Repository.ID,
IsDeletedBranch: util.OptionalBoolFalse,
@ -680,7 +673,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
return cancel
}
// non empty repo should have at least 1 branch, so this repository's branches haven't been synced yet
// non-empty repo should have at least 1 branch, so this repository's branches haven't been synced yet
if branchesTotal == 0 { // fallback to do a sync immediately
branchesTotal, err = repo_module.SyncRepoBranches(ctx, ctx.Repo.Repository.ID, 0)
if err != nil {
@ -689,24 +682,19 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
}
}
// FIXME: use paganation and async loading
branchOpts.ExcludeBranchNames = []string{ctx.Repo.Repository.DefaultBranch}
brs, err := git_model.FindBranchNames(ctx, branchOpts)
if err != nil {
ctx.ServerError("GetBranches", err)
return cancel
}
// always put default branch on the top
ctx.Data["Branches"] = append(branchOpts.ExcludeBranchNames, brs...)
ctx.Data["BranchesCount"] = branchesTotal
// If not branch selected, try default one.
// If default branch doesn't exist, fall back to some other branch.
// If no branch is set in the request URL, try to guess a default one.
if len(ctx.Repo.BranchName) == 0 {
if len(ctx.Repo.Repository.DefaultBranch) > 0 && gitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) {
ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
} else if len(brs) > 0 {
ctx.Repo.BranchName = brs[0]
} else {
ctx.Repo.BranchName, _ = gitRepo.GetDefaultBranch()
if ctx.Repo.BranchName == "" {
// If it still can't get a default branch, fall back to default branch from setting.
// Something might be wrong. Either site admin should fix the repo sync or Gitea should fix a potential bug.
ctx.Repo.BranchName = setting.Repository.DefaultBranch
}
}
ctx.Repo.RefName = ctx.Repo.BranchName
}

View File

@ -28,27 +28,15 @@ func AssetFS() *assetfs.LayeredFS {
return assetfs.Layered(CustomAssets(), BuiltinAssets())
}
// AssetsHandlerFunc implements the static handler for serving custom or original assets.
func AssetsHandlerFunc(prefix string) http.HandlerFunc {
// FileHandlerFunc implements the static handler for serving files in "public" assets
func FileHandlerFunc() http.HandlerFunc {
assetFS := AssetFS()
prefix = strings.TrimSuffix(prefix, "/") + "/"
return func(resp http.ResponseWriter, req *http.Request) {
subPath := req.URL.Path
if !strings.HasPrefix(subPath, prefix) {
return
}
subPath = strings.TrimPrefix(subPath, prefix)
if req.Method != "GET" && req.Method != "HEAD" {
resp.WriteHeader(http.StatusNotFound)
return
}
if handleRequest(resp, req, assetFS, subPath) {
return
}
resp.WriteHeader(http.StatusNotFound)
handleRequest(resp, req, assetFS, req.URL.Path)
}
}
@ -71,16 +59,17 @@ func setWellKnownContentType(w http.ResponseWriter, file string) {
}
}
func handleRequest(w http.ResponseWriter, req *http.Request, fs http.FileSystem, file string) bool {
func handleRequest(w http.ResponseWriter, req *http.Request, fs http.FileSystem, file string) {
// actually, fs (http.FileSystem) is designed to be a safe interface, relative paths won't bypass its parent directory, it's also fine to do a clean here
f, err := fs.Open(util.PathJoinRelX("assets", file))
f, err := fs.Open(util.PathJoinRelX(file))
if err != nil {
if os.IsNotExist(err) {
return false
w.WriteHeader(http.StatusNotFound)
return
}
w.WriteHeader(http.StatusInternalServerError)
log.Error("[Static] Open %q failed: %v", file, err)
return true
return
}
defer f.Close()
@ -88,17 +77,16 @@ func handleRequest(w http.ResponseWriter, req *http.Request, fs http.FileSystem,
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
log.Error("[Static] %q exists, but fails to open: %v", file, err)
return true
return
}
// Try to serve index file
// need to serve index file? (no at the moment)
if fi.IsDir() {
w.WriteHeader(http.StatusNotFound)
return true
return
}
serveContent(w, req, fi, fi.ModTime(), f)
return true
}
type GzipBytesProvider interface {

View File

@ -349,9 +349,4 @@ func loadServerFrom(rootCfg ConfigProvider) {
default:
LandingPageURL = LandingPage(landingPage)
}
HasRobotsTxt, err = util.IsFile(path.Join(CustomPath, "robots.txt"))
if err != nil {
log.Error("Unable to check if %s is a file. Error: %v", path.Join(CustomPath, "robots.txt"), err)
}
}

View File

@ -16,26 +16,17 @@ const (
CommitStatusError CommitStatusState = "error"
// CommitStatusFailure is for when the CommitStatus is Failure
CommitStatusFailure CommitStatusState = "failure"
// CommitStatusWarning is for when the CommitStatus is Warning
CommitStatusWarning CommitStatusState = "warning"
// CommitStatusRunning is for when the CommitStatus is Running
CommitStatusRunning CommitStatusState = "running"
)
// NoBetterThan returns true if this State is no better than the given State
func (css CommitStatusState) NoBetterThan(css2 CommitStatusState) bool {
switch css {
case CommitStatusError:
return true
case CommitStatusFailure:
return css2 != CommitStatusError
case CommitStatusWarning:
return css2 != CommitStatusError && css2 != CommitStatusFailure
case CommitStatusPending:
return css2 != CommitStatusError && css2 != CommitStatusFailure && css2 != CommitStatusWarning
default:
return css2 != CommitStatusError && css2 != CommitStatusFailure && css2 != CommitStatusWarning && css2 != CommitStatusPending
commitStatusPriorities := map[CommitStatusState]int{
CommitStatusError: 0,
CommitStatusFailure: 1,
CommitStatusPending: 2,
CommitStatusSuccess: 3,
}
return commitStatusPriorities[css] <= commitStatusPriorities[css2]
}
// IsPending represents if commit status state is pending
@ -57,8 +48,3 @@ func (css CommitStatusState) IsError() bool {
func (css CommitStatusState) IsFailure() bool {
return css == CommitStatusFailure
}
// IsWarning represents if commit status state is warning
func (css CommitStatusState) IsWarning() bool {
return css == CommitStatusWarning
}

View File

@ -0,0 +1,6 @@
# This site is running a Gitea instance.
# Gitea related security problems could be reported to Gitea community.
# Site related security problems should be reported to this site's admin.
Contact: https://github.com/go-gitea/gitea/blob/main/SECURITY.md
Policy: https://github.com/go-gitea/gitea/blob/main/SECURITY.md
Preferred-Languages: en

View File

@ -20,7 +20,7 @@ import (
func Routes() *web.Route {
base := web.NewRoute()
base.Use(common.ProtocolMiddlewares()...)
base.Methods("GET, HEAD", "/assets/*", public.AssetsHandlerFunc("/assets/"))
base.Methods("GET, HEAD", "/assets/*", public.FileHandlerFunc())
r := web.NewRoute()
r.Use(common.Sessioner(), Contexter())

View File

@ -34,9 +34,12 @@ func DummyOK(w http.ResponseWriter, req *http.Request) {
}
func RobotsTxt(w http.ResponseWriter, req *http.Request) {
filePath := util.FilePathJoinAbs(setting.CustomPath, "robots.txt")
robotsTxt := util.FilePathJoinAbs(setting.CustomPath, "public/robots.txt")
if ok, _ := util.IsExist(robotsTxt); !ok {
robotsTxt = util.FilePathJoinAbs(setting.CustomPath, "robots.txt") // the legacy "robots.txt"
}
httpcache.SetCacheControlInHeader(w.Header(), setting.StaticCacheTime)
http.ServeFile(w, req, filePath)
http.ServeFile(w, req, robotsTxt)
}
func StaticRedirect(target string) func(w http.ResponseWriter, req *http.Request) {

View File

@ -754,6 +754,12 @@ func CompareDiff(ctx *context.Context) {
}
ctx.Data["HeadBranches"] = headBranches
// For compare repo branches
PrepareBranchList(ctx)
if ctx.Written() {
return
}
headTags, err := repo_model.GetTagNamesByRepoID(ctx, ci.HeadRepo.ID)
if err != nil {
ctx.ServerError("GetTagNamesByRepoID", err)

View File

@ -785,18 +785,10 @@ func RetrieveRepoMetas(ctx *context.Context, repo *repo_model.Repository, isPull
return nil
}
brs, err := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{
RepoID: ctx.Repo.Repository.ID,
ListOptions: db.ListOptions{
ListAll: true,
},
IsDeletedBranch: util.OptionalBoolFalse,
})
if err != nil {
ctx.ServerError("GetBranches", err)
PrepareBranchList(ctx)
if ctx.Written() {
return nil
}
ctx.Data["Branches"] = brs
// Contains true if the user can create issue dependencies
ctx.Data["CanCreateIssueDependencies"] = ctx.Repo.CanCreateIssueDependencies(ctx.Doer, isPull)
@ -921,6 +913,13 @@ func NewIssue(ctx *context.Context) {
RetrieveRepoMetas(ctx, ctx.Repo.Repository, false)
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID)
if err != nil {
ctx.ServerError("GetTagNamesByRepoID", err)
return
}
ctx.Data["Tags"] = tags
_, templateErrs := issue_service.GetTemplatesFromDefaultBranch(ctx.Repo.Repository, ctx.Repo.GitRepo)
if errs := setTemplateIfExists(ctx, issueTemplateKey, IssueTemplateCandidates); len(errs) > 0 {
for k, v := range errs {
@ -1918,6 +1917,19 @@ func ViewIssue(ctx *context.Context) {
ctx.Data["ShouldShowCommentType"] = func(commentType issues_model.CommentType) bool {
return hiddenCommentTypes == nil || hiddenCommentTypes.Bit(int(commentType)) == 0
}
// For sidebar
PrepareBranchList(ctx)
if ctx.Written() {
return
}
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID)
if err != nil {
ctx.ServerError("GetTagNamesByRepoID", err)
return
}
ctx.Data["Tags"] = tags
ctx.HTML(http.StatusOK, tplIssueView)
}

View File

@ -729,6 +729,11 @@ func ViewPullCommits(ctx *context.Context) {
ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)
ctx.Data["IsIssuePoster"] = ctx.IsSigned && issue.IsPoster(ctx.Doer.ID)
// For PR commits page
PrepareBranchList(ctx)
if ctx.Written() {
return
}
getBranchData(ctx, issue)
ctx.HTML(http.StatusOK, tplPullCommits)
}
@ -893,6 +898,11 @@ func ViewPullFiles(ctx *context.Context) {
ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
// For files changed page
PrepareBranchList(ctx)
if ctx.Written() {
return
}
upload.AddUploadContext(ctx, "comment")
ctx.HTML(http.StatusOK, tplPullFiles)

View File

@ -352,6 +352,20 @@ func NewRelease(ctx *context.Context) {
ctx.Data["Assignees"] = MakeSelfOnTop(ctx, assigneeUsers)
upload.AddUploadContext(ctx, "release")
// For New Release page
PrepareBranchList(ctx)
if ctx.Written() {
return
}
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID)
if err != nil {
ctx.ServerError("GetTagNamesByRepoID", err)
return
}
ctx.Data["Tags"] = tags
ctx.HTML(http.StatusOK, tplReleaseNew)
}
@ -361,6 +375,13 @@ func NewReleasePost(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.release.new_release")
ctx.Data["PageIsReleaseList"] = true
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID)
if err != nil {
ctx.ServerError("GetTagNamesByRepoID", err)
return
}
ctx.Data["Tags"] = tags
if ctx.HasError() {
ctx.HTML(http.StatusOK, tplReleaseNew)
return

View File

@ -622,3 +622,64 @@ func SearchRepo(ctx *context.Context) {
Data: results,
})
}
type branchTagSearchResponse struct {
Results []string `json:"results"`
}
// GetBranchesList get branches for current repo'
func GetBranchesList(ctx *context.Context) {
branchOpts := git_model.FindBranchOptions{
RepoID: ctx.Repo.Repository.ID,
IsDeletedBranch: util.OptionalBoolFalse,
ListOptions: db.ListOptions{
ListAll: true,
},
}
branches, err := git_model.FindBranchNames(ctx, branchOpts)
if err != nil {
ctx.JSON(http.StatusInternalServerError, err)
return
}
resp := &branchTagSearchResponse{}
// always put default branch on the top if it exists
if util.SliceContains(branches, ctx.Repo.Repository.DefaultBranch) {
branches = util.SliceRemoveAll(branches, ctx.Repo.Repository.DefaultBranch)
branches = append([]string{ctx.Repo.Repository.DefaultBranch}, branches...)
}
resp.Results = branches
ctx.JSON(http.StatusOK, resp)
}
// GetTagList get tag list for current repo
func GetTagList(ctx *context.Context) {
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID)
if err != nil {
ctx.JSON(http.StatusInternalServerError, err)
return
}
resp := &branchTagSearchResponse{}
resp.Results = tags
ctx.JSON(http.StatusOK, resp)
}
func PrepareBranchList(ctx *context.Context) {
branchOpts := git_model.FindBranchOptions{
RepoID: ctx.Repo.Repository.ID,
IsDeletedBranch: util.OptionalBoolFalse,
ListOptions: db.ListOptions{
ListAll: true,
},
}
brs, err := git_model.FindBranchNames(ctx, branchOpts)
if err != nil {
ctx.ServerError("GetBranches", err)
return
}
// always put default branch on the top if it exists
if util.SliceContains(brs, ctx.Repo.Repository.DefaultBranch) {
brs = util.SliceRemoveAll(brs, ctx.Repo.Repository.DefaultBranch)
brs = append([]string{ctx.Repo.Repository.DefaultBranch}, brs...)
}
ctx.Data["Branches"] = brs
}

View File

@ -21,6 +21,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/web/repo"
"code.gitea.io/gitea/services/forms"
pull_service "code.gitea.io/gitea/services/pull"
"code.gitea.io/gitea/services/repository"
@ -44,6 +45,11 @@ func ProtectedBranchRules(ctx *context.Context) {
}
ctx.Data["ProtectedBranches"] = rules
repo.PrepareBranchList(ctx)
if ctx.Written() {
return
}
ctx.HTML(http.StatusOK, tplBranches)
}
@ -52,6 +58,11 @@ func SetDefaultBranchPost(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.settings.branches.update_default_branch")
ctx.Data["PageIsSettingsBranches"] = true
repo.PrepareBranchList(ctx)
if ctx.Written() {
return
}
repo := ctx.Repo.Repository
switch ctx.FormString("action") {

View File

@ -108,7 +108,7 @@ func Routes() *web.Route {
routes := web.NewRoute()
routes.Head("/", misc.DummyOK) // for health check - doesn't need to be passed through gzip handler
routes.Methods("GET, HEAD", "/assets/*", CorsHandler(), public.AssetsHandlerFunc("/assets/"))
routes.Methods("GET, HEAD", "/assets/*", CorsHandler(), public.FileHandlerFunc())
routes.Methods("GET, HEAD", "/avatars/*", storageHandler(setting.Avatar.Storage, "avatars", storage.Avatars))
routes.Methods("GET, HEAD", "/repo-avatars/*", storageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars))
routes.Methods("GET, HEAD", "/apple-touch-icon.png", misc.StaticRedirect("/assets/img/apple-touch-icon.png"))
@ -132,15 +132,12 @@ func Routes() *web.Route {
routes.Methods("GET,HEAD", "/captcha/*", append(mid, captcha.Captchaer(context.GetImageCaptcha()))...)
}
if setting.HasRobotsTxt {
routes.Get("/robots.txt", append(mid, misc.RobotsTxt)...)
}
if setting.Metrics.Enabled {
prometheus.MustRegister(metrics.NewCollector())
routes.Get("/metrics", append(mid, Metrics)...)
}
routes.Get("/robots.txt", append(mid, misc.RobotsTxt)...)
routes.Get("/ssh_info", misc.SSHInfo)
routes.Get("/api/healthz", healthcheck.Check)
@ -336,8 +333,7 @@ func registerRoutes(m *web.Route) {
// FIXME: not all routes need go through same middleware.
// Especially some AJAX requests, we can reduce middleware number to improve performance.
// Routers.
// for health check
m.Get("/", Home)
m.Get("/sitemap.xml", sitemapEnabled, ignExploreSignIn, HomeSitemap)
m.Group("/.well-known", func() {
@ -349,7 +345,8 @@ func registerRoutes(m *web.Route) {
m.Get("/change-password", func(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
})
})
m.Any("/*", CorsHandler(), public.FileHandlerFunc())
}, CorsHandler())
m.Group("/explore", func() {
m.Get("", func(ctx *context.Context) {
@ -1094,6 +1091,7 @@ func registerRoutes(m *web.Route) {
}, context.RepoRef(), canEnableEditor, context.RepoMustNotBeArchived())
m.Group("/branches", func() {
m.Get("/list", repo.GetBranchesList)
m.Group("/_new", func() {
m.Post("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.CreateBranch)
m.Post("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.CreateBranch)
@ -1108,6 +1106,7 @@ func registerRoutes(m *web.Route) {
m.Group("/{username}/{reponame}", func() {
m.Group("/tags", func() {
m.Get("", repo.TagsList)
m.Get("/list", repo.GetTagList)
m.Get(".rss", feedEnabled, repo.TagsListFeedRSS)
m.Get(".atom", feedEnabled, repo.TagsListFeedAtom)
}, ctxDataSet("EnableFeed", setting.Other.EnableFeed),

View File

@ -137,14 +137,10 @@ func toCommitStatus(status actions_model.Status) api.CommitStatusState {
switch status {
case actions_model.StatusSuccess, actions_model.StatusSkipped:
return api.CommitStatusSuccess
case actions_model.StatusFailure:
case actions_model.StatusFailure, actions_model.StatusCancelled:
return api.CommitStatusFailure
case actions_model.StatusCancelled:
return api.CommitStatusWarning
case actions_model.StatusWaiting, actions_model.StatusBlocked:
case actions_model.StatusWaiting, actions_model.StatusBlocked, actions_model.StatusRunning:
return api.CommitStatusPending
case actions_model.StatusRunning:
return api.CommitStatusRunning
default:
return api.CommitStatusError
}

View File

@ -52,6 +52,14 @@ func ToCombinedStatus(ctx context.Context, statuses []*git_model.CommitStatus, r
retStatus.State = status.State
}
}
// According to https://docs.github.com/en/rest/commits/statuses?apiVersion=2022-11-28#get-the-combined-status-for-a-specific-reference
// > Additionally, a combined state is returned. The state is one of:
// > failure if any of the contexts report as error or failure
// > pending if there are no statuses or a context is pending
// > success if the latest status for all contexts is success
if retStatus.State.IsError() {
retStatus.State = api.CommitStatusFailure
}
return retStatus
}

View File

@ -378,7 +378,7 @@ func buildPrimary(pv *packages_model.PackageVersion, pfs []*packages_model.Packa
Architecture: pd.FileMetadata.Architecture,
Version: Version{
Epoch: pd.FileMetadata.Epoch,
Version: pd.Version.Version,
Version: pd.FileMetadata.Version,
Release: pd.FileMetadata.Release,
},
Checksum: Checksum{
@ -466,7 +466,7 @@ func buildFilelists(pv *packages_model.PackageVersion, pfs []*packages_model.Pac
Architecture: pd.FileMetadata.Architecture,
Version: Version{
Epoch: pd.FileMetadata.Epoch,
Version: pd.Version.Version,
Version: pd.FileMetadata.Version,
Release: pd.FileMetadata.Release,
},
Files: pd.FileMetadata.Files,
@ -513,7 +513,7 @@ func buildOther(pv *packages_model.PackageVersion, pfs []*packages_model.Package
Architecture: pd.FileMetadata.Architecture,
Version: Version{
Epoch: pd.FileMetadata.Epoch,
Version: pd.Version.Version,
Version: pd.FileMetadata.Version,
Release: pd.FileMetadata.Release,
},
Changelogs: pd.FileMetadata.Changelogs,

View File

@ -44,8 +44,6 @@
'tagName': {{.root.TagName}},
'branchName': {{.root.BranchName}},
'noTag': {{.noTag}},
'branches': {{.root.Branches}},
'tags': {{.root.Tags}},
'defaultBranch': {{$defaultBranch}},
'enableFeed': {{.root.EnableFeed}},
'rssURLPrefix': '{{$.root.RepoLink}}/rss/branch/',

View File

@ -1,4 +1,5 @@
{{if or (eq .State "pending") (eq .State "running")}}
<!-- make sure this matches the color logic in web_src/js/components/DashboardRepoList.vue -->
{{if eq .State "pending"}}
{{svg "octicon-dot-fill" 18 "commit-status icon text yellow"}}
{{end}}
{{if eq .State "success"}}
@ -10,6 +11,3 @@
{{if eq .State "failure"}}
{{svg "octicon-x" 18 "commit-status icon text red"}}
{{end}}
{{if eq .State "warning"}}
{{svg "gitea-exclamation" 18 "commit-status icon text yellow"}}
{{end}}

View File

@ -196,15 +196,15 @@ gpgkey=%sapi/packages/%s/rpm/repository.key`, user.Name, user.Name, setting.AppN
switch d.Type {
case "primary":
assert.EqualValues(t, 718, d.Size)
assert.EqualValues(t, 1731, d.OpenSize)
assert.EqualValues(t, 1729, d.OpenSize)
assert.Equal(t, "repodata/primary.xml.gz", d.Location.Href)
case "filelists":
assert.EqualValues(t, 258, d.Size)
assert.EqualValues(t, 328, d.OpenSize)
assert.EqualValues(t, 257, d.Size)
assert.EqualValues(t, 326, d.OpenSize)
assert.Equal(t, "repodata/filelists.xml.gz", d.Location.Href)
case "other":
assert.EqualValues(t, 308, d.Size)
assert.EqualValues(t, 396, d.OpenSize)
assert.EqualValues(t, 306, d.Size)
assert.EqualValues(t, 394, d.OpenSize)
assert.Equal(t, "repodata/other.xml.gz", d.Location.Href)
}
}

View File

@ -5,17 +5,15 @@ package integration
import (
"bytes"
"flag"
"io"
"net/url"
"os"
"testing"
"code.gitea.io/gitea/cmd"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"github.com/urfave/cli"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli/v2"
)
func Test_CmdKeys(t *testing.T) {
@ -38,26 +36,18 @@ func Test_CmdKeys(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
realStdout := os.Stdout // Backup Stdout
r, w, _ := os.Pipe()
os.Stdout = w
set := flag.NewFlagSet("keys", 0)
_ = set.Parse(tt.args)
context := cli.NewContext(&cli.App{Writer: os.Stdout}, set, nil)
err := cmd.CmdKeys.Run(context)
if (err != nil) != tt.wantErr {
t.Errorf("CmdKeys.Run() error = %v, wantErr %v", err, tt.wantErr)
out := new(bytes.Buffer)
app := cli.NewApp()
app.Writer = out
app.Commands = []*cli.Command{cmd.CmdKeys}
cmd.CmdKeys.HideHelp = true
err := app.Run(append([]string{"prog"}, tt.args...))
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
w.Close()
var buf bytes.Buffer
io.Copy(&buf, r)
commandOutput := buf.String()
if tt.expectedOutput != commandOutput {
t.Errorf("expectedOutput: %#v, commandOutput: %#v", tt.expectedOutput, commandOutput)
}
// Restore stdout
os.Stdout = realStdout
assert.Equal(t, tt.expectedOutput, out.String())
})
}
})

View File

@ -38,6 +38,7 @@ func TestLinksNoLogin(t *testing.T) {
"/user2/repo1/projects/1",
"/assets/img/404.png",
"/assets/img/500.png",
"/.well-known/security.txt",
}
for _, link := range links {

View File

@ -52,7 +52,6 @@ func TestPullCreate_CommitStatus(t *testing.T) {
api.CommitStatusPending,
api.CommitStatusError,
api.CommitStatusFailure,
api.CommitStatusWarning,
api.CommitStatusSuccess,
}
@ -61,7 +60,6 @@ func TestPullCreate_CommitStatus(t *testing.T) {
api.CommitStatusSuccess: "octicon-check",
api.CommitStatusError: "gitea-exclamation",
api.CommitStatusFailure: "octicon-x",
api.CommitStatusWarning: "gitea-exclamation",
}
testCtx := NewAPITestContext(t, "user1", "repo1", auth_model.AccessTokenScopeWriteRepository)

View File

@ -125,14 +125,6 @@ func TestRepoCommitsWithStatusFailure(t *testing.T) {
doTestRepoCommitWithStatus(t, "failure", "octicon-x", "red")
}
func TestRepoCommitsWithStatusWarning(t *testing.T) {
doTestRepoCommitWithStatus(t, "warning", "gitea-exclamation", "yellow")
}
func TestRepoCommitsWithStatusRunning(t *testing.T) {
doTestRepoCommitWithStatus(t, "running", "octicon-dot-fill", "yellow")
}
func TestRepoCommitsStatusParallel(t *testing.T) {
defer tests.PrepareTestEnv(t)()

View File

@ -3359,3 +3359,7 @@ tbody.commit-list {
font-size: 18px;
margin-left: 4px;
}
#cherry-pick-modal .scrolling.menu {
max-height: 200px;
}

View File

@ -152,13 +152,12 @@ import {SvgIcon} from '../svg.js';
const {appSubUrl, assetUrlPrefix, pageData} = window.config;
// make sure this matches templates/repo/commit_status.tmpl
const commitStatus = {
pending: {name: 'octicon-dot-fill', color: 'yellow'},
running: {name: 'octicon-dot-fill', color: 'yellow'},
success: {name: 'octicon-check', color: 'green'},
error: {name: 'gitea-exclamation', color: 'red'},
failure: {name: 'octicon-x', color: 'red'},
warning: {name: 'gitea-exclamation', color: 'yellow'},
};
const sfc = {

View File

@ -11,7 +11,7 @@
</span>
<svg-icon name="octicon-triangle-down" :size="14" class-name="dropdown icon"/>
</button>
<div class="menu transition" :class="{visible: menuVisible}" v-if="menuVisible" v-cloak>
<div class="menu transition" :class="{visible: menuVisible}" v-show="menuVisible" v-cloak>
<div class="ui icon search input">
<i class="icon"><svg-icon name="octicon-filter" :size="16"/></i>
<input name="search" ref="searchField" autocomplete="off" v-model="searchTerm" @keydown="keydown($event)" :placeholder="searchFieldPlaceholder">
@ -20,13 +20,13 @@
<div class="header branch-tag-choice">
<div class="ui grid">
<div class="two column row">
<a class="reference column" href="#" @click="createTag = false; mode = 'branches'; focusSearchField()">
<a class="reference column" href="#" @click="handleTabSwitch('branches')">
<span class="text" :class="{black: mode === 'branches'}">
<svg-icon name="octicon-git-branch" :size="16" class-name="gt-mr-2"/>{{ textBranches }}
</span>
</a>
<template v-if="!noTag">
<a class="reference column" href="#" @click="createTag = true; mode = 'tags'; focusSearchField()">
<a class="reference column" href="#" @click="handleTabSwitch('tags')">
<span class="text" :class="{black: mode === 'tags'}">
<svg-icon name="octicon-tag" :size="16" class-name="gt-mr-2"/>{{ textTags }}
</span>
@ -37,20 +37,23 @@
</div>
</template>
<div class="scrolling menu" ref="scrollContainer">
<svg-icon name="octicon-rss" symbol-id="svg-symbol-octicon-rss"/>
<div class="loading-indicator is-loading" v-if="isLoading"/>
<div v-for="(item, index) in filteredItems" :key="item.name" class="item" :class="{selected: item.selected, active: active === index}" @click="selectItem(item)" :ref="'listItem' + index">
{{ item.name }}
<a v-if="enableFeed && mode === 'branches'" role="button" class="rss-icon ui compact right" :href="rssURLPrefix + item.url" target="_blank" @click.stop>
<svg-icon name="octicon-rss" :size="14"/>
<a v-show="enableFeed && mode === 'branches'" role="button" class="rss-icon ui compact right" :href="rssURLPrefix + item.url" target="_blank" @click.stop>
<!-- creating a lot of Vue component is pretty slow, so we use a static SVG here -->
<svg width="14" height="14" class="svg octicon-rss"><use href="#svg-symbol-octicon-rss"/></svg>
</a>
</div>
<div class="item" v-if="showCreateNewBranch" :class="{active: active === filteredItems.length}" :ref="'listItem' + filteredItems.length">
<a href="#" @click="createNewBranch()">
<div v-show="createTag">
<div v-show="shouldCreateTag">
<i class="reference tags icon"/>
<!-- eslint-disable-next-line vue/no-v-html -->
<span v-html="textCreateTag.replace('%s', searchTerm)"/>
</div>
<div v-show="!createTag">
<div v-show="!shouldCreateTag">
<svg-icon name="octicon-git-branch"/>
<!-- eslint-disable-next-line vue/no-v-html -->
<span v-html="textCreateBranch.replace('%s', searchTerm)"/>
@ -64,12 +67,12 @@
<form ref="newBranchForm" :action="formActionUrl" method="post">
<input type="hidden" name="_csrf" :value="csrfToken">
<input type="hidden" name="new_branch_name" v-model="searchTerm">
<input type="hidden" name="create_tag" v-model="createTag">
<input type="hidden" name="create_tag" v-model="shouldCreateTag">
<input type="hidden" name="current_path" v-model="treePath" v-if="treePath">
</form>
</div>
</div>
<div class="message" v-if="showNoResults">
<div class="message" v-if="showNoResults && !isLoading">
{{ noResults }}
</div>
</div>
@ -81,6 +84,7 @@ import {createApp, nextTick} from 'vue';
import $ from 'jquery';
import {SvgIcon} from '../svg.js';
import {pathEscapeSegments} from '../utils/url.js';
import {showErrorToast} from '../modules/toast.js';
const sfc = {
components: {SvgIcon},
@ -110,12 +114,16 @@ const sfc = {
formActionUrl() {
return `${this.repoLink}/branches/_new/${this.branchNameSubURL}`;
},
shouldCreateTag() {
return this.mode === 'tags';
}
},
watch: {
menuVisible(visible) {
if (visible) {
this.focusSearchField();
this.fetchBranchesOrTags();
}
}
},
@ -139,7 +147,6 @@ const sfc = {
}
});
},
methods: {
selectItem(item) {
const prev = this.getSelected();
@ -246,7 +253,44 @@ const sfc = {
event.preventDefault();
this.menuVisible = false;
}
}
},
handleTabSwitch(mode) {
if (this.isLoading) return;
this.mode = mode;
this.focusSearchField();
this.fetchBranchesOrTags();
},
async fetchBranchesOrTags() {
if (!['branches', 'tags'].includes(this.mode) || this.isLoading) return;
// only fetch when branch/tag list has not been initialized
if (this.hasListInitialized[this.mode] ||
(this.mode === 'branches' && !this.showBranchesInDropdown) ||
(this.mode === 'tags' && this.noTag)
) {
return;
}
this.isLoading = true;
try {
// the "data.defaultBranch" is ambiguous, it could be "branch name" or "tag name"
const reqUrl = `${this.repoLink}/${this.mode}/list`;
const resp = await fetch(reqUrl);
const {results} = await resp.json();
for (const result of results) {
let selected = false;
if (this.mode === 'branches') {
selected = result === this.defaultBranch;
} else {
selected = result === (this.release ? this.release.tagName : this.defaultBranch);
}
this.items.push({name: result, url: pathEscapeSegments(result), branch: this.mode === 'branches', tag: this.mode === 'tags', selected});
}
this.hasListInitialized[this.mode] = true;
} catch (e) {
showErrorToast(`Network error when fetching ${this.mode}, error: ${e}`);
} finally {
this.isLoading = false;
}
},
}
};
@ -258,7 +302,6 @@ export function initRepoBranchTagSelector(selector) {
searchTerm: '',
refNameText: '',
menuVisible: false,
createTag: false,
release: null,
isViewTag: false,
@ -266,27 +309,15 @@ export function initRepoBranchTagSelector(selector) {
isViewTree: false,
active: 0,
isLoading: false,
// This means whether branch list/tag list has initialized
hasListInitialized: {
'branches': false,
'tags': false,
},
...window.config.pageData.branchDropdownDataList[elIndex],
};
// the "data.defaultBranch" is ambiguous, it could be "branch name" or "tag name"
if (data.showBranchesInDropdown && data.branches) {
for (const branch of data.branches) {
data.items.push({name: branch, url: pathEscapeSegments(branch), branch: true, tag: false, selected: branch === data.defaultBranch});
}
}
if (!data.noTag && data.tags) {
for (const tag of data.tags) {
if (data.release) {
data.items.push({name: tag, url: pathEscapeSegments(tag), branch: false, tag: true, selected: tag === data.release.tagName});
} else {
data.items.push({name: tag, url: pathEscapeSegments(tag), branch: false, tag: true, selected: tag === data.defaultBranch});
}
}
}
const comp = {...sfc, data() { return data }};
createApp(comp).mount(elRoot);
}
@ -302,4 +333,8 @@ export default sfc; // activate IDE's Vue plugin
.menu .item:hover .rss-icon {
display: inline-block;
}
.scrolling.menu .loading-indicator {
height: 4em;
}
</style>

View File

@ -1,5 +1,6 @@
import {htmlEscape} from 'escape-goat';
import {svg} from '../svg.js';
import Toastify from 'toastify-js';
const levels = {
info: {
@ -23,7 +24,6 @@ const levels = {
async function showToast(message, level, {gravity, position, duration, ...other} = {}) {
if (!message) return;
const {default: Toastify} = await import(/* webpackChunkName: 'toastify' */'toastify-js');
const {icon, background, duration: levelDuration} = levels[level ?? 'info'];
const toast = Toastify({

View File

@ -185,9 +185,10 @@ export const SvgIcon = {
name: {type: String, required: true},
size: {type: Number, default: 16},
className: {type: String, default: ''},
symbolId: {type: String}
},
render() {
const {svgOuter, svgInnerHtml} = svgParseOuterInner(this.name);
let {svgOuter, svgInnerHtml} = svgParseOuterInner(this.name);
// https://vuejs.org/guide/extras/render-function.html#creating-vnodes
// the `^` is used for attr, set SVG attributes like 'width', `aria-hidden`, `viewBox`, etc
const attrs = {};
@ -207,7 +208,10 @@ export const SvgIcon = {
if (this.className) {
classes.push(...this.className.split(/\s+/).filter(Boolean));
}
if (this.symbolId) {
classes.push('gt-hidden', 'svg-symbol-container');
svgInnerHtml = `<symbol id="${this.symbolId}" viewBox="${attrs['^viewBox']}">${svgInnerHtml}</symbol>`;
}
// create VNode
return h('svg', {
...attrs,