1
0
Fork 0
mirror of https://codeberg.org/forgejo/forgejo.git synced 2025-10-10 19:32:02 +00:00

Merge remote-tracking branch 'upstream/forgejo' into port-32327

This commit is contained in:
Maxim Slipenko 2025-06-07 18:27:10 +03:00
commit 009a1bcf86
505 changed files with 13602 additions and 5442 deletions

View file

@ -87,12 +87,24 @@ forgejo.org/modules/eventsource
Event.String
forgejo.org/modules/forgefed
NewForgeFollowFromAp
NewForgeFollow
ForgeFollow.MarshalJSON
ForgeFollow.UnmarshalJSON
ForgeFollow.Validate
NewForgeUndoLike
ForgeUndoLike.UnmarshalJSON
ForgeUndoLike.Validate
NewForgeUserActivityFromAp
NewForgeUserActivity
ForgeUserActivity.Validate
NewPersonIDFromModel
GetItemByType
JSONUnmarshalerFn
NotEmpty
NewForgeUserActivityNoteFromAp
newNote
ForgeUserActivityNote.Validate
ToRepository
OnRepository
@ -204,6 +216,7 @@ forgejo.org/modules/util/filebuffer
forgejo.org/modules/validation
IsErrNotValid
ValidateIDExists
forgejo.org/modules/web
RouteMock

View file

@ -28,7 +28,7 @@ jobs:
runs-on: docker
container:
image: data.forgejo.org/renovate/renovate:40.11.19
image: data.forgejo.org/renovate/renovate:40.40.0
steps:
- name: Load renovate repo cache

View file

@ -37,19 +37,18 @@ endif
XGO_VERSION := go-1.21.x
AIR_PACKAGE ?= github.com/air-verse/air@v1 # renovate: datasource=go
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.2.1 # renovate: datasource=go
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.3.0 # renovate: datasource=go
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.8.0 # renovate: datasource=go
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6 # renovate: datasource=go
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 # renovate: datasource=go
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.6.0 # renovate: datasource=go
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0 # renovate: datasource=go
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasource=go
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go
DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.32.0 # renovate: datasource=go
GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.5.1 # renovate: datasource=go
DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.34.0 # renovate: datasource=go
GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.5.2 # renovate: datasource=go
GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.18.1 # renovate: datasource=go
RENOVATE_NPM_PACKAGE ?= renovate@40.11.19 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
RENOVATE_NPM_PACKAGE ?= renovate@40.40.0 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
# https://github.com/disposable-email-domains/disposable-email-domains/commits/main/
DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ...
@ -94,7 +93,7 @@ else
# drop the "g" prefix prepended by git describe to the commit hash
FORGEJO_VERSION ?= $(shell git describe --exclude '*-test' --tags --always 2>/dev/null | sed 's/^v//' | sed 's/\-g/-/')
ifneq ($(FORGEJO_VERSION),)
ifneq ($(GITEA_COMPATIBILITY),)
ifeq ($(findstring $(GITEA_COMPATIBILITY),$(FORGEJO_VERSION)),)
FORGEJO_VERSION := $(FORGEJO_VERSION)+$(GITEA_COMPATIBILITY)
endif
endif
@ -557,7 +556,7 @@ test-check:
.PHONY: test\#%
test\#%:
@echo "Running go test with -tags '$(TEST_TAGS)'..."
@echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..."
@$(GOTEST) $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -run $(subst .,/,$*) $(GO_TEST_PACKAGES)
.PHONY: coverage
@ -924,7 +923,6 @@ deps-tools:
$(GO) install $(GOFUMPT_PACKAGE)
$(GO) install $(GOLANGCI_LINT_PACKAGE)
$(GO) install $(GXZ_PACKAGE)
$(GO) install $(MISSPELL_PACKAGE)
$(GO) install $(SWAGGER_PACKAGE)
$(GO) install $(XGO_PACKAGE)
$(GO) install $(GO_LICENSES_PACKAGE)

File diff suppressed because one or more lines are too long

View file

@ -52,7 +52,7 @@ func initBlueMondayPolicy() {
policy.AllowAttrs("id").Matching(positionalPlaceholderRe).OnElements("code")
// Allowed elements with no attributes. Must be a recognized tagname.
policy.AllowElements("strong", "br", "b", "strike", "code", "i")
policy.AllowElements("strong", "br", "b", "strike", "code", "i", "kbd")
// TODO: Remove <c> in `actions.workflow.dispatch.trigger_found`.
policy.AllowNoAttrs().OnElements("c")

View file

@ -4,25 +4,28 @@
package cmd
import (
"context"
"fmt"
"forgejo.org/modules/private"
"forgejo.org/modules/setting"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var (
// CmdActions represents the available actions sub-commands.
CmdActions = &cli.Command{
// CmdActions represents the available actions sub-commands.
func cmdActions() *cli.Command {
return &cli.Command{
Name: "actions",
Usage: "Manage Forgejo Actions",
Subcommands: []*cli.Command{
subcmdActionsGenRunnerToken,
Commands: []*cli.Command{
subcmdActionsGenRunnerToken(),
},
}
}
subcmdActionsGenRunnerToken = &cli.Command{
func subcmdActionsGenRunnerToken() *cli.Command {
return &cli.Command{
Name: "generate-runner-token",
Usage: "Generate a new token for a runner to use to register with the server",
Action: runGenerateActionsRunnerToken,
@ -36,10 +39,10 @@ var (
},
},
}
)
}
func runGenerateActionsRunnerToken(c *cli.Context) error {
ctx, cancel := installSignals()
func runGenerateActionsRunnerToken(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
setting.MustInstalled()

View file

@ -15,56 +15,64 @@ import (
"forgejo.org/modules/log"
repo_module "forgejo.org/modules/repository"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var (
// CmdAdmin represents the available admin sub-command.
CmdAdmin = &cli.Command{
// CmdAdmin represents the available admin sub-command.
func cmdAdmin() *cli.Command {
return &cli.Command{
Name: "admin",
Usage: "Perform common administrative operations",
Subcommands: []*cli.Command{
subcmdUser,
subcmdRepoSyncReleases,
subcmdRegenerate,
subcmdAuth,
subcmdSendMail,
Commands: []*cli.Command{
subcmdUser(),
subcmdRepoSyncReleases(),
subcmdRegenerate(),
subcmdAuth(),
subcmdSendMail(),
},
}
}
subcmdRepoSyncReleases = &cli.Command{
func subcmdRepoSyncReleases() *cli.Command {
return &cli.Command{
Name: "repo-sync-releases",
Usage: "Synchronize repository releases with tags",
Action: runRepoSyncReleases,
}
}
subcmdRegenerate = &cli.Command{
func subcmdRegenerate() *cli.Command {
return &cli.Command{
Name: "regenerate",
Usage: "Regenerate specific files",
Subcommands: []*cli.Command{
Commands: []*cli.Command{
microcmdRegenHooks,
microcmdRegenKeys,
},
}
}
subcmdAuth = &cli.Command{
func subcmdAuth() *cli.Command {
return &cli.Command{
Name: "auth",
Usage: "Modify external auth providers",
Subcommands: []*cli.Command{
microcmdAuthAddOauth,
microcmdAuthUpdateOauth,
microcmdAuthAddLdapBindDn,
microcmdAuthUpdateLdapBindDn,
microcmdAuthAddLdapSimpleAuth,
microcmdAuthUpdateLdapSimpleAuth,
microcmdAuthAddSMTP,
microcmdAuthUpdateSMTP,
microcmdAuthList,
microcmdAuthDelete,
Commands: []*cli.Command{
microcmdAuthAddOauth(),
microcmdAuthUpdateOauth(),
microcmdAuthAddLdapBindDn(),
microcmdAuthUpdateLdapBindDn(),
microcmdAuthAddLdapSimpleAuth(),
microcmdAuthUpdateLdapSimpleAuth(),
microcmdAuthAddSMTP(),
microcmdAuthUpdateSMTP(),
microcmdAuthList(),
microcmdAuthDelete(),
},
}
}
subcmdSendMail = &cli.Command{
func subcmdSendMail() *cli.Command {
return &cli.Command{
Name: "sendmail",
Usage: "Send a message to all users",
Action: runSendMail,
@ -86,15 +94,17 @@ var (
},
},
}
}
idFlag = &cli.Int64Flag{
func idFlag() *cli.Int64Flag {
return &cli.Int64Flag{
Name: "id",
Usage: "ID of authentication source",
}
)
}
func runRepoSyncReleases(_ *cli.Context) error {
ctx, cancel := installSignals()
func runRepoSyncReleases(ctx context.Context, _ *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
if err := initDB(ctx); err != nil {

View file

@ -4,6 +4,7 @@
package cmd
import (
"context"
"errors"
"fmt"
"os"
@ -13,17 +14,20 @@ import (
"forgejo.org/models/db"
auth_service "forgejo.org/services/auth"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var (
microcmdAuthDelete = &cli.Command{
func microcmdAuthDelete() *cli.Command {
return &cli.Command{
Name: "delete",
Usage: "Delete specific auth source",
Flags: []cli.Flag{idFlag},
Flags: []cli.Flag{idFlag()},
Action: runDeleteAuth,
}
microcmdAuthList = &cli.Command{
}
func microcmdAuthList() *cli.Command {
return &cli.Command{
Name: "list",
Usage: "List auth sources",
Action: runListAuth,
@ -54,10 +58,10 @@ var (
},
},
}
)
}
func runListAuth(c *cli.Context) error {
ctx, cancel := installSignals()
func runListAuth(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
if err := initDB(ctx); err != nil {
@ -81,7 +85,7 @@ func runListAuth(c *cli.Context) error {
// loop through each source and print
w := tabwriter.NewWriter(os.Stdout, c.Int("min-width"), c.Int("tab-width"), c.Int("padding"), padChar, flags)
fmt.Fprintf(w, "ID\tName\tType\tEnabled\n")
fmt.Fprint(w, "ID\tName\tType\tEnabled\n")
for _, source := range authSources {
fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", source.ID, source.Name, source.Type.String(), source.IsActive)
}
@ -90,12 +94,12 @@ func runListAuth(c *cli.Context) error {
return nil
}
func runDeleteAuth(c *cli.Context) error {
func runDeleteAuth(ctx context.Context, c *cli.Command) error {
if !c.IsSet("id") {
return errors.New("--id flag is missing")
}
ctx, cancel := installSignals()
ctx, cancel := installSignals(ctx)
defer cancel()
if err := initDB(ctx); err != nil {

View file

@ -11,7 +11,7 @@ import (
"forgejo.org/models/auth"
"forgejo.org/services/auth/source/ldap"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
type (
@ -23,8 +23,8 @@ type (
}
)
var (
commonLdapCLIFlags = []cli.Flag{
func commonLdapCLIFlags() []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "name",
Usage: "Authentication name.",
@ -102,8 +102,10 @@ var (
Usage: "The attribute of the users LDAP record containing the users avatar.",
},
}
}
ldapBindDnCLIFlags = append(commonLdapCLIFlags,
func ldapBindDnCLIFlags() []cli.Flag {
return append(commonLdapCLIFlags(),
&cli.StringFlag{
Name: "bind-dn",
Usage: "The DN to bind to the LDAP server with when searching for the user.",
@ -128,49 +130,59 @@ var (
Name: "page-size",
Usage: "Search page size.",
})
}
ldapSimpleAuthCLIFlags = append(commonLdapCLIFlags,
func ldapSimpleAuthCLIFlags() []cli.Flag {
return append(commonLdapCLIFlags(),
&cli.StringFlag{
Name: "user-dn",
Usage: "The user's DN.",
})
}
microcmdAuthAddLdapBindDn = &cli.Command{
func microcmdAuthAddLdapBindDn() *cli.Command {
return &cli.Command{
Name: "add-ldap",
Usage: "Add new LDAP (via Bind DN) authentication source",
Action: func(c *cli.Context) error {
return newAuthService().addLdapBindDn(c)
Action: func(ctx context.Context, cli *cli.Command) error {
return newAuthService().addLdapBindDn(ctx, cli)
},
Flags: ldapBindDnCLIFlags,
Flags: ldapBindDnCLIFlags(),
}
}
microcmdAuthUpdateLdapBindDn = &cli.Command{
func microcmdAuthUpdateLdapBindDn() *cli.Command {
return &cli.Command{
Name: "update-ldap",
Usage: "Update existing LDAP (via Bind DN) authentication source",
Action: func(c *cli.Context) error {
return newAuthService().updateLdapBindDn(c)
Action: func(ctx context.Context, cli *cli.Command) error {
return newAuthService().updateLdapBindDn(ctx, cli)
},
Flags: append([]cli.Flag{idFlag}, ldapBindDnCLIFlags...),
Flags: append([]cli.Flag{idFlag()}, ldapBindDnCLIFlags()...),
}
}
microcmdAuthAddLdapSimpleAuth = &cli.Command{
func microcmdAuthAddLdapSimpleAuth() *cli.Command {
return &cli.Command{
Name: "add-ldap-simple",
Usage: "Add new LDAP (simple auth) authentication source",
Action: func(c *cli.Context) error {
return newAuthService().addLdapSimpleAuth(c)
Action: func(ctx context.Context, cli *cli.Command) error {
return newAuthService().addLdapSimpleAuth(ctx, cli)
},
Flags: ldapSimpleAuthCLIFlags,
Flags: ldapSimpleAuthCLIFlags(),
}
}
microcmdAuthUpdateLdapSimpleAuth = &cli.Command{
func microcmdAuthUpdateLdapSimpleAuth() *cli.Command {
return &cli.Command{
Name: "update-ldap-simple",
Usage: "Update existing LDAP (simple auth) authentication source",
Action: func(c *cli.Context) error {
return newAuthService().updateLdapSimpleAuth(c)
Action: func(ctx context.Context, cli *cli.Command) error {
return newAuthService().updateLdapSimpleAuth(ctx, cli)
},
Flags: append([]cli.Flag{idFlag}, ldapSimpleAuthCLIFlags...),
Flags: append([]cli.Flag{idFlag()}, ldapSimpleAuthCLIFlags()...),
}
)
}
// newAuthService creates a service with default functions.
func newAuthService() *authService {
@ -183,7 +195,7 @@ func newAuthService() *authService {
}
// parseAuthSource assigns values on authSource according to command line flags.
func parseAuthSource(c *cli.Context, authSource *auth.Source) {
func parseAuthSource(c *cli.Command, authSource *auth.Source) {
if c.IsSet("name") {
authSource.Name = c.String("name")
}
@ -202,7 +214,7 @@ func parseAuthSource(c *cli.Context, authSource *auth.Source) {
}
// parseLdapConfig assigns values on config according to command line flags.
func parseLdapConfig(c *cli.Context, config *ldap.Source) error {
func parseLdapConfig(c *cli.Command, config *ldap.Source) error {
if c.IsSet("name") {
config.Name = c.String("name")
}
@ -289,7 +301,7 @@ func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) {
// getAuthSource gets the login source by its id defined in the command line flags.
// It returns an error if the id is not set, does not match any source or if the source is not of expected type.
func (a *authService) getAuthSource(ctx context.Context, c *cli.Context, authType auth.Type) (*auth.Source, error) {
func (a *authService) getAuthSource(ctx context.Context, c *cli.Command, authType auth.Type) (*auth.Source, error) {
if err := argsSet(c, "id"); err != nil {
return nil, err
}
@ -307,12 +319,12 @@ func (a *authService) getAuthSource(ctx context.Context, c *cli.Context, authTyp
}
// addLdapBindDn adds a new LDAP via Bind DN authentication source.
func (a *authService) addLdapBindDn(c *cli.Context) error {
func (a *authService) addLdapBindDn(ctx context.Context, c *cli.Command) error {
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-search-base", "user-filter", "email-attribute"); err != nil {
return err
}
ctx, cancel := installSignals()
ctx, cancel := installSignals(ctx)
defer cancel()
if err := a.initDB(ctx); err != nil {
@ -336,8 +348,8 @@ func (a *authService) addLdapBindDn(c *cli.Context) error {
}
// updateLdapBindDn updates a new LDAP via Bind DN authentication source.
func (a *authService) updateLdapBindDn(c *cli.Context) error {
ctx, cancel := installSignals()
func (a *authService) updateLdapBindDn(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
if err := a.initDB(ctx); err != nil {
@ -358,12 +370,12 @@ func (a *authService) updateLdapBindDn(c *cli.Context) error {
}
// addLdapSimpleAuth adds a new LDAP (simple auth) authentication source.
func (a *authService) addLdapSimpleAuth(c *cli.Context) error {
func (a *authService) addLdapSimpleAuth(ctx context.Context, c *cli.Command) error {
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-dn", "user-filter", "email-attribute"); err != nil {
return err
}
ctx, cancel := installSignals()
ctx, cancel := installSignals(ctx)
defer cancel()
if err := a.initDB(ctx); err != nil {
@ -387,8 +399,8 @@ func (a *authService) addLdapSimpleAuth(c *cli.Context) error {
}
// updateLdapSimpleAuth updates a new LDAP (simple auth) authentication source.
func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
ctx, cancel := installSignals()
func (a *authService) updateLdapSimpleAuth(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
if err := a.initDB(ctx); err != nil {

View file

@ -8,18 +8,17 @@ import (
"testing"
"forgejo.org/models/auth"
"forgejo.org/modules/test"
"forgejo.org/services/auth/source/ldap"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
func TestAddLdapBindDn(t *testing.T) {
// Mock cli functions to do not exit on error
osExiter := cli.OsExiter
defer func() { cli.OsExiter = osExiter }()
cli.OsExiter = func(code int) {}
defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
// Test cases
cases := []struct {
@ -226,12 +225,12 @@ func TestAddLdapBindDn(t *testing.T) {
}
// Create a copy of command to test
app := cli.NewApp()
app.Flags = microcmdAuthAddLdapBindDn.Flags
app := cli.Command{}
app.Flags = microcmdAuthAddLdapBindDn().Flags
app.Action = service.addLdapBindDn
// Run it
err := app.Run(c.args)
err := app.Run(t.Context(), c.args)
if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else {
@ -243,9 +242,7 @@ func TestAddLdapBindDn(t *testing.T) {
func TestAddLdapSimpleAuth(t *testing.T) {
// Mock cli functions to do not exit on error
osExiter := cli.OsExiter
defer func() { cli.OsExiter = osExiter }()
cli.OsExiter = func(code int) {}
defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
// Test cases
cases := []struct {
@ -457,12 +454,12 @@ func TestAddLdapSimpleAuth(t *testing.T) {
}
// Create a copy of command to test
app := cli.NewApp()
app.Flags = microcmdAuthAddLdapSimpleAuth.Flags
app := cli.Command{}
app.Flags = microcmdAuthAddLdapSimpleAuth().Flags
app.Action = service.addLdapSimpleAuth
// Run it
err := app.Run(c.args)
err := app.Run(t.Context(), c.args)
if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else {
@ -474,9 +471,7 @@ func TestAddLdapSimpleAuth(t *testing.T) {
func TestUpdateLdapBindDn(t *testing.T) {
// Mock cli functions to do not exit on error
osExiter := cli.OsExiter
defer func() { cli.OsExiter = osExiter }()
cli.OsExiter = func(code int) {}
defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
// Test cases
cases := []struct {
@ -920,12 +915,12 @@ func TestUpdateLdapBindDn(t *testing.T) {
}
// Create a copy of command to test
app := cli.NewApp()
app.Flags = microcmdAuthUpdateLdapBindDn.Flags
app := cli.Command{}
app.Flags = microcmdAuthUpdateLdapBindDn().Flags
app.Action = service.updateLdapBindDn
// Run it
err := app.Run(c.args)
err := app.Run(t.Context(), c.args)
if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else {
@ -937,9 +932,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
func TestUpdateLdapSimpleAuth(t *testing.T) {
// Mock cli functions to do not exit on error
osExiter := cli.OsExiter
defer func() { cli.OsExiter = osExiter }()
cli.OsExiter = func(code int) {}
defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
// Test cases
cases := []struct {
@ -1310,12 +1303,12 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
}
// Create a copy of command to test
app := cli.NewApp()
app.Flags = microcmdAuthUpdateLdapSimpleAuth.Flags
app := cli.Command{}
app.Flags = microcmdAuthUpdateLdapSimpleAuth().Flags
app.Action = service.updateLdapSimpleAuth
// Run it
err := app.Run(c.args)
err := app.Run(t.Context(), c.args)
if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else {

View file

@ -4,6 +4,7 @@
package cmd
import (
"context"
"errors"
"fmt"
"net/url"
@ -11,11 +12,11 @@ import (
auth_model "forgejo.org/models/auth"
"forgejo.org/services/auth/source/oauth2"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var (
oauthCLIFlags = []cli.Flag{
func oauthCLIFlags() []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "name",
Value: "",
@ -120,23 +121,27 @@ var (
Usage: "Activate automatic team membership removal depending on groups",
},
}
}
microcmdAuthAddOauth = &cli.Command{
func microcmdAuthAddOauth() *cli.Command {
return &cli.Command{
Name: "add-oauth",
Usage: "Add new Oauth authentication source",
Action: runAddOauth,
Flags: oauthCLIFlags,
Flags: oauthCLIFlags(),
}
}
microcmdAuthUpdateOauth = &cli.Command{
func microcmdAuthUpdateOauth() *cli.Command {
return &cli.Command{
Name: "update-oauth",
Usage: "Update existing Oauth authentication source",
Action: runUpdateOauth,
Flags: append(oauthCLIFlags[:1], append([]cli.Flag{idFlag}, oauthCLIFlags[1:]...)...),
Flags: append(oauthCLIFlags()[:1], append([]cli.Flag{idFlag()}, oauthCLIFlags()[1:]...)...),
}
)
}
func parseOAuth2Config(c *cli.Context) *oauth2.Source {
func parseOAuth2Config(_ context.Context, c *cli.Command) *oauth2.Source {
var customURLMapping *oauth2.CustomURLMapping
if c.IsSet("use-custom-urls") {
customURLMapping = &oauth2.CustomURLMapping{
@ -168,15 +173,15 @@ func parseOAuth2Config(c *cli.Context) *oauth2.Source {
}
}
func runAddOauth(c *cli.Context) error {
ctx, cancel := installSignals()
func runAddOauth(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
if err := initDB(ctx); err != nil {
return err
}
config := parseOAuth2Config(c)
config := parseOAuth2Config(ctx, c)
if config.Provider == "openidConnect" {
discoveryURL, err := url.Parse(config.OpenIDConnectAutoDiscoveryURL)
if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") {
@ -192,12 +197,12 @@ func runAddOauth(c *cli.Context) error {
})
}
func runUpdateOauth(c *cli.Context) error {
func runUpdateOauth(ctx context.Context, c *cli.Command) error {
if !c.IsSet("id") {
return errors.New("--id flag is missing")
}
ctx, cancel := installSignals()
ctx, cancel := installSignals(ctx)
defer cancel()
if err := initDB(ctx); err != nil {

View file

@ -4,6 +4,7 @@
package cmd
import (
"context"
"errors"
"strings"
@ -11,11 +12,11 @@ import (
"forgejo.org/modules/util"
"forgejo.org/services/auth/source/smtp"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var (
smtpCLIFlags = []cli.Flag{
func smtpCLIFlags() []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "name",
Value: "",
@ -71,23 +72,27 @@ var (
Value: true,
},
}
}
microcmdAuthAddSMTP = &cli.Command{
func microcmdAuthAddSMTP() *cli.Command {
return &cli.Command{
Name: "add-smtp",
Usage: "Add new SMTP authentication source",
Action: runAddSMTP,
Flags: smtpCLIFlags,
Flags: smtpCLIFlags(),
}
}
microcmdAuthUpdateSMTP = &cli.Command{
func microcmdAuthUpdateSMTP() *cli.Command {
return &cli.Command{
Name: "update-smtp",
Usage: "Update existing SMTP authentication source",
Action: runUpdateSMTP,
Flags: append(smtpCLIFlags[:1], append([]cli.Flag{idFlag}, smtpCLIFlags[1:]...)...),
Flags: append(smtpCLIFlags()[:1], append([]cli.Flag{idFlag()}, smtpCLIFlags()[1:]...)...),
}
)
}
func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
func parseSMTPConfig(c *cli.Command, conf *smtp.Source) error {
if c.IsSet("auth-type") {
conf.Auth = c.String("auth-type")
validAuthTypes := []string{"PLAIN", "LOGIN", "CRAM-MD5"}
@ -123,8 +128,8 @@ func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
return nil
}
func runAddSMTP(c *cli.Context) error {
ctx, cancel := installSignals()
func runAddSMTP(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
if err := initDB(ctx); err != nil {
@ -163,12 +168,12 @@ func runAddSMTP(c *cli.Context) error {
})
}
func runUpdateSMTP(c *cli.Context) error {
func runUpdateSMTP(ctx context.Context, c *cli.Command) error {
if !c.IsSet("id") {
return errors.New("--id flag is missing")
}
ctx, cancel := installSignals()
ctx, cancel := installSignals(ctx)
defer cancel()
if err := initDB(ctx); err != nil {

View file

@ -4,11 +4,13 @@
package cmd
import (
"context"
asymkey_model "forgejo.org/models/asymkey"
"forgejo.org/modules/graceful"
repo_service "forgejo.org/services/repository"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var (
@ -25,8 +27,8 @@ var (
}
)
func runRegenerateHooks(_ *cli.Context) error {
ctx, cancel := installSignals()
func runRegenerateHooks(ctx context.Context, _ *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
if err := initDB(ctx); err != nil {
@ -35,8 +37,8 @@ func runRegenerateHooks(_ *cli.Context) error {
return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
}
func runRegenerateKeys(_ *cli.Context) error {
ctx, cancel := installSignals()
func runRegenerateKeys(ctx context.Context, _ *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
if err := initDB(ctx); err != nil {

View file

@ -4,18 +4,21 @@
package cmd
import (
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var subcmdUser = &cli.Command{
Name: "user",
Usage: "Modify users",
Subcommands: []*cli.Command{
microcmdUserCreate,
microcmdUserList,
microcmdUserChangePassword,
microcmdUserDelete,
microcmdUserGenerateAccessToken,
microcmdUserMustChangePassword,
},
func subcmdUser() *cli.Command {
return &cli.Command{
Name: "user",
Usage: "Modify users",
Commands: []*cli.Command{
microcmdUserCreate(),
microcmdUserList(),
microcmdUserChangePassword(),
microcmdUserDelete(),
microcmdUserGenerateAccessToken(),
microcmdUserMustChangePassword(),
microcmdUserResetMFA(),
},
}
}

View file

@ -4,6 +4,7 @@
package cmd
import (
"context"
"errors"
"fmt"
@ -13,40 +14,42 @@ import (
"forgejo.org/modules/setting"
user_service "forgejo.org/services/user"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var microcmdUserChangePassword = &cli.Command{
Name: "change-password",
Usage: "Change a user's password",
Action: runChangePassword,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "username",
Aliases: []string{"u"},
Value: "",
Usage: "The user to change password for",
func microcmdUserChangePassword() *cli.Command {
return &cli.Command{
Name: "change-password",
Usage: "Change a user's password",
Action: runChangePassword,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "username",
Aliases: []string{"u"},
Value: "",
Usage: "The user to change password for",
},
&cli.StringFlag{
Name: "password",
Aliases: []string{"p"},
Value: "",
Usage: "New password to set for user",
},
&cli.BoolFlag{
Name: "must-change-password",
Usage: "User must change password",
Value: true,
},
},
&cli.StringFlag{
Name: "password",
Aliases: []string{"p"},
Value: "",
Usage: "New password to set for user",
},
&cli.BoolFlag{
Name: "must-change-password",
Usage: "User must change password",
Value: true,
},
},
}
}
func runChangePassword(c *cli.Context) error {
func runChangePassword(ctx context.Context, c *cli.Command) error {
if err := argsSet(c, "username", "password"); err != nil {
return err
}
ctx, cancel := installSignals()
ctx, cancel := installSignals(ctx)
defer cancel()
if err := initDB(ctx); err != nil {

View file

@ -4,6 +4,7 @@
package cmd
import (
"context"
"errors"
"fmt"
"strings"
@ -15,75 +16,76 @@ import (
"forgejo.org/modules/optional"
"forgejo.org/modules/setting"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var microcmdUserCreate = &cli.Command{
Name: "create",
Usage: "Create a new user in database",
Action: runCreateUser,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Usage: "Username. DEPRECATED: use username instead",
func microcmdUserCreate() *cli.Command {
return &cli.Command{
Name: "create",
Usage: "Create a new user in database",
Action: runCreateUser,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Usage: "Username. DEPRECATED: use username instead",
},
&cli.StringFlag{
Name: "username",
Usage: "Username",
},
&cli.StringFlag{
Name: "password",
Usage: "User password",
},
&cli.StringFlag{
Name: "email",
Usage: "User email address",
},
&cli.BoolFlag{
Name: "admin",
Usage: "User is an admin",
},
&cli.BoolFlag{
Name: "random-password",
Usage: "Generate a random password for the user",
},
&cli.BoolFlag{
Name: "must-change-password",
Usage: "Set this option to false to prevent forcing the user to change their password after initial login",
Value: true,
},
&cli.IntFlag{
Name: "random-password-length",
Usage: "Length of the random password to be generated",
Value: 12,
},
&cli.BoolFlag{
Name: "access-token",
Usage: "Generate access token for the user",
},
&cli.StringFlag{
Name: "access-token-name",
Usage: `Name of the generated access token`,
Value: "gitea-admin",
},
&cli.StringFlag{
Name: "access-token-scopes",
Usage: `Scopes of the generated access token, comma separated. Examples: "all", "public-only,read:issue", "write:repository,write:user"`,
Value: "all",
},
&cli.BoolFlag{
Name: "restricted",
Usage: "Make a restricted user account",
},
&cli.StringFlag{
Name: "fullname",
Usage: `The full, human-readable name of the user`,
},
},
&cli.StringFlag{
Name: "username",
Usage: "Username",
},
&cli.StringFlag{
Name: "password",
Usage: "User password",
},
&cli.StringFlag{
Name: "email",
Usage: "User email address",
},
&cli.BoolFlag{
Name: "admin",
Usage: "User is an admin",
},
&cli.BoolFlag{
Name: "random-password",
Usage: "Generate a random password for the user",
},
&cli.BoolFlag{
Name: "must-change-password",
Usage: "Set this option to false to prevent forcing the user to change their password after initial login",
Value: true,
DisableDefaultText: true,
},
&cli.IntFlag{
Name: "random-password-length",
Usage: "Length of the random password to be generated",
Value: 12,
},
&cli.BoolFlag{
Name: "access-token",
Usage: "Generate access token for the user",
},
&cli.StringFlag{
Name: "access-token-name",
Usage: `Name of the generated access token`,
Value: "gitea-admin",
},
&cli.StringFlag{
Name: "access-token-scopes",
Usage: `Scopes of the generated access token, comma separated. Examples: "all", "public-only,read:issue", "write:repository,write:user"`,
Value: "all",
},
&cli.BoolFlag{
Name: "restricted",
Usage: "Make a restricted user account",
},
&cli.StringFlag{
Name: "fullname",
Usage: `The full, human-readable name of the user`,
},
},
}
}
func runCreateUser(c *cli.Context) error {
func runCreateUser(ctx context.Context, c *cli.Command) error {
// this command highly depends on the many setting options (create org, visibility, etc.), so it must have a full setting load first
// duplicate setting loading should be safe at the moment, but it should be refactored & improved in the future.
setting.LoadSettings()
@ -108,10 +110,10 @@ func runCreateUser(c *cli.Context) error {
username = c.String("username")
} else {
username = c.String("name")
_, _ = fmt.Fprintf(c.App.ErrWriter, "--name flag is deprecated. Use --username instead.\n")
_, _ = fmt.Fprint(c.Root().ErrWriter, "--name flag is deprecated. Use --username instead.\n")
}
ctx, cancel := installSignals()
ctx, cancel := installSignals(ctx)
defer cancel()
if err := initDB(ctx); err != nil {

View file

@ -4,6 +4,7 @@
package cmd
import (
"context"
"errors"
"fmt"
"strings"
@ -12,41 +13,43 @@ import (
"forgejo.org/modules/storage"
user_service "forgejo.org/services/user"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var microcmdUserDelete = &cli.Command{
Name: "delete",
Usage: "Delete specific user by id, name or email",
Flags: []cli.Flag{
&cli.Int64Flag{
Name: "id",
Usage: "ID of user of the user to delete",
func microcmdUserDelete() *cli.Command {
return &cli.Command{
Name: "delete",
Usage: "Delete specific user by id, name or email",
Flags: []cli.Flag{
&cli.Int64Flag{
Name: "id",
Usage: "ID of user of the user to delete",
},
&cli.StringFlag{
Name: "username",
Aliases: []string{"u"},
Usage: "Username of the user to delete",
},
&cli.StringFlag{
Name: "email",
Aliases: []string{"e"},
Usage: "Email of the user to delete",
},
&cli.BoolFlag{
Name: "purge",
Usage: "Purge user, all their repositories, organizations and comments",
},
},
&cli.StringFlag{
Name: "username",
Aliases: []string{"u"},
Usage: "Username of the user to delete",
},
&cli.StringFlag{
Name: "email",
Aliases: []string{"e"},
Usage: "Email of the user to delete",
},
&cli.BoolFlag{
Name: "purge",
Usage: "Purge user, all their repositories, organizations and comments",
},
},
Action: runDeleteUser,
Action: runDeleteUser,
}
}
func runDeleteUser(c *cli.Context) error {
func runDeleteUser(ctx context.Context, c *cli.Command) error {
if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") {
return errors.New("You must provide the id, username or email of a user to delete")
}
ctx, cancel := installSignals()
ctx, cancel := installSignals(ctx)
defer cancel()
if err := initDB(ctx); err != nil {

View file

@ -4,49 +4,52 @@
package cmd
import (
"context"
"errors"
"fmt"
auth_model "forgejo.org/models/auth"
user_model "forgejo.org/models/user"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var microcmdUserGenerateAccessToken = &cli.Command{
Name: "generate-access-token",
Usage: "Generate an access token for a specific user",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "username",
Aliases: []string{"u"},
Usage: "Username",
func microcmdUserGenerateAccessToken() *cli.Command {
return &cli.Command{
Name: "generate-access-token",
Usage: "Generate an access token for a specific user",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "username",
Aliases: []string{"u"},
Usage: "Username",
},
&cli.StringFlag{
Name: "token-name",
Aliases: []string{"t"},
Usage: "Token name",
Value: "gitea-admin",
},
&cli.BoolFlag{
Name: "raw",
Usage: "Display only the token value",
},
&cli.StringFlag{
Name: "scopes",
Value: "all",
Usage: `Comma separated list of scopes to apply to access token, examples: "all", "public-only,read:issue", "write:repository,write:user"`,
},
},
&cli.StringFlag{
Name: "token-name",
Aliases: []string{"t"},
Usage: "Token name",
Value: "gitea-admin",
},
&cli.BoolFlag{
Name: "raw",
Usage: "Display only the token value",
},
&cli.StringFlag{
Name: "scopes",
Value: "all",
Usage: `Comma separated list of scopes to apply to access token, examples: "all", "public-only,read:issue", "write:repository,write:user"`,
},
},
Action: runGenerateAccessToken,
Action: runGenerateAccessToken,
}
}
func runGenerateAccessToken(c *cli.Context) error {
func runGenerateAccessToken(ctx context.Context, c *cli.Command) error {
if !c.IsSet("username") {
return errors.New("you must provide a username to generate a token for")
}
ctx, cancel := installSignals()
ctx, cancel := installSignals(ctx)
defer cancel()
if err := initDB(ctx); err != nil {

View file

@ -4,29 +4,32 @@
package cmd
import (
"context"
"fmt"
"os"
"text/tabwriter"
user_model "forgejo.org/models/user"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var microcmdUserList = &cli.Command{
Name: "list",
Usage: "List users",
Action: runListUsers,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "admin",
Usage: "List only admin users",
func microcmdUserList() *cli.Command {
return &cli.Command{
Name: "list",
Usage: "List users",
Action: runListUsers,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "admin",
Usage: "List only admin users",
},
},
},
}
}
func runListUsers(c *cli.Context) error {
ctx, cancel := installSignals()
func runListUsers(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
if err := initDB(ctx); err != nil {
@ -41,7 +44,7 @@ func runListUsers(c *cli.Context) error {
w := tabwriter.NewWriter(os.Stdout, 5, 0, 1, ' ', 0)
if c.IsSet("admin") {
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\n")
fmt.Fprint(w, "ID\tUsername\tEmail\tIsActive\n")
for _, u := range users {
if u.IsAdmin {
fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", u.ID, u.Name, u.Email, u.IsActive)
@ -49,7 +52,7 @@ func runListUsers(c *cli.Context) error {
}
} else {
twofa := user_model.UserList(users).GetTwoFaStatus(ctx)
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\n")
fmt.Fprint(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\n")
for _, u := range users {
fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%t\t%t\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin, twofa[u.ID])
}

View file

@ -4,38 +4,41 @@
package cmd
import (
"context"
"errors"
"fmt"
user_model "forgejo.org/models/user"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
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",
Aliases: []string{"A"},
Usage: "All users must change password, except those explicitly excluded with --exclude",
func microcmdUserMustChangePassword() *cli.Command {
return &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",
Aliases: []string{"A"},
Usage: "All users must change password, except those explicitly excluded with --exclude",
},
&cli.StringSliceFlag{
Name: "exclude",
Aliases: []string{"e"},
Usage: "Do not change the must-change-password flag for these users",
},
&cli.BoolFlag{
Name: "unset",
Usage: "Instead of setting the must-change-password flag, unset it",
},
},
&cli.StringSliceFlag{
Name: "exclude",
Aliases: []string{"e"},
Usage: "Do not change the must-change-password flag for these users",
},
&cli.BoolFlag{
Name: "unset",
Usage: "Instead of setting the must-change-password flag, unset it",
},
},
}
}
func runMustChangePassword(c *cli.Context) error {
ctx, cancel := installSignals()
func runMustChangePassword(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
if c.NArg() == 0 && !c.IsSet("all") {

View file

@ -0,0 +1,73 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"context"
"fmt"
auth_model "forgejo.org/models/auth"
user_model "forgejo.org/models/user"
"github.com/urfave/cli/v3"
)
func microcmdUserResetMFA() *cli.Command {
return &cli.Command{
Name: "reset-mfa",
Usage: "Remove all two-factor authentication configurations for a user",
Action: runResetMFA,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "username",
Aliases: []string{"u"},
Value: "",
Usage: "The user to update",
},
},
}
}
func runResetMFA(ctx context.Context, c *cli.Command) error {
if err := argsSet(c, "username"); err != nil {
return err
}
ctx, cancel := installSignals(ctx)
defer cancel()
if err := initDB(ctx); err != nil {
return err
}
user, err := user_model.GetUserByName(ctx, c.String("username"))
if err != nil {
return err
}
webAuthnList, err := auth_model.GetWebAuthnCredentialsByUID(ctx, user.ID)
if err != nil {
return err
}
for _, credential := range webAuthnList {
if _, err := auth_model.DeleteCredential(ctx, credential.ID, user.ID); err != nil {
return err
}
}
tfaModes, err := auth_model.GetTwoFactorByUID(ctx, user.ID)
if err == nil && tfaModes != nil {
if err := auth_model.DeleteTwoFactorByID(ctx, tfaModes.ID, user.ID); err != nil {
return err
}
} else {
if _, is := err.(auth_model.ErrTwoFactorNotEnrolled); !is {
return err
}
}
fmt.Printf("%s's two-factor authentication settings have been removed!\n", user.Name)
return nil
}

View file

@ -6,6 +6,7 @@
package cmd
import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
@ -20,47 +21,49 @@ import (
"strings"
"time"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// CmdCert represents the available cert sub-command.
var CmdCert = &cli.Command{
Name: "cert",
Usage: "Generate self-signed certificate",
Description: `Generate a self-signed X.509 certificate for a TLS server.
func cmdCert() *cli.Command {
return &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{
Name: "host",
Value: "",
Usage: "Comma-separated hostnames and IPs to generate a certificate for",
Action: runCert,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "host",
Value: "",
Usage: "Comma-separated hostnames and IPs to generate a certificate for",
},
&cli.StringFlag{
Name: "ecdsa-curve",
Value: "",
Usage: "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521",
},
&cli.IntFlag{
Name: "rsa-bits",
Value: 3072,
Usage: "Size of RSA key to generate. Ignored if --ecdsa-curve is set",
},
&cli.StringFlag{
Name: "start-date",
Value: "",
Usage: "Creation date formatted as Jan 1 15:04:05 2011",
},
&cli.DurationFlag{
Name: "duration",
Value: 365 * 24 * time.Hour,
Usage: "Duration that certificate is valid for",
},
&cli.BoolFlag{
Name: "ca",
Usage: "whether this cert should be its own Certificate Authority",
},
},
&cli.StringFlag{
Name: "ecdsa-curve",
Value: "",
Usage: "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521",
},
&cli.IntFlag{
Name: "rsa-bits",
Value: 3072,
Usage: "Size of RSA key to generate. Ignored if --ecdsa-curve is set",
},
&cli.StringFlag{
Name: "start-date",
Value: "",
Usage: "Creation date formatted as Jan 1 15:04:05 2011",
},
&cli.DurationFlag{
Name: "duration",
Value: 365 * 24 * time.Hour,
Usage: "Duration that certificate is valid for",
},
&cli.BoolFlag{
Name: "ca",
Usage: "whether this cert should be its own Certificate Authority",
},
},
}
}
func publicKey(priv any) any {
@ -89,7 +92,7 @@ func pemBlockForKey(priv any) *pem.Block {
}
}
func runCert(c *cli.Context) error {
func runCert(ctx context.Context, c *cli.Command) error {
if err := argsSet(c, "host"); err != nil {
return err
}

View file

@ -20,19 +20,21 @@ import (
"forgejo.org/modules/setting"
"forgejo.org/modules/util"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// argsSet checks that all the required arguments are set. args is a list of
// arguments that must be set in the passed Context.
func argsSet(c *cli.Context, args ...string) error {
func argsSet(c *cli.Command, args ...string) error {
for _, a := range args {
if !c.IsSet(a) {
return errors.New(a + " is not set")
}
if util.IsEmptyString(c.String(a)) {
return errors.New(a + " is required")
if s, ok := c.Value(a).(string); ok {
if util.IsEmptyString(s) {
return errors.New(a + " is required")
}
}
}
return nil
@ -73,8 +75,8 @@ If this is the intended configuration file complete the [database] section.`, se
return nil
}
func installSignals() (context.Context, context.CancelFunc) {
ctx, cancel := context.WithCancel(context.Background())
func installSignals(ctx context.Context) (context.Context, context.CancelFunc) {
ctx, cancel := context.WithCancel(ctx)
go func() {
// install notify
signalChannel := make(chan os.Signal, 1)
@ -109,7 +111,7 @@ 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 {
func globalBool(c *cli.Command, name string) bool {
for _, ctx := range c.Lineage() {
if ctx.Bool(name) {
return true
@ -120,16 +122,16 @@ func globalBool(c *cli.Context, name string) bool {
// 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 {
func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(ctx context.Context, cli *cli.Command) (context.Context, error) {
return func(ctx context.Context, cli *cli.Command) (context.Context, error) {
level := defaultLevel
if globalBool(c, "quiet") {
if globalBool(cli, "quiet") {
level = log.FATAL
}
if globalBool(c, "debug") || globalBool(c, "verbose") {
if globalBool(cli, "debug") || globalBool(cli, "verbose") {
level = log.TRACE
}
log.SetConsoleLogger(log.DEFAULT, "console-default", level)
return nil
return ctx, nil
}
}

View file

@ -1,65 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"fmt"
"os"
"strings"
"github.com/urfave/cli/v2"
)
// CmdDocs represents the available docs sub-command.
var CmdDocs = &cli.Command{
Name: "docs",
Usage: "Output CLI documentation",
Description: "A command to output Forgejo's CLI documentation, optionally to a file.",
Action: runDocs,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "man",
Usage: "Output man pages instead",
},
&cli.StringFlag{
Name: "output",
Aliases: []string{"o"},
Usage: "Path to output to instead of stdout (will overwrite if exists)",
},
},
}
func runDocs(ctx *cli.Context) error {
docs, err := ctx.App.ToMarkdown()
if ctx.Bool("man") {
docs, err = ctx.App.ToMan()
}
if err != nil {
return err
}
if !ctx.Bool("man") {
// Clean up markdown. The following bug was fixed in v2, but is present in v1.
// It affects markdown output (even though the issue is referring to man pages)
// https://github.com/urfave/cli/issues/1040
firstHashtagIndex := strings.Index(docs, "#")
if firstHashtagIndex > 0 {
docs = docs[firstHashtagIndex:]
}
}
out := os.Stdout
if ctx.String("output") != "" {
fi, err := os.Create(ctx.String("output"))
if err != nil {
return err
}
defer fi.Close()
out = fi
}
_, err = fmt.Fprintln(out, docs)
return err
}

View file

@ -4,6 +4,7 @@
package cmd
import (
"context"
"fmt"
golog "log"
"os"
@ -19,80 +20,86 @@ import (
"forgejo.org/modules/setting"
"forgejo.org/services/doctor"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// CmdDoctor represents the available doctor sub-command.
var CmdDoctor = &cli.Command{
Name: "doctor",
Usage: "Diagnose and optionally fix problems, convert or re-create database tables",
Description: "A command to diagnose problems with the current Forgejo instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
func cmdDoctor() *cli.Command {
return &cli.Command{
Name: "doctor",
Usage: "Diagnose and optionally fix problems, convert or re-create database tables",
Description: "A command to diagnose problems with the current Forgejo instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
Subcommands: []*cli.Command{
cmdDoctorCheck,
cmdRecreateTable,
cmdDoctorConvert,
},
Commands: []*cli.Command{
cmdDoctorCheck(),
cmdRecreateTable(),
cmdDoctorConvert(),
},
}
}
var cmdDoctorCheck = &cli.Command{
Name: "check",
Usage: "Diagnose and optionally fix problems",
Description: "A command to diagnose problems with the current Forgejo instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
Action: runDoctorCheck,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "list",
Usage: "List the available checks",
func cmdDoctorCheck() *cli.Command {
return &cli.Command{
Name: "check",
Usage: "Diagnose and optionally fix problems",
Description: "A command to diagnose problems with the current Forgejo instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
Action: runDoctorCheck,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "list",
Usage: "List the available checks",
},
&cli.BoolFlag{
Name: "default",
Usage: "Run the default checks (if neither --run or --all is set, this is the default behaviour)",
},
&cli.StringSliceFlag{
Name: "run",
Usage: "Run the provided checks - (if --default is set, the default checks will also run)",
},
&cli.BoolFlag{
Name: "all",
Usage: "Run all the available checks",
},
&cli.BoolFlag{
Name: "fix",
Usage: "Automatically fix what we can",
},
&cli.StringFlag{
Name: "log-file",
Usage: `Name of the log file (no verbose log output by default). Set to "-" to output to stdout`,
},
&cli.BoolFlag{
Name: "color",
Aliases: []string{"H"},
Usage: "Use color for outputted information",
},
},
&cli.BoolFlag{
Name: "default",
Usage: "Run the default checks (if neither --run or --all is set, this is the default behaviour)",
},
&cli.StringSliceFlag{
Name: "run",
Usage: "Run the provided checks - (if --default is set, the default checks will also run)",
},
&cli.BoolFlag{
Name: "all",
Usage: "Run all the available checks",
},
&cli.BoolFlag{
Name: "fix",
Usage: "Automatically fix what we can",
},
&cli.StringFlag{
Name: "log-file",
Usage: `Name of the log file (no verbose log output by default). Set to "-" to output to stdout`,
},
&cli.BoolFlag{
Name: "color",
Aliases: []string{"H"},
Usage: "Use color for outputted information",
},
},
}
}
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{
Name: "debug",
Usage: "Print SQL commands sent",
func cmdRecreateTable() *cli.Command {
return &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{
Name: "debug",
Usage: "Print SQL commands sent",
},
},
},
Description: `The database definitions Forgejo uses change across versions, sometimes changing default values and leaving old unused columns.
Description: `The database definitions Forgejo uses change across versions, sometimes changing default values and leaving old unused columns.
This command will cause Xorm to recreate tables, copying over the data and deleting the old table.
You should back-up your database before doing this and ensure that your database is up-to-date first.`,
Action: runRecreateTable,
Action: runRecreateTable,
}
}
func runRecreateTable(ctx *cli.Context) error {
stdCtx, cancel := installSignals()
func runRecreateTable(stdCtx context.Context, ctx *cli.Command) error {
stdCtx, cancel := installSignals(stdCtx)
defer cancel()
// Redirect the default golog to here
@ -143,7 +150,7 @@ func runRecreateTable(ctx *cli.Context) error {
})
}
func setupDoctorDefaultLogger(ctx *cli.Context, colorize bool) {
func setupDoctorDefaultLogger(ctx *cli.Command, colorize bool) {
// Silence the default loggers
setupConsoleLogger(log.FATAL, log.CanColorStderr, os.Stderr)
@ -165,8 +172,8 @@ func setupDoctorDefaultLogger(ctx *cli.Context, colorize bool) {
}
}
func runDoctorCheck(ctx *cli.Context) error {
stdCtx, cancel := installSignals()
func runDoctorCheck(stdCtx context.Context, ctx *cli.Command) error {
stdCtx, cancel := installSignals(stdCtx)
defer cancel()
colorize := log.CanColorStdout

View file

@ -4,25 +4,28 @@
package cmd
import (
"context"
"fmt"
"forgejo.org/models/db"
"forgejo.org/modules/log"
"forgejo.org/modules/setting"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// cmdDoctorConvert represents the available convert sub-command.
var cmdDoctorConvert = &cli.Command{
Name: "convert",
Usage: "Convert the database",
Description: "A command to convert an existing MySQL database from utf8 to utf8mb4",
Action: runDoctorConvert,
func cmdDoctorConvert() *cli.Command {
return &cli.Command{
Name: "convert",
Usage: "Convert the database",
Description: "A command to convert an existing MySQL database from utf8 to utf8mb4",
Action: runDoctorConvert,
}
}
func runDoctorConvert(ctx *cli.Context) error {
stdCtx, cancel := installSignals()
func runDoctorConvert(stdCtx context.Context, ctx *cli.Command) error {
stdCtx, cancel := installSignals(stdCtx)
defer cancel()
if err := initDB(stdCtx); err != nil {

View file

@ -11,7 +11,7 @@ import (
"forgejo.org/services/doctor"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
func TestDoctorRun(t *testing.T) {
@ -22,12 +22,12 @@ func TestDoctorRun(t *testing.T) {
SkipDatabaseInitialization: true,
})
app := cli.NewApp()
app.Commands = []*cli.Command{cmdDoctorCheck}
err := app.Run([]string{"./gitea", "check", "--run", "test-check"})
app := cli.Command{}
app.Commands = []*cli.Command{cmdDoctorCheck()}
err := app.Run(t.Context(), []string{"./gitea", "check", "--run", "test-check"})
require.NoError(t, err)
err = app.Run([]string{"./gitea", "check", "--run", "no-such"})
err = app.Run(t.Context(), []string{"./gitea", "check", "--run", "no-such"})
require.ErrorContains(t, err, `unknown checks: "no-such"`)
err = app.Run([]string{"./gitea", "check", "--run", "test-check,no-such"})
err = app.Run(t.Context(), []string{"./gitea", "check", "--run", "test-check,no-such"})
require.ErrorContains(t, err, `unknown checks: "no-such"`)
}

View file

@ -5,6 +5,8 @@
package cmd
import (
"context"
"errors"
"fmt"
"io"
"os"
@ -22,7 +24,7 @@ import (
"code.forgejo.org/go-chi/session"
"github.com/mholt/archiver/v3"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
func addReader(w archiver.Writer, r io.ReadCloser, info os.FileInfo, customName string, verbose bool) error {
@ -83,6 +85,10 @@ func (o *outputType) Set(value string) error {
return fmt.Errorf("allowed values are %s", o.Join())
}
func (o *outputType) Get() any {
return o.String()
}
func (o outputType) String() string {
if o.selected == "" {
return o.Default
@ -96,79 +102,81 @@ var outputTypeEnum = &outputType{
}
// CmdDump represents the available dump sub-command.
var CmdDump = &cli.Command{
Name: "dump",
Usage: "Dump Forgejo files and database",
Description: `Dump compresses all related files and database into zip file.
func cmdDump() *cli.Command {
return &cli.Command{
Name: "dump",
Usage: "Dump Forgejo files and database",
Description: `Dump compresses all related files and database into zip file.
It can be used for backup and capture Forgejo server image to send to maintainer`,
Action: runDump,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "file",
Aliases: []string{"f"},
Value: fmt.Sprintf("forgejo-dump-%d.zip", time.Now().Unix()),
Usage: "Name of the dump file which will be created. Supply '-' for stdout. See type for available types.",
Action: runDump,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "file",
Aliases: []string{"f"},
Value: fmt.Sprintf("forgejo-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",
Aliases: []string{"V"},
Usage: "Show process details",
},
&cli.BoolFlag{
Name: "quiet",
Aliases: []string{"q"},
Usage: "Only display warnings and errors",
},
&cli.StringFlag{
Name: "tempdir",
Aliases: []string{"t"},
Usage: "Temporary dir path",
},
&cli.StringFlag{
Name: "database",
Aliases: []string{"d"},
Usage: "Specify the database SQL syntax: sqlite3, mysql, postgres",
},
&cli.BoolFlag{
Name: "skip-repository",
Aliases: []string{"R"},
Usage: "Skip repositories",
},
&cli.BoolFlag{
Name: "skip-log",
Aliases: []string{"L"},
Usage: "Skip logs",
},
&cli.BoolFlag{
Name: "skip-custom-dir",
Usage: "Skip custom directory",
},
&cli.BoolFlag{
Name: "skip-lfs-data",
Usage: "Skip LFS data",
},
&cli.BoolFlag{
Name: "skip-attachment-data",
Usage: "Skip attachment data",
},
&cli.BoolFlag{
Name: "skip-package-data",
Usage: "Skip package data",
},
&cli.BoolFlag{
Name: "skip-index",
Usage: "Skip bleve index data",
},
&cli.BoolFlag{
Name: "skip-repo-archives",
Usage: "Skip repository archives",
},
&cli.GenericFlag{
Name: "type",
Value: outputTypeEnum,
Usage: fmt.Sprintf("Dump output format: %s", outputTypeEnum.Join()),
},
},
&cli.BoolFlag{
Name: "verbose",
Aliases: []string{"V"},
Usage: "Show process details",
},
&cli.BoolFlag{
Name: "quiet",
Aliases: []string{"q"},
Usage: "Only display warnings and errors",
},
&cli.StringFlag{
Name: "tempdir",
Aliases: []string{"t"},
Usage: "Temporary dir path",
},
&cli.StringFlag{
Name: "database",
Aliases: []string{"d"},
Usage: "Specify the database SQL syntax: sqlite3, mysql, postgres",
},
&cli.BoolFlag{
Name: "skip-repository",
Aliases: []string{"R"},
Usage: "Skip repositories",
},
&cli.BoolFlag{
Name: "skip-log",
Aliases: []string{"L"},
Usage: "Skip logs",
},
&cli.BoolFlag{
Name: "skip-custom-dir",
Usage: "Skip custom directory",
},
&cli.BoolFlag{
Name: "skip-lfs-data",
Usage: "Skip LFS data",
},
&cli.BoolFlag{
Name: "skip-attachment-data",
Usage: "Skip attachment data",
},
&cli.BoolFlag{
Name: "skip-package-data",
Usage: "Skip package data",
},
&cli.BoolFlag{
Name: "skip-index",
Usage: "Skip bleve index data",
},
&cli.BoolFlag{
Name: "skip-repo-archives",
Usage: "Skip repository archives",
},
&cli.GenericFlag{
Name: "type",
Value: outputTypeEnum,
Usage: fmt.Sprintf("Dump output format: %s", outputTypeEnum.Join()),
},
},
}
}
func fatal(format string, args ...any) {
@ -176,7 +184,7 @@ func fatal(format string, args ...any) {
log.Fatal(format, args...)
}
func runDump(ctx *cli.Context) error {
func runDump(stdCtx context.Context, ctx *cli.Command) error {
var file *os.File
fileName := ctx.String("file")
outType := ctx.String("type")
@ -212,16 +220,16 @@ func runDump(ctx *cli.Context) error {
if !setting.InstallLock {
log.Error("Is '%s' really the right config path?\n", setting.CustomConf)
return fmt.Errorf("forgejo is not initialized")
return errors.New("forgejo is not initialized")
}
setting.LoadSettings() // cannot access session settings otherwise
verbose := ctx.Bool("verbose")
if verbose && ctx.Bool("quiet") {
return fmt.Errorf("--quiet and --verbose cannot both be set")
return errors.New("--quiet and --verbose cannot both be set")
}
stdCtx, cancel := installSignals()
stdCtx, cancel := installSignals(stdCtx)
defer cancel()
err := db.InitEngine(stdCtx)

View file

@ -19,68 +19,70 @@ import (
"forgejo.org/services/convert"
"forgejo.org/services/migrations"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// CmdDumpRepository represents the available dump repository sub-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{
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",
Aliases: []string{"r"},
Value: "./data",
Usage: "Repository dir path to store the data",
},
&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{
Name: "auth_username",
Value: "",
Usage: "The username to visit the clone_addr",
},
&cli.StringFlag{
Name: "auth_password",
Value: "",
Usage: "The password to visit the clone_addr",
},
&cli.StringFlag{
Name: "auth_token",
Value: "",
Usage: "The personal token to visit the clone_addr",
},
&cli.StringFlag{
Name: "owner_name",
Value: "",
Usage: "The data will be stored on a directory with owner name if not empty",
},
&cli.StringFlag{
Name: "repo_name",
Value: "",
Usage: "The data will be stored on a directory with repository name if not empty",
},
&cli.StringFlag{
Name: "units",
Value: "",
Usage: `Which items will be migrated, one or more units should be separated as comma.
func cmdDumpRepository() *cli.Command {
return &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{
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",
Aliases: []string{"r"},
Value: "./data",
Usage: "Repository dir path to store the data",
},
&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{
Name: "auth_username",
Value: "",
Usage: "The username to visit the clone_addr",
},
&cli.StringFlag{
Name: "auth_password",
Value: "",
Usage: "The password to visit the clone_addr",
},
&cli.StringFlag{
Name: "auth_token",
Value: "",
Usage: "The personal token to visit the clone_addr",
},
&cli.StringFlag{
Name: "owner_name",
Value: "",
Usage: "The data will be stored on a directory with owner name if not empty",
},
&cli.StringFlag{
Name: "repo_name",
Value: "",
Usage: "The data will be stored on a directory with repository name if not empty",
},
&cli.StringFlag{
Name: "units",
Value: "",
Usage: `Which items will be migrated, 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.`,
},
},
},
}
}
func runDumpRepository(ctx *cli.Context) error {
stdCtx, cancel := installSignals()
func runDumpRepository(stdCtx context.Context, ctx *cli.Command) error {
stdCtx, cancel := installSignals(stdCtx)
defer cancel()
if err := initDB(stdCtx); err != nil {

View file

@ -4,6 +4,7 @@
package cmd
import (
"context"
"errors"
"fmt"
"os"
@ -19,23 +20,25 @@ import (
"forgejo.org/modules/util"
"github.com/gobwas/glob"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// CmdEmbedded represents the available extract sub-command.
var (
CmdEmbedded = &cli.Command{
func cmdEmbedded() *cli.Command {
return &cli.Command{
Name: "embedded",
Usage: "Extract embedded resources",
Description: "A command for extracting embedded resources, like templates and images",
Subcommands: []*cli.Command{
subcmdList,
subcmdView,
subcmdExtract,
Commands: []*cli.Command{
subcmdList(),
subcmdView(),
subcmdExtract(),
},
}
}
subcmdList = &cli.Command{
func subcmdList() *cli.Command {
return &cli.Command{
Name: "list",
Usage: "List files matching the given pattern",
Action: runList,
@ -47,8 +50,10 @@ var (
},
},
}
}
subcmdView = &cli.Command{
func subcmdView() *cli.Command {
return &cli.Command{
Name: "view",
Usage: "View a file matching the given pattern",
Action: runView,
@ -60,8 +65,10 @@ var (
},
},
}
}
subcmdExtract = &cli.Command{
func subcmdExtract() *cli.Command {
return &cli.Command{
Name: "extract",
Usage: "Extract resources",
Action: runExtract,
@ -90,9 +97,9 @@ var (
},
},
}
}
matchedAssetFiles []assetFile
)
var matchedAssetFiles []assetFile
type assetFile struct {
fs *assetfs.LayeredFS
@ -100,7 +107,7 @@ type assetFile struct {
path string
}
func initEmbeddedExtractor(c *cli.Context) error {
func initEmbeddedExtractor(_ context.Context, c *cli.Command) error {
setupConsoleLogger(log.ERROR, log.CanColorStderr, os.Stderr)
patterns, err := compileCollectPatterns(c.Args().Slice())
@ -115,32 +122,32 @@ func initEmbeddedExtractor(c *cli.Context) error {
return nil
}
func runList(c *cli.Context) error {
if err := runListDo(c); err != nil {
func runList(ctx context.Context, c *cli.Command) error {
if err := runListDo(ctx, c); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return err
}
return nil
}
func runView(c *cli.Context) error {
if err := runViewDo(c); err != nil {
func runView(ctx context.Context, c *cli.Command) error {
if err := runViewDo(ctx, c); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return err
}
return nil
}
func runExtract(c *cli.Context) error {
if err := runExtractDo(c); err != nil {
func runExtract(ctx context.Context, c *cli.Command) error {
if err := runExtractDo(ctx, c); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return err
}
return nil
}
func runListDo(c *cli.Context) error {
if err := initEmbeddedExtractor(c); err != nil {
func runListDo(ctx context.Context, c *cli.Command) error {
if err := initEmbeddedExtractor(ctx, c); err != nil {
return err
}
@ -151,8 +158,8 @@ func runListDo(c *cli.Context) error {
return nil
}
func runViewDo(c *cli.Context) error {
if err := initEmbeddedExtractor(c); err != nil {
func runViewDo(ctx context.Context, c *cli.Command) error {
if err := initEmbeddedExtractor(ctx, c); err != nil {
return err
}
@ -174,8 +181,8 @@ func runViewDo(c *cli.Context) error {
return nil
}
func runExtractDo(c *cli.Context) error {
if err := initEmbeddedExtractor(c); err != nil {
func runExtractDo(ctx context.Context, c *cli.Command) error {
if err := initEmbeddedExtractor(ctx, c); err != nil {
return err
}
@ -271,7 +278,7 @@ func extractAsset(d string, a assetFile, overwrite, rename bool) error {
return nil
}
func collectAssetFilesByPattern(c *cli.Context, globs []glob.Glob, path string, layer *assetfs.Layer) {
func collectAssetFilesByPattern(c *cli.Command, globs []glob.Glob, path string, layer *assetfs.Layer) {
fs := assetfs.Layered(layer)
files, err := fs.ListAllFiles(".", true)
if err != nil {

View file

@ -6,6 +6,7 @@ package forgejo
import (
"context"
"encoding/hex"
"errors"
"fmt"
"io"
"os"
@ -16,14 +17,14 @@ import (
"forgejo.org/modules/setting"
private_routers "forgejo.org/routers/private"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
func CmdActions(ctx context.Context) *cli.Command {
return &cli.Command{
Name: "actions",
Usage: "Commands for managing Forgejo Actions",
Subcommands: []*cli.Command{
Commands: []*cli.Command{
SubcmdActionsGenerateRunnerToken(ctx),
SubcmdActionsGenerateRunnerSecret(ctx),
SubcmdActionsRegister(ctx),
@ -36,7 +37,7 @@ func SubcmdActionsGenerateRunnerToken(ctx context.Context) *cli.Command {
Name: "generate-runner-token",
Usage: "Generate a new token for a runner to use to register with the server",
Before: prepareWorkPathAndCustomConf(ctx),
Action: func(cliCtx *cli.Context) error { return RunGenerateActionsRunnerToken(ctx, cliCtx) },
Action: RunGenerateActionsRunnerToken,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "scope",
@ -52,7 +53,7 @@ func SubcmdActionsGenerateRunnerSecret(ctx context.Context) *cli.Command {
return &cli.Command{
Name: "generate-secret",
Usage: "Generate a secret suitable for input to the register subcommand",
Action: func(cliCtx *cli.Context) error { return RunGenerateSecret(ctx, cliCtx) },
Action: RunGenerateSecret,
}
}
@ -61,7 +62,7 @@ func SubcmdActionsRegister(ctx context.Context) *cli.Command {
Name: "register",
Usage: "Idempotent registration of a runner using a shared secret",
Before: prepareWorkPathAndCustomConf(ctx),
Action: func(cliCtx *cli.Context) error { return RunRegister(ctx, cliCtx) },
Action: RunRegister,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "secret",
@ -105,26 +106,26 @@ func SubcmdActionsRegister(ctx context.Context) *cli.Command {
}
}
func readSecret(ctx context.Context, cliCtx *cli.Context) (string, error) {
if cliCtx.IsSet("secret") {
return cliCtx.String("secret"), nil
func readSecret(ctx context.Context, cli *cli.Command) (string, error) {
if cli.IsSet("secret") {
return cli.String("secret"), nil
}
if cliCtx.IsSet("secret-stdin") {
if cli.IsSet("secret-stdin") {
buf, err := io.ReadAll(ContextGetStdin(ctx))
if err != nil {
return "", err
}
return string(buf), nil
}
if cliCtx.IsSet("secret-file") {
path := cliCtx.String("secret-file")
if cli.IsSet("secret-file") {
path := cli.String("secret-file")
buf, err := os.ReadFile(path)
if err != nil {
return "", err
}
return string(buf), nil
}
return "", fmt.Errorf("at least one of the --secret, --secret-stdin, --secret-file options is required")
return "", errors.New("at least one of the --secret, --secret-stdin, --secret-file options is required")
}
func validateSecret(secret string) error {
@ -138,18 +139,18 @@ func validateSecret(secret string) error {
return nil
}
func getLabels(cliCtx *cli.Context) (*[]string, error) {
if !cliCtx.Bool("keep-labels") {
lblValue := strings.Split(cliCtx.String("labels"), ",")
func getLabels(cli *cli.Command) (*[]string, error) {
if !cli.Bool("keep-labels") {
lblValue := strings.Split(cli.String("labels"), ",")
return &lblValue, nil
}
if cliCtx.String("labels") != "" {
return nil, fmt.Errorf("--labels and --keep-labels should not be used together")
if cli.String("labels") != "" {
return nil, errors.New("--labels and --keep-labels should not be used together")
}
return nil, nil
}
func RunRegister(ctx context.Context, cliCtx *cli.Context) error {
func RunRegister(ctx context.Context, cli *cli.Command) error {
var cancel context.CancelFunc
if !ContextGetNoInit(ctx) {
ctx, cancel = installSignals(ctx)
@ -161,17 +162,17 @@ func RunRegister(ctx context.Context, cliCtx *cli.Context) error {
}
setting.MustInstalled()
secret, err := readSecret(ctx, cliCtx)
secret, err := readSecret(ctx, cli)
if err != nil {
return err
}
if err := validateSecret(secret); err != nil {
return err
}
scope := cliCtx.String("scope")
name := cliCtx.String("name")
version := cliCtx.String("version")
labels, err := getLabels(cliCtx)
scope := cli.String("scope")
name := cli.String("name")
version := cli.String("version")
labels, err := getLabels(cli)
if err != nil {
return err
}
@ -209,7 +210,7 @@ func RunRegister(ctx context.Context, cliCtx *cli.Context) error {
return nil
}
func RunGenerateSecret(ctx context.Context, cliCtx *cli.Context) error {
func RunGenerateSecret(ctx context.Context, cli *cli.Command) error {
runner := actions_model.ActionRunner{}
if err := runner.GenerateToken(); err != nil {
return err
@ -220,7 +221,7 @@ func RunGenerateSecret(ctx context.Context, cliCtx *cli.Context) error {
return nil
}
func RunGenerateActionsRunnerToken(ctx context.Context, cliCtx *cli.Context) error {
func RunGenerateActionsRunnerToken(ctx context.Context, cli *cli.Command) error {
if !ContextGetNoInit(ctx) {
var cancel context.CancelFunc
ctx, cancel = installSignals(ctx)
@ -229,7 +230,7 @@ func RunGenerateActionsRunnerToken(ctx context.Context, cliCtx *cli.Context) err
setting.MustInstalled()
scope := cliCtx.String("scope")
scope := cli.String("scope")
respText, extra := private.GenerateActionsRunnerToken(ctx, scope)
if extra.HasError() {

View file

@ -4,14 +4,13 @@
package forgejo
import (
"context"
"fmt"
"testing"
"forgejo.org/services/context"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
func TestActions_getLabels(t *testing.T) {
@ -54,21 +53,21 @@ func TestActions_getLabels(t *testing.T) {
},
}
flags := SubcmdActionsRegister(context.Context{}).Flags
flags := SubcmdActionsRegister(t.Context()).Flags
for _, c := range cases {
t.Run(fmt.Sprintf("args: %v", c.args), func(t *testing.T) {
// Create a copy of command to test
var result *resultType
app := cli.NewApp()
app := cli.Command{}
app.Flags = flags
app.Action = func(ctx *cli.Context) error {
app.Action = func(_ context.Context, ctx *cli.Command) error {
labels, err := getLabels(ctx)
result = &resultType{labels, err}
return nil
}
// Run it
_ = app.Run(c.args)
_ = app.Run(t.Context(), c.args)
// Test the results
require.NotNil(t, result)

View file

@ -20,7 +20,7 @@ import (
f3_cmd "code.forgejo.org/f3/gof3/v3/cmd"
f3_logger "code.forgejo.org/f3/gof3/v3/logger"
f3_util "code.forgejo.org/f3/gof3/v3/util"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
func CmdF3(ctx context.Context) *cli.Command {
@ -28,21 +28,21 @@ func CmdF3(ctx context.Context) *cli.Command {
return &cli.Command{
Name: "f3",
Usage: "F3",
Subcommands: []*cli.Command{
Commands: []*cli.Command{
SubcmdF3Mirror(ctx),
},
}
}
func SubcmdF3Mirror(ctx context.Context) *cli.Command {
mirrorCmd := f3_cmd.CreateCmdMirror(ctx)
mirrorCmd := f3_cmd.CreateCmdMirror()
mirrorCmd.Before = prepareWorkPathAndCustomConf(ctx)
f3Action := mirrorCmd.Action
mirrorCmd.Action = func(c *cli.Context) error { return runMirror(ctx, c, f3Action) }
mirrorCmd.Action = func(ctx context.Context, cli *cli.Command) error { return runMirror(ctx, cli, f3Action) }
return mirrorCmd
}
func runMirror(ctx context.Context, c *cli.Context, action cli.ActionFunc) error {
func runMirror(ctx context.Context, c *cli.Command, action cli.ActionFunc) error {
setting.LoadF3Setting()
if !setting.F3.Enabled {
return errors.New("F3 is disabled, it is not ready to be used and is only present for development purposes")
@ -69,7 +69,7 @@ func runMirror(ctx context.Context, c *cli.Context, action cli.ActionFunc) error
}
}
err := action(c)
err := action(ctx, c)
if panicError, ok := err.(f3_util.PanicError); ok {
log.Debug("F3 Stack trace\n%s", panicError.Stack())
}

View file

@ -16,7 +16,7 @@ import (
"forgejo.org/modules/private"
"forgejo.org/modules/setting"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
type key int
@ -34,7 +34,7 @@ func CmdForgejo(ctx context.Context) *cli.Command {
Name: "forgejo-cli",
Usage: "Forgejo CLI",
Flags: []cli.Flag{},
Subcommands: []*cli.Command{
Commands: []*cli.Command{
CmdActions(ctx),
CmdF3(ctx),
},
@ -147,12 +147,12 @@ func handleCliResponseExtra(ctx context.Context, extra private.ResponseExtra) er
return cli.Exit(extra.Error, 1)
}
func prepareWorkPathAndCustomConf(ctx context.Context) func(c *cli.Context) error {
return func(c *cli.Context) error {
func prepareWorkPathAndCustomConf(ctx context.Context) func(ctx context.Context, cli *cli.Command) (context.Context, error) {
return func(ctx context.Context, cli *cli.Command) (context.Context, error) {
if !ContextGetNoInit(ctx) {
var args setting.ArgWorkPathAndCustomConf
// from children to parent, check the global flags
for _, curCtx := range c.Lineage() {
for _, curCtx := range cli.Lineage() {
if curCtx.IsSet("work-path") && args.WorkPath == "" {
args.WorkPath = curCtx.String("work-path")
}
@ -165,6 +165,6 @@ func prepareWorkPathAndCustomConf(ctx context.Context) func(c *cli.Context) erro
}
setting.InitWorkPathAndCommonConfig(os.Getenv, args)
}
return nil
return ctx, nil
}
}

View file

@ -5,56 +5,65 @@
package cmd
import (
"context"
"fmt"
"os"
"forgejo.org/modules/generate"
"github.com/mattn/go-isatty"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var (
// CmdGenerate represents the available generate sub-command.
CmdGenerate = &cli.Command{
// CmdGenerate represents the available generate sub-command.
func cmdGenerate() *cli.Command {
return &cli.Command{
Name: "generate",
Usage: "Generate Gitea's secrets/keys/tokens",
Subcommands: []*cli.Command{
subcmdSecret,
Commands: []*cli.Command{
subcmdSecret(),
},
}
}
subcmdSecret = &cli.Command{
func subcmdSecret() *cli.Command {
return &cli.Command{
Name: "secret",
Usage: "Generate a secret token",
Subcommands: []*cli.Command{
microcmdGenerateInternalToken,
microcmdGenerateLfsJwtSecret,
microcmdGenerateSecretKey,
Commands: []*cli.Command{
microcmdGenerateInternalToken(),
microcmdGenerateLfsJwtSecret(),
microcmdGenerateSecretKey(),
},
}
}
microcmdGenerateInternalToken = &cli.Command{
func microcmdGenerateInternalToken() *cli.Command {
return &cli.Command{
Name: "INTERNAL_TOKEN",
Usage: "Generate a new INTERNAL_TOKEN",
Action: runGenerateInternalToken,
}
}
microcmdGenerateLfsJwtSecret = &cli.Command{
func microcmdGenerateLfsJwtSecret() *cli.Command {
return &cli.Command{
Name: "JWT_SECRET",
Aliases: []string{"LFS_JWT_SECRET"},
Usage: "Generate a new JWT_SECRET",
Action: runGenerateLfsJwtSecret,
}
}
microcmdGenerateSecretKey = &cli.Command{
func microcmdGenerateSecretKey() *cli.Command {
return &cli.Command{
Name: "SECRET_KEY",
Usage: "Generate a new SECRET_KEY",
Action: runGenerateSecretKey,
}
)
}
func runGenerateInternalToken(c *cli.Context) error {
func runGenerateInternalToken(ctx context.Context, c *cli.Command) error {
internalToken, err := generate.NewInternalToken()
if err != nil {
return err
@ -63,25 +72,25 @@ func runGenerateInternalToken(c *cli.Context) error {
fmt.Printf("%s", internalToken)
if isatty.IsTerminal(os.Stdout.Fd()) {
fmt.Printf("\n")
fmt.Println()
}
return nil
}
func runGenerateLfsJwtSecret(c *cli.Context) error {
func runGenerateLfsJwtSecret(ctx context.Context, c *cli.Command) error {
_, jwtSecretBase64 := generate.NewJwtSecret()
fmt.Printf("%s", jwtSecretBase64)
if isatty.IsTerminal(os.Stdout.Fd()) {
fmt.Printf("\n")
fmt.Print("\n")
}
return nil
}
func runGenerateSecretKey(c *cli.Context) error {
func runGenerateSecretKey(ctx context.Context, c *cli.Command) error {
secretKey, err := generate.NewSecretKey()
if err != nil {
return err
@ -90,7 +99,7 @@ func runGenerateSecretKey(c *cli.Context) error {
fmt.Printf("%s", secretKey)
if isatty.IsTerminal(os.Stdout.Fd()) {
fmt.Printf("\n")
fmt.Print("\n")
}
return nil

View file

@ -21,29 +21,31 @@ import (
repo_module "forgejo.org/modules/repository"
"forgejo.org/modules/setting"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
const (
hookBatchSize = 30
)
var (
// CmdHook represents the available hooks sub-command.
CmdHook = &cli.Command{
// CmdHook represents the available hooks sub-command.
func cmdHook() *cli.Command {
return &cli.Command{
Name: "hook",
Usage: "(internal) Should only be called by Git",
Description: "Delegate commands to corresponding Git hooks",
Before: PrepareConsoleLoggerLevel(log.FATAL),
Subcommands: []*cli.Command{
subcmdHookPreReceive,
subcmdHookUpdate,
subcmdHookPostReceive,
subcmdHookProcReceive,
Commands: []*cli.Command{
subcmdHookPreReceive(),
subcmdHookUpdate(),
subcmdHookPostReceive(),
subcmdHookProcReceive(),
},
}
}
subcmdHookPreReceive = &cli.Command{
func subcmdHookPreReceive() *cli.Command {
return &cli.Command{
Name: "pre-receive",
Usage: "Delegate pre-receive Git hook",
Description: "This command should only be called by Git",
@ -54,7 +56,10 @@ var (
},
},
}
subcmdHookUpdate = &cli.Command{
}
func subcmdHookUpdate() *cli.Command {
return &cli.Command{
Name: "update",
Usage: "Delegate update Git hook",
Description: "This command should only be called by Git",
@ -65,7 +70,10 @@ var (
},
},
}
subcmdHookPostReceive = &cli.Command{
}
func subcmdHookPostReceive() *cli.Command {
return &cli.Command{
Name: "post-receive",
Usage: "Delegate post-receive Git hook",
Description: "This command should only be called by Git",
@ -76,8 +84,11 @@ var (
},
},
}
// Note: new hook since git 2.29
subcmdHookProcReceive = &cli.Command{
}
// Note: new hook since git 2.29
func subcmdHookProcReceive() *cli.Command {
return &cli.Command{
Name: "proc-receive",
Usage: "Delegate proc-receive Git hook",
Description: "This command should only be called by Git",
@ -88,7 +99,7 @@ var (
},
},
}
)
}
type delayWriter struct {
internal io.Writer
@ -161,11 +172,11 @@ func (n *nilWriter) WriteString(s string) (int, error) {
return len(s), nil
}
func runHookPreReceive(c *cli.Context) error {
func runHookPreReceive(ctx context.Context, c *cli.Command) error {
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
return nil
}
ctx, cancel := installSignals()
ctx, cancel := installSignals(ctx)
defer cancel()
setup(ctx, c.Bool("debug"), true)
@ -247,7 +258,7 @@ Forgejo or set your environment appropriately.`, "")
newCommitIDs[count] = newCommitID
refFullNames[count] = refFullName
count++
fmt.Fprintf(out, "*")
fmt.Fprint(out, "*")
if count >= hookBatchSize {
fmt.Fprintf(out, " Checking %d references\n", count)
@ -263,10 +274,10 @@ Forgejo or set your environment appropriately.`, "")
lastline = 0
}
} else {
fmt.Fprintf(out, ".")
fmt.Fprint(out, ".")
}
if lastline >= hookBatchSize {
fmt.Fprintf(out, "\n")
fmt.Fprint(out, "\n")
lastline = 0
}
}
@ -283,7 +294,7 @@ Forgejo or set your environment appropriately.`, "")
return fail(ctx, extra.UserMsg, "HookPreReceive(last) failed: %v", extra.Error)
}
} else if lastline > 0 {
fmt.Fprintf(out, "\n")
fmt.Fprint(out, "\n")
}
fmt.Fprintf(out, "Checked %d references in total\n", total)
@ -291,13 +302,13 @@ Forgejo or set your environment appropriately.`, "")
}
// runHookUpdate process the update hook: https://git-scm.com/docs/githooks#update
func runHookUpdate(c *cli.Context) error {
func runHookUpdate(ctx context.Context, c *cli.Command) error {
// Now if we're an internal don't do anything else
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
return nil
}
ctx, cancel := installSignals()
ctx, cancel := installSignals(ctx)
defer cancel()
if c.NArg() != 3 {
@ -323,8 +334,8 @@ func runHookUpdate(c *cli.Context) error {
return fail(ctx, fmt.Sprintf("The modification of %s is skipped as it's an internal reference.", refFullName), "")
}
func runHookPostReceive(c *cli.Context) error {
ctx, cancel := installSignals()
func runHookPostReceive(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
setup(ctx, c.Bool("debug"), true)
@ -399,7 +410,7 @@ Forgejo or set your environment appropriately.`, "")
continue
}
fmt.Fprintf(out, ".")
fmt.Fprint(out, ".")
oldCommitIDs[count] = string(fields[0])
newCommitIDs[count] = string(fields[1])
refFullNames[count] = git.RefName(fields[2])
@ -487,8 +498,8 @@ func hookPrintResults(results []private.HookPostReceiveBranchResult) {
}
}
func runHookProcReceive(c *cli.Context) error {
ctx, cancel := installSignals()
func runHookProcReceive(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
setup(ctx, c.Bool("debug"), true)

View file

@ -19,7 +19,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// Capture what's being written into a standard file descriptor.
@ -134,14 +134,14 @@ func TestDelayWriter(t *testing.T) {
defer ts.Close()
defer test.MockVariableValue(&setting.LocalURL, ts.URL+"/")()
app := cli.NewApp()
app.Commands = []*cli.Command{subcmdHookPreReceive}
app := cli.Command{}
app.Commands = []*cli.Command{subcmdHookPreReceive()}
t.Run("Should delay", func(t *testing.T) {
defer test.MockVariableValue(&setting.Git.VerbosePushDelay, time.Millisecond*500)()
finish := captureOutput(t, os.Stdout)
err = app.Run([]string{"./forgejo", "pre-receive"})
err = app.Run(t.Context(), []string{"./forgejo", "pre-receive"})
require.NoError(t, err)
out := finish()
@ -153,7 +153,7 @@ func TestDelayWriter(t *testing.T) {
defer test.MockVariableValue(&setting.Git.VerbosePushDelay, time.Second*5)()
finish := captureOutput(t, os.Stdout)
err = app.Run([]string{"./forgejo", "pre-receive"})
err = app.Run(t.Context(), []string{"./forgejo", "pre-receive"})
require.NoError(t, err)
out := finish()
@ -163,15 +163,15 @@ func TestDelayWriter(t *testing.T) {
}
func TestRunHookUpdate(t *testing.T) {
app := cli.NewApp()
app.Commands = []*cli.Command{subcmdHookUpdate}
app := cli.Command{}
app.Commands = []*cli.Command{subcmdHookUpdate()}
t.Run("Removal of internal reference", func(t *testing.T) {
defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
defer test.MockVariableValue(&setting.IsProd, false)()
finish := captureOutput(t, os.Stderr)
err := app.Run([]string{"./forgejo", "update", "refs/pull/1/head", "0a51ae26bc73c47e2f754560c40904cf14ed51a9", "0000000000000000000000000000000000000000"})
err := app.Run(t.Context(), []string{"./forgejo", "update", "refs/pull/1/head", "0a51ae26bc73c47e2f754560c40904cf14ed51a9", "0000000000000000000000000000000000000000"})
out := finish()
require.Error(t, err)
@ -183,7 +183,7 @@ func TestRunHookUpdate(t *testing.T) {
defer test.MockVariableValue(&setting.IsProd, false)()
finish := captureOutput(t, os.Stderr)
err := app.Run([]string{"./forgejo", "update", "refs/pull/1/head", "0a51ae26bc73c47e2f754560c40904cf14ed51a9", "0000000000000000000000000000000000000001"})
err := app.Run(t.Context(), []string{"./forgejo", "update", "refs/pull/1/head", "0a51ae26bc73c47e2f754560c40904cf14ed51a9", "0000000000000000000000000000000000000001"})
out := finish()
require.Error(t, err)
@ -191,12 +191,12 @@ func TestRunHookUpdate(t *testing.T) {
})
t.Run("Removal of branch", func(t *testing.T) {
err := app.Run([]string{"./forgejo", "update", "refs/head/main", "0a51ae26bc73c47e2f754560c40904cf14ed51a9", "0000000000000000000000000000000000000000"})
err := app.Run(t.Context(), []string{"./forgejo", "update", "refs/head/main", "0a51ae26bc73c47e2f754560c40904cf14ed51a9", "0000000000000000000000000000000000000000"})
require.NoError(t, err)
})
t.Run("Not enough arguments", func(t *testing.T) {
err := app.Run([]string{"./forgejo", "update"})
err := app.Run(t.Context(), []string{"./forgejo", "update"})
require.NoError(t, err)
})
}

View file

@ -4,6 +4,7 @@
package cmd
import (
"context"
"errors"
"fmt"
"strings"
@ -11,45 +12,47 @@ import (
"forgejo.org/modules/log"
"forgejo.org/modules/private"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// CmdKeys represents the available keys sub-command
var CmdKeys = &cli.Command{
Name: "keys",
Usage: "(internal) Should only be called by SSH server",
Description: "Queries the Forgejo 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",
Aliases: []string{"e"},
Value: "git",
Usage: "Expected user for whom provide key commands",
func cmdKeys() *cli.Command {
return &cli.Command{
Name: "keys",
Usage: "(internal) Should only be called by SSH server",
Description: "Queries the Forgejo 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",
Aliases: []string{"e"},
Value: "git",
Usage: "Expected user for whom provide key commands",
},
&cli.StringFlag{
Name: "username",
Aliases: []string{"u"},
Value: "",
Usage: "Username trying to log in by SSH",
},
&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",
Aliases: []string{"k"},
Value: "",
Usage: "Base64 encoded content of the SSH key provided to the SSH Server (requires type to be provided too)",
},
},
&cli.StringFlag{
Name: "username",
Aliases: []string{"u"},
Value: "",
Usage: "Username trying to log in by SSH",
},
&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",
Aliases: []string{"k"},
Value: "",
Usage: "Base64 encoded content of the SSH key provided to the SSH Server (requires type to be provided too)",
},
},
}
}
func runKeys(c *cli.Context) error {
func runKeys(ctx context.Context, c *cli.Command) error {
if !c.IsSet("username") {
return errors.New("No username provided")
}
@ -68,7 +71,7 @@ func runKeys(c *cli.Context) error {
return errors.New("No key type and content provided")
}
ctx, cancel := installSignals()
ctx, cancel := installSignals(ctx)
defer cancel()
setup(ctx, c.Bool("debug"), true)
@ -78,6 +81,6 @@ func runKeys(c *cli.Context) error {
if extra.Error != nil {
return extra.Error
}
_, _ = fmt.Fprintln(c.App.Writer, strings.TrimSpace(authorizedString.Text))
_, _ = fmt.Fprintln(c.Root().Writer, strings.TrimSpace(authorizedString.Text))
return nil
}

View file

@ -4,16 +4,17 @@
package cmd
import (
"context"
"fmt"
"forgejo.org/modules/private"
"forgejo.org/modules/setting"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
func runSendMail(c *cli.Context) error {
ctx, cancel := installSignals()
func runSendMail(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
setting.MustInstalled()

View file

@ -14,7 +14,7 @@ import (
"forgejo.org/modules/log"
"forgejo.org/modules/setting"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// cmdHelp is our own help subcommand with more information
@ -25,18 +25,18 @@ func cmdHelp() *cli.Command {
Aliases: []string{"h"},
Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]",
Action: func(c *cli.Context) (err error) {
lineage := c.Lineage() // The order is from child to parent: help, doctor, Gitea, {Command:nil}
Action: func(ctx context.Context, c *cli.Command) (err error) {
lineage := c.Lineage() // The order is from child to parent: help, doctor, Forgejo
targetCmdIdx := 0
if c.Command.Name == "help" {
if c.Name == "help" {
targetCmdIdx = 1
}
if lineage[targetCmdIdx+1].Command != nil {
err = cli.ShowCommandHelp(lineage[targetCmdIdx+1], lineage[targetCmdIdx].Command.Name)
if targetCmdIdx+1 < len(lineage) {
err = cli.ShowCommandHelp(ctx, lineage[targetCmdIdx+1], lineage[targetCmdIdx].Name)
} else {
err = cli.ShowAppHelp(c)
}
_, _ = fmt.Fprintf(c.App.Writer, `
_, _ = fmt.Fprintf(c.Root().Writer, `
DEFAULT CONFIGURATION:
AppPath: %s
WorkPath: %s
@ -77,25 +77,25 @@ func appGlobalFlags() []cli.Flag {
}
}
func prepareSubcommandWithConfig(command *cli.Command, globalFlags []cli.Flag) {
command.Flags = append(append([]cli.Flag{}, globalFlags...), command.Flags...)
func prepareSubcommandWithConfig(command *cli.Command, globalFlags func() []cli.Flag) {
command.Flags = append(globalFlags(), command.Flags...)
command.Action = prepareWorkPathAndCustomConf(command.Action)
command.HideHelp = true
if command.Name != "help" {
command.Subcommands = append(command.Subcommands, cmdHelp())
command.Commands = append(command.Commands, cmdHelp())
}
for i := range command.Subcommands {
prepareSubcommandWithConfig(command.Subcommands[i], globalFlags)
for i := range command.Commands {
prepareSubcommandWithConfig(command.Commands[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 {
func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(_ context.Context, _ *cli.Command) error {
return func(ctx context.Context, cli *cli.Command) error {
var args setting.ArgWorkPathAndCustomConf
// from children to parent, check the global flags
for _, curCtx := range ctx.Lineage() {
for _, curCtx := range cli.Lineage() {
if curCtx.IsSet("work-path") && args.WorkPath == "" {
args.WorkPath = curCtx.String("work-path")
}
@ -107,15 +107,15 @@ func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context)
}
}
setting.InitWorkPathAndCommonConfig(os.Getenv, args)
if ctx.Bool("help") || action == nil {
if cli.Bool("help") || action == nil {
// the default behavior of "urfave/cli": "nil action" means "show help"
return cmdHelp().Action(ctx)
return cmdHelp().Action(ctx, cli)
}
return action(ctx)
return action(ctx, cli)
}
}
func NewMainApp(version, versionExtra string) *cli.App {
func NewMainApp(version, versionExtra string) *cli.Command {
path, err := os.Executable()
if err != nil {
panic(err)
@ -124,7 +124,7 @@ func NewMainApp(version, versionExtra string) *cli.App {
subCmdsStandalone := make([]*cli.Command, 0, 10)
subCmdWithConfig := make([]*cli.Command, 0, 10)
globalFlags := make([]cli.Flag, 0, 10)
globalFlags := func() []cli.Flag { return []cli.Flag{} }
//
// If the executable is forgejo-cli, provide a Forgejo specific CLI
@ -133,14 +133,16 @@ func NewMainApp(version, versionExtra string) *cli.App {
if executable == "forgejo-cli" {
subCmdsStandalone = append(subCmdsStandalone, forgejo.CmdActions(context.Background()))
subCmdWithConfig = append(subCmdWithConfig, forgejo.CmdF3(context.Background()))
globalFlags = append(globalFlags, []cli.Flag{
&cli.BoolFlag{
Name: "quiet",
},
&cli.BoolFlag{
Name: "verbose",
},
}...)
globalFlags = func() []cli.Flag {
return []cli.Flag{
&cli.BoolFlag{
Name: "quiet",
},
&cli.BoolFlag{
Name: "verbose",
},
}
}
} else {
//
// Otherwise provide a Gitea compatible CLI which includes Forgejo
@ -149,55 +151,54 @@ func NewMainApp(version, versionExtra string) *cli.App {
// binary and rename it to forgejo if they want.
//
subCmdsStandalone = append(subCmdsStandalone, forgejo.CmdForgejo(context.Background()))
subCmdWithConfig = append(subCmdWithConfig, CmdActions)
subCmdWithConfig = append(subCmdWithConfig, cmdActions())
}
return innerNewMainApp(version, versionExtra, subCmdsStandalone, subCmdWithConfig, globalFlags)
}
func innerNewMainApp(version, versionExtra string, subCmdsStandaloneArgs, subCmdWithConfigArgs []*cli.Command, globalFlagsArgs []cli.Flag) *cli.App {
app := cli.NewApp()
app.HelpName = "forgejo"
app.Name = "Forgejo"
func innerNewMainApp(version, versionExtra string, subCmdsStandaloneArgs, subCmdWithConfigArgs []*cli.Command, globalFlagsArgs func() []cli.Flag) *cli.Command {
app := &cli.Command{}
app.Name = "forgejo"
app.Usage = "Beyond coding. We forge."
app.Description = `By default, forgejo will start serving using the web-server with no argument, which can alternatively be run by running the subcommand "web".`
app.Version = version + versionExtra
app.EnableBashCompletion = true
app.EnableShellCompletion = true
// these sub-commands need to use config file
subCmdWithConfig := []*cli.Command{
cmdHelp(), // the "help" sub-command was used to show the more information for "work path" and "custom config"
CmdWeb,
CmdServ,
CmdHook,
CmdKeys,
CmdDump,
CmdAdmin,
CmdMigrate,
CmdDoctor,
CmdManager,
CmdEmbedded,
CmdMigrateStorage,
CmdDumpRepository,
CmdRestoreRepository,
cmdWeb(),
cmdServ(),
cmdHook(),
cmdKeys(),
cmdDump(),
cmdAdmin(),
cmdMigrate(),
cmdDoctor(),
cmdManager(),
cmdEmbedded(),
cmdMigrateStorage(),
cmdDumpRepository(),
cmdRestoreRepository(),
}
subCmdWithConfig = append(subCmdWithConfig, subCmdWithConfigArgs...)
// 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,
cmdCert(),
cmdGenerate(),
}
subCmdStandalone = append(subCmdStandalone, subCmdsStandaloneArgs...)
app.DefaultCommand = CmdWeb.Name
app.DefaultCommand = cmdWeb().Name
globalFlags := appGlobalFlags()
globalFlags = append(globalFlags, globalFlagsArgs...)
globalFlags := func() []cli.Flag {
return append(appGlobalFlags(), globalFlagsArgs()...)
}
app.Flags = append(app.Flags, cli.VersionFlag)
app.Flags = append(app.Flags, globalFlags...)
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 {
@ -210,8 +211,8 @@ func innerNewMainApp(version, versionExtra string, subCmdsStandaloneArgs, subCmd
return app
}
func RunMainApp(app *cli.App, args ...string) error {
err := app.Run(args)
func RunMainApp(app *cli.Command, args ...string) error {
err := app.Run(context.Background(), args)
if err == nil {
return nil
}
@ -220,7 +221,7 @@ func RunMainApp(app *cli.App, args ...string) error {
cli.OsExiter(1)
return err
}
_, _ = fmt.Fprintf(app.ErrWriter, "Command error: %v\n", err)
_, _ = fmt.Fprintf(app.Root().ErrWriter, "Command error: %v\n", err)
cli.OsExiter(1)
return err
}

View file

@ -4,6 +4,8 @@
package cmd
import (
"context"
"errors"
"fmt"
"io"
"path/filepath"
@ -16,7 +18,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
func TestMain(m *testing.M) {
@ -27,10 +29,10 @@ func makePathOutput(workPath, customPath, customConf string) string {
return fmt.Sprintf("WorkPath=%s\nCustomPath=%s\nCustomConf=%s", workPath, customPath, customConf)
}
func newTestApp(testCmdAction func(ctx *cli.Context) error) *cli.App {
func newTestApp(testCmdAction func(_ context.Context, ctx *cli.Command) error) *cli.Command {
app := NewMainApp("version", "version-extra")
testCmd := &cli.Command{Name: "test-cmd", Action: testCmdAction}
prepareSubcommandWithConfig(testCmd, appGlobalFlags())
prepareSubcommandWithConfig(testCmd, appGlobalFlags)
app.Commands = append(app.Commands, testCmd)
app.DefaultCommand = testCmd.Name
return app
@ -42,7 +44,7 @@ type runResult struct {
ExitCode int
}
func runTestApp(app *cli.App, args ...string) (runResult, error) {
func runTestApp(app *cli.Command, args ...string) (runResult, error) {
outBuf := new(strings.Builder)
errBuf := new(strings.Builder)
app.Writer = outBuf
@ -65,7 +67,6 @@ func TestCliCmd(t *testing.T) {
defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini")
cli.CommandHelpTemplate = "(command help template)"
cli.AppHelpTemplate = "(app help template)"
cli.SubcommandHelpTemplate = "(subcommand help template)"
cases := []struct {
@ -109,12 +110,17 @@ func TestCliCmd(t *testing.T) {
},
}
app := newTestApp(func(ctx *cli.Context) error {
_, _ = fmt.Fprint(ctx.App.Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
return nil
})
for _, c := range cases {
t.Run(c.cmd, func(t *testing.T) {
defer test.MockProtect(&setting.AppWorkPath)()
defer test.MockProtect(&setting.CustomPath)()
defer test.MockProtect(&setting.CustomConf)()
app := newTestApp(func(_ context.Context, ctx *cli.Command) error {
_, _ = fmt.Fprint(ctx.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
return nil
})
for k, v := range c.env {
t.Setenv(k, v)
}
@ -122,34 +128,34 @@ func TestCliCmd(t *testing.T) {
r, err := runTestApp(app, args...)
require.NoError(t, err, c.cmd)
assert.NotEmpty(t, c.exp, c.cmd)
assert.Contains(t, r.Stdout, c.exp, c.cmd)
assert.Contains(t, r.Stdout, c.exp, c.cmd+"\n"+r.Stdout)
})
}
}
func TestCliCmdError(t *testing.T) {
app := newTestApp(func(ctx *cli.Context) error { return fmt.Errorf("normal error") })
app := newTestApp(func(_ context.Context, ctx *cli.Command) error { return errors.New("normal error") })
r, err := runTestApp(app, "./gitea", "test-cmd")
require.Error(t, err)
assert.Equal(t, 1, r.ExitCode)
assert.Empty(t, r.Stdout)
assert.Equal(t, "Command error: normal error\n", r.Stderr)
app = newTestApp(func(ctx *cli.Context) error { return cli.Exit("exit error", 2) })
app = newTestApp(func(_ context.Context, ctx *cli.Command) error { return cli.Exit("exit error", 2) })
r, err = runTestApp(app, "./gitea", "test-cmd")
require.Error(t, err)
assert.Equal(t, 2, r.ExitCode)
assert.Empty(t, r.Stdout)
assert.Equal(t, "exit error\n", r.Stderr)
app = newTestApp(func(ctx *cli.Context) error { return nil })
app = newTestApp(func(_ context.Context, ctx *cli.Command) error { return nil })
r, err = runTestApp(app, "./gitea", "test-cmd", "--no-such")
require.Error(t, err)
assert.Equal(t, 1, r.ExitCode)
assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stdout)
assert.Empty(t, r.Stderr) // the cli package's strange behavior, the error message is not in stderr ....
assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stderr)
assert.Empty(t, r.Stdout)
app = newTestApp(func(ctx *cli.Context) error { return nil })
app = newTestApp(func(_ context.Context, ctx *cli.Command) error { return nil })
r, err = runTestApp(app, "./gitea", "test-cmd")
require.NoError(t, err)
assert.Equal(t, -1, r.ExitCode) // the cli.OsExiter is not called

View file

@ -4,30 +4,34 @@
package cmd
import (
"context"
"os"
"time"
"forgejo.org/modules/private"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var (
// CmdManager represents the manager command
CmdManager = &cli.Command{
// CmdManager represents the manager command
func cmdManager() *cli.Command {
return &cli.Command{
Name: "manager",
Usage: "Manage the running forgejo process",
Description: "This is a command for managing the running forgejo process",
Subcommands: []*cli.Command{
subcmdShutdown,
subcmdRestart,
subcmdReloadTemplates,
subcmdFlushQueues,
subcmdLogging,
subCmdProcesses,
Commands: []*cli.Command{
subcmdShutdown(),
subcmdRestart(),
subcmdReloadTemplates(),
subcmdFlushQueues(),
subcmdLogging(),
subCmdProcesses(),
},
}
subcmdShutdown = &cli.Command{
}
func subcmdShutdown() *cli.Command {
return &cli.Command{
Name: "shutdown",
Usage: "Gracefully shutdown the running process",
Flags: []cli.Flag{
@ -37,7 +41,10 @@ var (
},
Action: runShutdown,
}
subcmdRestart = &cli.Command{
}
func subcmdRestart() *cli.Command {
return &cli.Command{
Name: "restart",
Usage: "Gracefully restart the running process - (not implemented for windows servers)",
Flags: []cli.Flag{
@ -47,7 +54,10 @@ var (
},
Action: runRestart,
}
subcmdReloadTemplates = &cli.Command{
}
func subcmdReloadTemplates() *cli.Command {
return &cli.Command{
Name: "reload-templates",
Usage: "Reload template files in the running process",
Flags: []cli.Flag{
@ -57,7 +67,10 @@ var (
},
Action: runReloadTemplates,
}
subcmdFlushQueues = &cli.Command{
}
func subcmdFlushQueues() *cli.Command {
return &cli.Command{
Name: "flush-queues",
Usage: "Flush queues in the running process",
Action: runFlushQueues,
@ -76,7 +89,10 @@ var (
},
},
}
subCmdProcesses = &cli.Command{
}
func subCmdProcesses() *cli.Command {
return &cli.Command{
Name: "processes",
Usage: "Display running processes within the current process",
Action: runProcesses,
@ -106,10 +122,10 @@ var (
},
},
}
)
}
func runShutdown(c *cli.Context) error {
ctx, cancel := installSignals()
func runShutdown(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
setup(ctx, c.Bool("debug"), false)
@ -117,8 +133,8 @@ func runShutdown(c *cli.Context) error {
return handleCliResponseExtra(extra)
}
func runRestart(c *cli.Context) error {
ctx, cancel := installSignals()
func runRestart(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
setup(ctx, c.Bool("debug"), false)
@ -126,8 +142,8 @@ func runRestart(c *cli.Context) error {
return handleCliResponseExtra(extra)
}
func runReloadTemplates(c *cli.Context) error {
ctx, cancel := installSignals()
func runReloadTemplates(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
setup(ctx, c.Bool("debug"), false)
@ -135,8 +151,8 @@ func runReloadTemplates(c *cli.Context) error {
return handleCliResponseExtra(extra)
}
func runFlushQueues(c *cli.Context) error {
ctx, cancel := installSignals()
func runFlushQueues(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
setup(ctx, c.Bool("debug"), false)
@ -144,8 +160,8 @@ func runFlushQueues(c *cli.Context) error {
return handleCliResponseExtra(extra)
}
func runProcesses(c *cli.Context) error {
ctx, cancel := installSignals()
func runProcesses(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
setup(ctx, c.Bool("debug"), false)

View file

@ -4,6 +4,7 @@
package cmd
import (
"context"
"errors"
"fmt"
"os"
@ -11,11 +12,11 @@ import (
"forgejo.org/modules/log"
"forgejo.org/modules/private"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var (
defaultLoggingFlags = []cli.Flag{
func defaultLoggingFlags() []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "logger",
Usage: `Logger name - will default to "default"`,
@ -56,11 +57,13 @@ var (
Name: "debug",
},
}
}
subcmdLogging = &cli.Command{
func subcmdLogging() *cli.Command {
return &cli.Command{
Name: "logging",
Usage: "Adjust logging commands",
Subcommands: []*cli.Command{
Commands: []*cli.Command{
{
Name: "pause",
Usage: "Pause logging (Forgejo will buffer logs up to a certain point and will drop them after that point)",
@ -104,11 +107,11 @@ var (
}, {
Name: "add",
Usage: "Add a logger",
Subcommands: []*cli.Command{
Commands: []*cli.Command{
{
Name: "file",
Usage: "Add a file logger",
Flags: append(defaultLoggingFlags, []cli.Flag{
Flags: append(defaultLoggingFlags(), []cli.Flag{
&cli.StringFlag{
Name: "filename",
Aliases: []string{"f"},
@ -152,7 +155,7 @@ var (
}, {
Name: "conn",
Usage: "Add a net conn logger",
Flags: append(defaultLoggingFlags, []cli.Flag{
Flags: append(defaultLoggingFlags(), []cli.Flag{
&cli.BoolFlag{
Name: "reconnect-on-message",
Aliases: []string{"R"},
@ -193,10 +196,10 @@ var (
},
},
}
)
}
func runRemoveLogger(c *cli.Context) error {
ctx, cancel := installSignals()
func runRemoveLogger(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
setup(ctx, c.Bool("debug"), false)
@ -210,8 +213,8 @@ func runRemoveLogger(c *cli.Context) error {
return handleCliResponseExtra(extra)
}
func runAddConnLogger(c *cli.Context) error {
ctx, cancel := installSignals()
func runAddConnLogger(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
setup(ctx, c.Bool("debug"), false)
@ -237,11 +240,11 @@ func runAddConnLogger(c *cli.Context) error {
if c.IsSet("reconnect-on-message") {
vals["reconnectOnMsg"] = c.Bool("reconnect-on-message")
}
return commonAddLogger(c, mode, vals)
return commonAddLogger(ctx, c, mode, vals)
}
func runAddFileLogger(c *cli.Context) error {
ctx, cancel := installSignals()
func runAddFileLogger(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
setup(ctx, c.Bool("debug"), false)
@ -270,10 +273,10 @@ func runAddFileLogger(c *cli.Context) error {
if c.IsSet("compression-level") {
vals["compressionLevel"] = c.Int("compression-level")
}
return commonAddLogger(c, mode, vals)
return commonAddLogger(ctx, c, mode, vals)
}
func commonAddLogger(c *cli.Context, mode string, vals map[string]any) error {
func commonAddLogger(ctx context.Context, c *cli.Command, mode string, vals map[string]any) error {
if len(c.String("level")) > 0 {
vals["level"] = log.LevelFromString(c.String("level")).String()
}
@ -300,15 +303,15 @@ func commonAddLogger(c *cli.Context, mode string, vals map[string]any) error {
if c.IsSet("writer") {
writer = c.String("writer")
}
ctx, cancel := installSignals()
ctx, cancel := installSignals(ctx)
defer cancel()
extra := private.AddLogger(ctx, logger, writer, mode, vals)
return handleCliResponseExtra(extra)
}
func runPauseLogging(c *cli.Context) error {
ctx, cancel := installSignals()
func runPauseLogging(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
setup(ctx, c.Bool("debug"), false)
@ -317,8 +320,8 @@ func runPauseLogging(c *cli.Context) error {
return nil
}
func runResumeLogging(c *cli.Context) error {
ctx, cancel := installSignals()
func runResumeLogging(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
setup(ctx, c.Bool("debug"), false)
@ -327,8 +330,8 @@ func runResumeLogging(c *cli.Context) error {
return nil
}
func runReleaseReopenLogging(c *cli.Context) error {
ctx, cancel := installSignals()
func runReleaseReopenLogging(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
setup(ctx, c.Bool("debug"), false)
@ -337,8 +340,8 @@ func runReleaseReopenLogging(c *cli.Context) error {
return nil
}
func runSetLogSQL(c *cli.Context) error {
ctx, cancel := installSignals()
func runSetLogSQL(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
setup(ctx, c.Bool("debug"), false)

View file

@ -11,19 +11,21 @@ import (
"forgejo.org/modules/log"
"forgejo.org/modules/setting"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// CmdMigrate represents the available migrate sub-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 'forgejo admin user create' before starting the server.",
Action: runMigrate,
func cmdMigrate() *cli.Command {
return &cli.Command{
Name: "migrate",
Usage: "Migrate the database",
Description: "This is a command for migrating the database, so that you can run 'forgejo admin user create' before starting the server.",
Action: runMigrate,
}
}
func runMigrate(ctx *cli.Context) error {
stdCtx, cancel := installSignals()
func runMigrate(stdCtx context.Context, ctx *cli.Command) error {
stdCtx, cancel := installSignals(stdCtx)
defer cancel()
if err := initDB(stdCtx); err != nil {

View file

@ -22,79 +22,81 @@ import (
"forgejo.org/modules/setting"
"forgejo.org/modules/storage"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
"xorm.io/xorm"
)
// CmdMigrateStorage represents the available migrate storage sub-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",
Aliases: []string{"t"},
Value: "",
Usage: "Type of stored files to copy. Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages', 'actions-log', 'actions-artifacts'",
func cmdMigrateStorage() *cli.Command {
return &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",
Aliases: []string{"t"},
Value: "",
Usage: "Type of stored files to copy. Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages', 'actions-log', 'actions-artifacts'",
},
&cli.StringFlag{
Name: "storage",
Aliases: []string{"s"},
Value: "",
Usage: "New storage type: local (default) or minio",
},
&cli.StringFlag{
Name: "path",
Aliases: []string{"p"},
Value: "",
Usage: "New storage placement if store is local (leave blank for default)",
},
&cli.StringFlag{
Name: "minio-endpoint",
Value: "",
Usage: "Minio storage endpoint",
},
&cli.StringFlag{
Name: "minio-access-key-id",
Value: "",
Usage: "Minio storage accessKeyID",
},
&cli.StringFlag{
Name: "minio-secret-access-key",
Value: "",
Usage: "Minio storage secretAccessKey",
},
&cli.StringFlag{
Name: "minio-bucket",
Value: "",
Usage: "Minio storage bucket",
},
&cli.StringFlag{
Name: "minio-location",
Value: "",
Usage: "Minio storage location to create bucket",
},
&cli.StringFlag{
Name: "minio-base-path",
Value: "",
Usage: "Minio storage base path on the bucket",
},
&cli.BoolFlag{
Name: "minio-use-ssl",
Usage: "Enable SSL for minio",
},
&cli.BoolFlag{
Name: "minio-insecure-skip-verify",
Usage: "Skip SSL verification",
},
&cli.StringFlag{
Name: "minio-checksum-algorithm",
Value: "",
Usage: "Minio checksum algorithm (default/md5)",
},
},
&cli.StringFlag{
Name: "storage",
Aliases: []string{"s"},
Value: "",
Usage: "New storage type: local (default) or minio",
},
&cli.StringFlag{
Name: "path",
Aliases: []string{"p"},
Value: "",
Usage: "New storage placement if store is local (leave blank for default)",
},
&cli.StringFlag{
Name: "minio-endpoint",
Value: "",
Usage: "Minio storage endpoint",
},
&cli.StringFlag{
Name: "minio-access-key-id",
Value: "",
Usage: "Minio storage accessKeyID",
},
&cli.StringFlag{
Name: "minio-secret-access-key",
Value: "",
Usage: "Minio storage secretAccessKey",
},
&cli.StringFlag{
Name: "minio-bucket",
Value: "",
Usage: "Minio storage bucket",
},
&cli.StringFlag{
Name: "minio-location",
Value: "",
Usage: "Minio storage location to create bucket",
},
&cli.StringFlag{
Name: "minio-base-path",
Value: "",
Usage: "Minio storage base path on the bucket",
},
&cli.BoolFlag{
Name: "minio-use-ssl",
Usage: "Enable SSL for minio",
},
&cli.BoolFlag{
Name: "minio-insecure-skip-verify",
Usage: "Skip SSL verification",
},
&cli.StringFlag{
Name: "minio-checksum-algorithm",
Value: "",
Usage: "Minio checksum algorithm (default/md5)",
},
},
}
}
func migrateAttachments(ctx context.Context, dstStorage storage.ObjectStorage) error {
@ -182,8 +184,8 @@ func migrateActionsArtifacts(ctx context.Context, dstStorage storage.ObjectStora
})
}
func runMigrateStorage(ctx *cli.Context) error {
stdCtx, cancel := installSignals()
func runMigrateStorage(stdCtx context.Context, ctx *cli.Command) error {
stdCtx, cancel := installSignals(stdCtx)
defer cancel()
if err := initDB(stdCtx); err != nil {

View file

@ -4,52 +4,55 @@
package cmd
import (
"context"
"strings"
"forgejo.org/modules/private"
"forgejo.org/modules/setting"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// CmdRestoreRepository represents the available restore a repository sub-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",
Aliases: []string{"r"},
Value: "./data",
Usage: "Repository dir path to restore from",
},
&cli.StringFlag{
Name: "owner_name",
Value: "",
Usage: "Restore destination owner name",
},
&cli.StringFlag{
Name: "repo_name",
Value: "",
Usage: "Restore destination repository name",
},
&cli.StringFlag{
Name: "units",
Value: "",
Usage: `Which items will be restored, one or more units should be separated as comma.
func cmdRestoreRepository() *cli.Command {
return &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",
Aliases: []string{"r"},
Value: "./data",
Usage: "Repository dir path to restore from",
},
&cli.StringFlag{
Name: "owner_name",
Value: "",
Usage: "Restore destination owner name",
},
&cli.StringFlag{
Name: "repo_name",
Value: "",
Usage: "Restore destination repository name",
},
&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{
Name: "validation",
Usage: "Sanity check the content of the files before trying to load them",
},
},
&cli.BoolFlag{
Name: "validation",
Usage: "Sanity check the content of the files before trying to load them",
},
},
}
}
func runRestoreRepository(c *cli.Context) error {
ctx, cancel := installSignals()
func runRestoreRepository(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
setting.MustInstalled()

View file

@ -33,7 +33,7 @@ import (
"github.com/golang-jwt/jwt/v5"
"github.com/kballard/go-shellquote"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
const (
@ -41,20 +41,22 @@ const (
)
// CmdServ represents the available serv sub-command.
var CmdServ = &cli.Command{
Name: "serv",
Usage: "(internal) 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{
Name: "enable-pprof",
func cmdServ() *cli.Command {
return &cli.Command{
Name: "serv",
Usage: "(internal) 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{
Name: "enable-pprof",
},
&cli.BoolFlag{
Name: "debug",
},
},
&cli.BoolFlag{
Name: "debug",
},
},
}
}
func setup(ctx context.Context, debug, gitNeeded bool) {
@ -131,8 +133,8 @@ func handleCliResponseExtra(extra private.ResponseExtra) error {
return nil
}
func runServ(c *cli.Context) error {
ctx, cancel := installSignals()
func runServ(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
// FIXME: This needs to internationalised
@ -194,7 +196,7 @@ func runServ(c *cli.Context) error {
if git.CheckGitVersionAtLeast("2.29") == nil {
// for AGit Flow
if cmd == "ssh_info" {
fmt.Print(`{"type":"gitea","version":1}`)
fmt.Print(`{"type":"agit","version":1}`)
return nil
}
}

View file

@ -26,48 +26,50 @@ import (
"forgejo.org/routers/install"
"github.com/felixge/fgprof"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// PIDFile could be set from build tag
var PIDFile = "/run/gitea.pid"
// CmdWeb represents the available web sub-command.
var CmdWeb = &cli.Command{
Name: "web",
Usage: "Start the Forgejo web server",
Description: `The Forgejo web server is the only thing you need to run,
func cmdWeb() *cli.Command {
return &cli.Command{
Name: "web",
Usage: "Start the Forgejo web server",
Description: `The Forgejo web server is the only thing you need to run,
and it takes care of all the other things for you`,
Before: PrepareConsoleLoggerLevel(log.INFO),
Action: runWeb,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "port",
Aliases: []string{"p"},
Value: "3000",
Usage: "Temporary port number to prevent conflict",
Before: PrepareConsoleLoggerLevel(log.INFO),
Action: runWeb,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "port",
Aliases: []string{"p"},
Value: "3000",
Usage: "Temporary port number to prevent conflict",
},
&cli.StringFlag{
Name: "install-port",
Value: "3000",
Usage: "Temporary port number to run the install page on to prevent conflict",
},
&cli.StringFlag{
Name: "pid",
Aliases: []string{"P"},
Value: PIDFile,
Usage: "Custom pid file path",
},
&cli.BoolFlag{
Name: "quiet",
Aliases: []string{"q"},
Usage: "Only display Fatal logging errors until logging is set-up",
},
&cli.BoolFlag{
Name: "verbose",
Usage: "Set initial logging to TRACE level until logging is properly set-up",
},
},
&cli.StringFlag{
Name: "install-port",
Value: "3000",
Usage: "Temporary port number to run the install page on to prevent conflict",
},
&cli.StringFlag{
Name: "pid",
Aliases: []string{"P"},
Value: PIDFile,
Usage: "Custom pid file path",
},
&cli.BoolFlag{
Name: "quiet",
Aliases: []string{"q"},
Usage: "Only display Fatal logging errors until logging is set-up",
},
&cli.BoolFlag{
Name: "verbose",
Usage: "Set initial logging to TRACE level until logging is properly set-up",
},
},
}
}
func runHTTPRedirector() {
@ -128,7 +130,7 @@ func showWebStartupMessage(msg string) {
}
}
func serveInstall(ctx *cli.Context) error {
func serveInstall(_ context.Context, ctx *cli.Command) error {
showWebStartupMessage("Prepare to run install page")
routers.InitWebInstallPage(graceful.GetManager().HammerContext())
@ -161,7 +163,7 @@ func serveInstall(ctx *cli.Context) error {
return nil
}
func serveInstalled(ctx *cli.Context) error {
func serveInstalled(_ context.Context, ctx *cli.Command) error {
setting.InitCfgProvider(setting.CustomConf)
setting.LoadCommonSettings()
setting.MustInstalled()
@ -233,7 +235,7 @@ func servePprof() {
finished()
}
func runWeb(ctx *cli.Context) error {
func runWeb(ctx context.Context, cli *cli.Command) error {
defer func() {
if panicked := recover(); panicked != nil {
log.Fatal("PANIC: %v\n%s", panicked, log.Stack(2))
@ -251,12 +253,12 @@ func runWeb(ctx *cli.Context) error {
}
// Set pid file setting
if ctx.IsSet("pid") {
createPIDFile(ctx.String("pid"))
if cli.IsSet("pid") {
createPIDFile(cli.String("pid"))
}
if !setting.InstallLock {
if err := serveInstall(ctx); err != nil {
if err := serveInstall(ctx, cli); err != nil {
return err
}
} else {
@ -267,7 +269,7 @@ func runWeb(ctx *cli.Context) error {
go servePprof()
}
return serveInstalled(ctx)
return serveInstalled(ctx, cli)
}
func setPort(port string) error {

View file

@ -4,16 +4,17 @@
package main
import (
"context"
"os"
"forgejo.org/modules/log"
"forgejo.org/modules/setting"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
func main() {
app := cli.NewApp()
app := cli.Command{}
app.Name = "environment-to-ini"
app.Usage = "Use provided environment to update configuration ini"
app.Description = `As a helper to allow docker users to update the forgejo configuration
@ -72,13 +73,13 @@ func main() {
},
}
app.Action = runEnvironmentToIni
err := app.Run(os.Args)
err := app.Run(context.Background(), os.Args)
if err != nil {
log.Fatal("Failed to run app with %s: %v", os.Args, err)
}
}
func runEnvironmentToIni(c *cli.Context) error {
func runEnvironmentToIni(ctx context.Context, c *cli.Command) error {
// the config system may change the environment variables, so get a copy first, to be used later
env := append([]string{}, os.Environ()...)
setting.InitWorkPathAndCfgProvider(os.Getenv, setting.ArgWorkPathAndCustomConf{

View file

@ -1025,6 +1025,10 @@ LEVEL = Info
;; The set of allowed values and rules are the same as DEFAULT_REPO_UNITS.
;DEFAULT_FORK_REPO_UNITS = repo.code,repo.pulls
;;
;; Comma separated list of default mirror repo units.
;; The set of allowed values and rules are the same as DEFAULT_REPO_UNITS.
;DEFAULT_MIRROR_REPO_UNITS = repo.code,repo.releases,repo.issues,repo.wiki,repo.projects,repo.packages
;;
;; Prefix archive files by placing them in a directory named after the repository
;PREFIX_ARCHIVE_FILES = true
;;
@ -1572,6 +1576,15 @@ LEVEL = Info
;; - manage_gpg_keys: a user cannot configure gpg keys
;;EXTERNAL_USER_DISABLE_FEATURES =
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[moderation]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; When true enables moderation capabilities; default is false.
;; If enabled it will be possible for users to report abusive content (new actions are added in the UI and /report_abuse route will be enabled) and a new Moderation section will be added to Admin settings where the reports can be reviewed.
;ENABLED = false
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[openid]

64
go.mod
View file

@ -2,17 +2,18 @@ module forgejo.org
go 1.24
toolchain go1.24.2
toolchain go1.24.4
require (
code.forgejo.org/f3/gof3/v3 v3.10.6
code.forgejo.org/f3/gof3/v3 v3.11.0
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251
code.forgejo.org/forgejo/go-rpmutils v1.0.0
code.forgejo.org/forgejo/levelqueue v1.0.0
code.forgejo.org/forgejo/reply v1.0.2
code.forgejo.org/go-chi/binding v1.0.0
code.forgejo.org/go-chi/cache v1.0.0
code.forgejo.org/go-chi/captcha v1.0.1
code.forgejo.org/go-chi/session v1.0.1
code.forgejo.org/go-chi/binding v1.0.1
code.forgejo.org/go-chi/cache v1.0.1
code.forgejo.org/go-chi/captcha v1.0.2
code.forgejo.org/go-chi/session v1.0.2
code.gitea.io/actions-proto-go v0.4.0
code.gitea.io/sdk/gitea v0.21.0
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
@ -20,12 +21,12 @@ require (
github.com/42wim/httpsig v1.2.3
github.com/42wim/sshsig v0.0.0-20250502153856-5100632e8920
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
github.com/ProtonMail/go-crypto v1.1.6
github.com/ProtonMail/go-crypto v1.3.0
github.com/PuerkitoBio/goquery v1.10.3
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2
github.com/alecthomas/chroma/v2 v2.17.2
github.com/alecthomas/chroma/v2 v2.18.0
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
github.com/blevesearch/bleve/v2 v2.5.1
github.com/blevesearch/bleve/v2 v2.5.2
github.com/buildkite/terminal-to-html/v3 v3.16.8
github.com/caddyserver/certmagic v0.23.0
github.com/chi-middleware/proxy v1.1.1
@ -40,14 +41,14 @@ require (
github.com/gliderlabs/ssh v0.3.8
github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
github.com/go-chi/chi/v5 v5.2.0
github.com/go-chi/chi/v5 v5.2.1
github.com/go-chi/cors v1.2.1
github.com/go-co-op/gocron v1.37.0
github.com/go-enry/go-enry/v2 v2.9.2
github.com/go-git/go-git/v5 v5.13.2
github.com/go-ldap/ldap/v3 v3.4.6
github.com/go-openapi/spec v0.20.14
github.com/go-sql-driver/mysql v1.9.1
github.com/go-openapi/spec v0.21.0
github.com/go-sql-driver/mysql v1.9.2
github.com/go-webauthn/webauthn v0.13.0
github.com/gobwas/glob v0.2.3
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
@ -86,27 +87,27 @@ require (
github.com/prometheus/client_golang v1.21.1
github.com/redis/go-redis/v9 v9.8.0
github.com/robfig/cron/v3 v3.0.1
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1
github.com/sassoftware/go-rpmutils v0.4.0
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2
github.com/sergi/go-diff v1.4.0
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92
github.com/stretchr/testify v1.10.0
github.com/syndtr/goleveldb v1.0.0
github.com/ulikunitz/xz v0.5.12
github.com/urfave/cli/v2 v2.27.6
github.com/urfave/cli/v3 v3.3.3
github.com/valyala/fastjson v1.6.4
github.com/yohcop/openid-go v1.0.1
github.com/yuin/goldmark v1.7.11
github.com/yuin/goldmark v1.7.12
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
gitlab.com/gitlab-org/api/client-go v0.126.0
go.uber.org/mock v0.5.1
golang.org/x/crypto v0.38.0
gitlab.com/gitlab-org/api/client-go v0.129.0
go.uber.org/mock v0.5.2
golang.org/x/crypto v0.39.0
golang.org/x/image v0.27.0
golang.org/x/net v0.40.0
golang.org/x/net v0.41.0
golang.org/x/oauth2 v0.30.0
golang.org/x/sync v0.14.0
golang.org/x/sync v0.15.0
golang.org/x/sys v0.33.0
golang.org/x/text v0.25.0
golang.org/x/text v0.26.0
google.golang.org/protobuf v1.36.4
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/ini.v1 v1.67.0
@ -121,7 +122,6 @@ require (
dario.cat/mergo v1.0.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
github.com/DataDog/zstd v1.5.5 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect
@ -146,13 +146,13 @@ require (
github.com/blevesearch/zapx/v13 v13.4.2 // indirect
github.com/blevesearch/zapx/v14 v14.4.2 // indirect
github.com/blevesearch/zapx/v15 v15.4.2 // indirect
github.com/blevesearch/zapx/v16 v16.2.3 // indirect
github.com/blevesearch/zapx/v16 v16.2.4 // indirect
github.com/boombuler/barcode v1.0.1 // indirect
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect
github.com/caddyserver/zerossl v0.1.3 // indirect
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudflare/circl v1.3.8 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
@ -170,9 +170,9 @@ require (
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-openapi/jsonpointer v0.20.2 // indirect
github.com/go-openapi/jsonreference v0.20.4 // indirect
github.com/go-openapi/swag v0.22.7 // indirect
github.com/go-openapi/jsonpointer v0.21.1 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.1 // indirect
github.com/go-webauthn/x v0.1.21 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
@ -237,9 +237,9 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.uber.org/zap/exp v0.3.0 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/time v0.10.0 // indirect
golang.org/x/tools v0.31.0 // indirect
golang.org/x/mod v0.25.0 // indirect
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.34.0 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)

124
go.sum
View file

@ -1,13 +1,15 @@
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
code.forgejo.org/f3/gof3/v3 v3.10.6 h1:Ru/Iz+pqM8IPi7atUHE7+q7v3O3DRbYgMFqrFTsO1m8=
code.forgejo.org/f3/gof3/v3 v3.10.6/go.mod h1:K6lQCWQIyN/5rjP/OJL9fMA6fd++satndE20w/I6Kss=
code.forgejo.org/f3/gof3/v3 v3.11.0 h1:f/xToKwqTgxG6PYxvewywjDQyCcyHEEJ6sZqUitFsAE=
code.forgejo.org/f3/gof3/v3 v3.11.0/go.mod h1:4FaRUNSQGBiD1M0DuB0yNv+Z2wMtlOeckgygHSSq4KQ=
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 h1:HTZl3CBk3ABNYtFI6TPLvJgGKFIhKT5CBk0sbOtkDKU=
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:PphB88CPbx601QrWPMZATeorACeVmQlyv3u+uUMbSaM=
code.forgejo.org/forgejo/act v1.26.0 h1:6mTmoaw7d/WpYiw/Pw6AaypxFdgJog5OFi/PMEgEbxs=
code.forgejo.org/forgejo/act v1.26.0/go.mod h1:HFDFrXPrqfM9aH2RCnMiBdo/3ThxDmZjp58InPjGOfo=
code.forgejo.org/forgejo/archiver/v3 v3.5.1 h1:UmmbA7D5550uf71SQjarmrn6yKwOGxtEjb3jaYYtmSE=
code.forgejo.org/forgejo/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
code.forgejo.org/forgejo/go-rpmutils v1.0.0 h1:RZGGeKt70p/WaIEL97pyT6uiiEIoN8/aLmS5Z6WmX0M=
code.forgejo.org/forgejo/go-rpmutils v1.0.0/go.mod h1:cg+VbgLXfrDPza9T+kBsMb3TVmmzPN4XseT6gDGLSUk=
code.forgejo.org/forgejo/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:RArF5AsF9LH4nEoJxqRxcP5r8hhRfWcId84G82YbqzA=
code.forgejo.org/forgejo/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
code.forgejo.org/forgejo/levelqueue v1.0.0 h1:9krYpU6BM+j/1Ntj6m+VCAIu0UNnne1/UfU/XgPpLuE=
@ -16,14 +18,14 @@ code.forgejo.org/forgejo/reply v1.0.2 h1:dMhQCHV6/O3L5CLWNTol+dNzDAuyCK88z4J/lCd
code.forgejo.org/forgejo/reply v1.0.2/go.mod h1:RyZUfzQLc+fuLIGjTSQWDAJWPiL4WtKXB/FifT5fM7U=
code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616 h1:kEZL84+02jY9RxXM4zHBWZ3Fml0B09cmP1LGkDsCfIA=
code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
code.forgejo.org/go-chi/binding v1.0.0 h1:EIDJtk9brK7WsT7rvS/D4cxX8XlnhY3LMy8ex1jeHu0=
code.forgejo.org/go-chi/binding v1.0.0/go.mod h1:fWwqaHj0H1/KeCpBqdvKunflq8pYfciEHI5v3UUeE2E=
code.forgejo.org/go-chi/cache v1.0.0 h1:akLfGxNlHcacmtutovNtYFSTMsbdcp5MGjAEsP4pxnE=
code.forgejo.org/go-chi/cache v1.0.0/go.mod h1:OVlZ/TqDYJ+RUJ+R+J+OLxtlyjo3pbjBeK7LAWAB+Vk=
code.forgejo.org/go-chi/captcha v1.0.1 h1:/oe1fvGOpdyyeGijg3oMYNOYLvEovNvp79Y3gLe3qbk=
code.forgejo.org/go-chi/captcha v1.0.1/go.mod h1:6EbjSVVa7WoZFENgwK/hLAJZq+HBXtgRsjnIngILC8Y=
code.forgejo.org/go-chi/session v1.0.1 h1:RNkcJQZJBqlvJoIFXSth87b3kMFZLDBA18VcitD+Z0Y=
code.forgejo.org/go-chi/session v1.0.1/go.mod h1:y69sjS984wc7k4xyu77yNE5HKeSlBoQW8VSGdsK7RAs=
code.forgejo.org/go-chi/binding v1.0.1 h1:coKNI+X1NzRN7X85LlrpvBRqk0TXpJ+ja28vusQWEuY=
code.forgejo.org/go-chi/binding v1.0.1/go.mod h1:oTFFDg/dkwFbmVuusiULB1OlrIJM95cOGK7Nc3GYcoo=
code.forgejo.org/go-chi/cache v1.0.1 h1:w6IsDcPbeEnEYZn7M2HJe3/3/Ehtcw/72VjcVK7+lBw=
code.forgejo.org/go-chi/cache v1.0.1/go.mod h1:K3aQSyRIN4xiuqV1kanfQ6O4ToDpzDpY3bNOyGjFe3U=
code.forgejo.org/go-chi/captcha v1.0.2 h1:vyHDPXkpjDv8bLO9NqtWzZayzstD/WpJ5xwEkAaqZGQ=
code.forgejo.org/go-chi/captcha v1.0.2/go.mod h1:lxiPLcJ76UCZHoH31/Wbum4GUi2NgjfFZLrJkKv1lLE=
code.forgejo.org/go-chi/session v1.0.2 h1:pG+AXre9L9VXJmTaADXkmeEPuRalhmBXyv6tG2Rvjcc=
code.forgejo.org/go-chi/session v1.0.2/go.mod h1:HnEGyBny7WPzCiVLP2vzL5ssma+3gCSl/vLpuVNYrqc=
code.gitea.io/actions-proto-go v0.4.0 h1:OsPBPhodXuQnsspG1sQ4eRE1PeoZyofd7+i73zCwnsU=
code.gitea.io/actions-proto-go v0.4.0/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas=
code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4=
@ -46,13 +48,11 @@ github.com/6543/go-version v1.3.1 h1:HvOp+Telns7HWJ2Xo/05YXQSB2bE0WmVgbHqwMPZT4U
github.com/6543/go-version v1.3.1/go.mod h1:oqFAHCwtLVUTLdhQmVZWYvaHXTdsbB4SY85at64SQEo=
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/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ=
github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
github.com/RoaringBitmap/roaring/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg=
@ -62,8 +62,8 @@ github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2/go.mod h1:JitQWJ8JuV4Y
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
github.com/alecthomas/chroma/v2 v2.17.2 h1:Rm81SCZ2mPoH+Q8ZCc/9YvzPUN/E7HgPiPJD8SLV6GI=
github.com/alecthomas/chroma/v2 v2.17.2/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
github.com/alecthomas/chroma/v2 v2.18.0 h1:6h53Q4hW83SuF+jcsp7CVhLsMozzvQvO8HBbKQW+gn4=
github.com/alecthomas/chroma/v2 v2.18.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
@ -87,8 +87,8 @@ github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCk
github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
github.com/blevesearch/bleve/v2 v2.5.1 h1:cc/O++W2Hcjp1SU5ETHeE+QYWv2oV88ldYEPowdmg8M=
github.com/blevesearch/bleve/v2 v2.5.1/go.mod h1:9g/wnbWKm9AgXrU8Ecqi+IDdqjUHWymwkQRDg+5tafU=
github.com/blevesearch/bleve/v2 v2.5.2 h1:Ab0r0MODV2C5A6BEL87GqLBySqp/s9xFgceCju6BQk8=
github.com/blevesearch/bleve/v2 v2.5.2/go.mod h1:5Dj6dUQxZM6aqYT3eutTD/GpWKGFSsV8f7LDidFbwXo=
github.com/blevesearch/bleve_index_api v1.2.8 h1:Y98Pu5/MdlkRyLM0qDHostYo7i+Vv1cDNhqTeR4Sy6Y=
github.com/blevesearch/bleve_index_api v1.2.8/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0=
github.com/blevesearch/geo v0.2.3 h1:K9/vbGI9ehlXdxjxDRJtoAMt7zGAsMIzc6n8zWcwnhg=
@ -121,13 +121,13 @@ github.com/blevesearch/zapx/v14 v14.4.2 h1:2SGHakVKd+TrtEqpfeq8X+So5PShQ5nW6GNxT
github.com/blevesearch/zapx/v14 v14.4.2/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXmppH9Hi7a877D8=
github.com/blevesearch/zapx/v15 v15.4.2 h1:sWxpDE0QQOTjyxYbAVjt3+0ieu8NCE0fDRaFxEsp31k=
github.com/blevesearch/zapx/v15 v15.4.2/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw=
github.com/blevesearch/zapx/v16 v16.2.3 h1:7Y0r+a3diEvlazsncexq1qoFOcBd64xwMS7aDm4lo1s=
github.com/blevesearch/zapx/v16 v16.2.3/go.mod h1:wVJ+GtURAaRG9KQAMNYyklq0egV+XJlGcXNCE0OFjjA=
github.com/blevesearch/zapx/v16 v16.2.4 h1:tGgfvleXTAkwsD5mEzgM3zCS/7pgocTCnO1oyAUjlww=
github.com/blevesearch/zapx/v16 v16.2.4/go.mod h1:Rti/REtuuMmzwsI8/C/qIzRaEoSK/wiFYw5e5ctUKKs=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous=
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf h1:TqhNAT4zKbTdLa62d2HDBFdvgSbIGB3eJE8HqhgiL9I=
github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
@ -150,8 +150,8 @@ github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moA
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI=
github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@ -215,8 +215,8 @@ github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73/go.mod h1:jyveZeGw5La
github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA=
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0=
github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0=
@ -239,16 +239,16 @@ github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A=
github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc=
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU=
github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4=
github.com/go-openapi/spec v0.20.14 h1:7CBlRnw+mtjFGlPDRZmAMnq35cRzI91xj03HVyUi/Do=
github.com/go-openapi/spec v0.20.14/go.mod h1:8EOhTpBoFiask8rrgwbLC3zmJfz4zsCUueRuPM6GNkw=
github.com/go-openapi/swag v0.22.7 h1:JWrc1uc/P9cSomxfnsFSVWoE1FW6bNbrVPmpQYpCcR8=
github.com/go-openapi/swag v0.22.7/go.mod h1:Gl91UqO+btAM0plGGxHqJcQZ1ZTy6jbmridBTsDy8A0=
github.com/go-sql-driver/mysql v1.9.1 h1:FrjNGn/BsJQjVRuSa8CBrM5BWA9BWoXXat3KrtSb/iI=
github.com/go-sql-driver/mysql v1.9.1/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
@ -499,13 +499,11 @@ github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg=
github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+YgDpBcK1ITf3o96N/K7/wsRXQnUTEs=
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c/go.mod h1:owqhoLW1qZoYLZzLnBw+QkPP9WZnjlSWihhxAJC1+/M=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
@ -537,6 +535,8 @@ github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g=
github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/urfave/cli/v3 v3.3.3 h1:byCBaVdIXuLPIDm5CYZRVG6NvT7tv1ECqdU4YzlEa3I=
github.com/urfave/cli/v3 v3.3.3/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
@ -554,8 +554,8 @@ github.com/yohcop/openid-go v1.0.1/go.mod h1:b/AvD03P0KHj4yuihb+VtLD6bYYgsy0zqBz
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.11 h1:ZCxLyDMtz0nT2HFfsYG8WZ47Trip2+JyLysKcMYE5bo=
github.com/yuin/goldmark v1.7.11/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY=
github.com/yuin/goldmark v1.7.12/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
@ -564,8 +564,8 @@ github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
gitlab.com/gitlab-org/api/client-go v0.126.0 h1:VV5TdkF6pMbEdFGvbR2CwEgJwg6qdg1u3bj5eD2tiWk=
gitlab.com/gitlab-org/api/client-go v0.126.0/go.mod h1:bYC6fPORKSmtuPRyD9Z2rtbAjE7UeNatu2VWHRf4/LE=
gitlab.com/gitlab-org/api/client-go v0.129.0 h1:o9KLn6fezmxBQWYnQrnilwyuOjlx4206KP0bUn3HuBE=
gitlab.com/gitlab-org/api/client-go v0.129.0/go.mod h1:ZhSxLAWadqP6J9lMh40IAZOlOxBLPRh7yFOXR/bMJWM=
go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@ -573,8 +573,8 @@ go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.5.1 h1:ASgazW/qBmR+A32MYFDB6E2POoTgOwT509VP0CT/fjs=
go.uber.org/mock v0.5.1/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
@ -591,8 +591,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w=
@ -603,8 +603,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -620,8 +620,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -633,8 +633,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -684,10 +684,10 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@ -695,8 +695,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

38
manifest.scm Normal file
View file

@ -0,0 +1,38 @@
;;; Copyright 2025 The Forgejo Authors. All rights reserved.
;;; SPDX-License-Identifier: MIT
;;;
;;; Commentary:
;;;
;;; This is a GNU Guix manifest that can be used to create a
;;; development environment to build and test Forgejo.
;;;
;;; The following is a usage example to create a containerized
;;; environment, with HOME shared for the Go cache and the network
;;; made available to fetch required Go and Node dependencies.
;;;
#|
guix shell -CNF --share=$HOME -m manifest.scm
export GOTOOLCHAIN=local # to use the Go binary from Guix
export CC=gcc CGO_ENABLED=1
export TAGS="timetzdata sqlite sqlite_unlock_notify"
make clean
make -j$(nproc)
make test -j$(nproc) # run unit tests
make test-sqlite -j$(nproc) # run integration tests
make watch # run an instance/rebuild on changes
|#
(specifications->manifest
(list "bash-minimal"
"coreutils"
"findutils"
"gcc-toolchain"
"git" ;libpcre support is required
"git-lfs"
"gnupg"
"go"
"grep"
"make"
"node"
"nss-certs"
"openssh"
"sed"))

View file

@ -5,6 +5,7 @@ package actions
import (
"context"
"errors"
"fmt"
"slices"
"strings"
@ -222,29 +223,38 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
var hasWaiting bool
for _, v := range jobs {
id, job := v.Job()
needs := job.Needs()
if err := v.SetJob(id, job.EraseNeeds()); err != nil {
return err
status := StatusFailure
payload := []byte{}
needs := []string{}
name := run.Title
runsOn := []string{}
if job != nil {
needs = job.Needs()
if err := v.SetJob(id, job.EraseNeeds()); err != nil {
return err
}
payload, _ = v.Marshal()
if len(needs) > 0 || run.NeedApproval {
status = StatusBlocked
} else {
status = StatusWaiting
hasWaiting = true
}
name, _ = util.SplitStringAtByteN(job.Name, 255)
runsOn = job.RunsOn()
}
payload, _ := v.Marshal()
status := StatusWaiting
if len(needs) > 0 || run.NeedApproval {
status = StatusBlocked
} else {
hasWaiting = true
}
job.Name, _ = util.SplitStringAtByteN(job.Name, 255)
runJobs = append(runJobs, &ActionRunJob{
RunID: run.ID,
RepoID: run.RepoID,
OwnerID: run.OwnerID,
CommitSHA: run.CommitSHA,
IsForkPullRequest: run.IsForkPullRequest,
Name: job.Name,
Name: name,
WorkflowPayload: payload,
JobID: id,
Needs: needs,
RunsOn: job.RunsOn(),
RunsOn: runsOn,
Status: status,
})
}
@ -346,7 +356,7 @@ func UpdateRunWithoutNotification(ctx context.Context, run *ActionRun, cols ...s
return err
}
if affected == 0 {
return fmt.Errorf("run has changed")
return errors.New("run has changed")
// It's impossible that the run is not found, since Gitea never deletes runs.
}

View file

@ -54,6 +54,8 @@ type FindRunJobOptions struct {
CommitSHA string
Statuses []Status
UpdatedBefore timeutil.TimeStamp
Events []string // []webhook_module.HookEventType
RunNumber int64
}
func (opts FindRunJobOptions) ToConds() builder.Cond {
@ -76,5 +78,11 @@ func (opts FindRunJobOptions) ToConds() builder.Cond {
if opts.UpdatedBefore > 0 {
cond = cond.And(builder.Lt{"updated": opts.UpdatedBefore})
}
if len(opts.Events) > 0 {
cond = cond.And(builder.In("event", opts.Events))
}
if opts.RunNumber > 0 {
cond = cond.And(builder.Eq{"`index`": opts.RunNumber})
}
return cond
}

View file

@ -34,6 +34,15 @@ var statusNames = map[Status]string{
StatusBlocked: "blocked",
}
var nameToStatus = make(map[string]Status, len(statusNames))
func init() {
// Populate name to status lookup map
for status, name := range statusNames {
nameToStatus[name] = status
}
}
// String returns the string name of the Status
func (s Status) String() string {
return statusNames[s]
@ -102,3 +111,8 @@ func (s Status) AsResult() runnerv1.Result {
}
return runnerv1.Result_RESULT_UNSPECIFIED
}
func StatusFromString(name string) (Status, bool) {
status, exists := nameToStatus[name]
return status, exists
}

View file

@ -7,6 +7,7 @@ package activities
import (
"context"
"errors"
"fmt"
"net/url"
"path"
@ -458,7 +459,7 @@ type GetFeedsOptions struct {
// GetFeeds returns actions according to the provided options
func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, error) {
if opts.RequestedUser == nil && opts.RequestedTeam == nil && opts.RequestedRepo == nil {
return nil, 0, fmt.Errorf("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
return nil, 0, errors.New("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
}
cond, err := activityQueryCondition(ctx, opts)

View file

@ -13,6 +13,12 @@ import (
"forgejo.org/modules/timeutil"
)
const (
// contributionsMaxAgeSeconds How old data to retrieve for the heatmap.
// 371 days to cover the entire heatmap (53 *full* weeks)
contributionsMaxAgeSeconds = 32054400
)
// UserHeatmapData represents the data needed to create a heatmap
type UserHeatmapData struct {
Timestamp timeutil.TimeStamp `json:"timestamp"`
@ -62,7 +68,7 @@ func getUserHeatmapData(ctx context.Context, user *user_model.User, team *organi
Select(groupBy+" AS timestamp, count(user_id) as contributions").
Table("action").
Where(cond).
And("created_unix > ?", timeutil.TimeStampNow()-31536000).
And("created_unix >= ?", timeutil.TimeStampNow()-contributionsMaxAgeSeconds).
GroupBy("timestamp").
OrderBy("timestamp").
Find(&hdata)

View file

@ -54,6 +54,10 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
"multiple actions performed with two grouped together",
10, 10, 3, `[{"timestamp":1603009800,"contributions":1},{"timestamp":1603010700,"contributions":2}]`,
},
{
"test cutoff within",
40, 40, 1, `[{"timestamp":1577404800,"contributions":1}]`,
},
}
// Prepare
require.NoError(t, unittest.PrepareTestDatabase())

View file

@ -5,6 +5,7 @@ package asymkey
import (
"context"
"errors"
"fmt"
"strings"
"time"
@ -209,7 +210,7 @@ func parseGPGKey(ctx context.Context, ownerID int64, e *openpgp.Entity, verified
// deleteGPGKey does the actual key deletion
func deleteGPGKey(ctx context.Context, keyID string) (int64, error) {
if keyID == "" {
return 0, fmt.Errorf("empty KeyId forbidden") // Should never happen but just to be sure
return 0, errors.New("empty KeyId forbidden") // Should never happen but just to be sure
}
// Delete imported key
n, err := db.GetEngine(ctx).Where("key_id=?", keyID).Delete(new(GPGKeyImport))

View file

@ -7,6 +7,7 @@ import (
"bytes"
"crypto"
"encoding/base64"
"errors"
"fmt"
"hash"
"io"
@ -75,7 +76,7 @@ func base64DecPubKey(content string) (*packet.PublicKey, error) {
// Check type
pkey, ok := p.(*packet.PublicKey)
if !ok {
return nil, fmt.Errorf("key is not a public key")
return nil, errors.New("key is not a public key")
}
return pkey, nil
}
@ -122,15 +123,15 @@ func readArmoredSign(r io.Reader) (body io.Reader, err error) {
func extractSignature(s string) (*packet.Signature, error) {
r, err := readArmoredSign(strings.NewReader(s))
if err != nil {
return nil, fmt.Errorf("Failed to read signature armor")
return nil, errors.New("Failed to read signature armor")
}
p, err := packet.Read(r)
if err != nil {
return nil, fmt.Errorf("Failed to read signature packet")
return nil, errors.New("Failed to read signature packet")
}
sig, ok := p.(*packet.Signature)
if !ok {
return nil, fmt.Errorf("Packet is not a signature")
return nil, errors.New("Packet is not a signature")
}
return sig, nil
}

View file

@ -6,6 +6,7 @@ package asymkey
import (
"context"
"errors"
"fmt"
"hash"
"strings"
@ -316,7 +317,7 @@ func verifyWithGPGSettings(ctx context.Context, gpgSettings *git.GPGSettings, si
func verifySign(s *packet.Signature, h hash.Hash, k *GPGKey) error {
// Check if key can sign
if !k.CanSign {
return fmt.Errorf("key can not sign")
return errors.New("key can not sign")
}
// Decode key
pkey, err := base64DecPubKey(k.Content)

View file

@ -10,6 +10,7 @@ import (
"encoding/base64"
"encoding/binary"
"encoding/pem"
"errors"
"fmt"
"math/big"
"os"
@ -93,7 +94,7 @@ func parseKeyString(content string) (string, error) {
block, _ := pem.Decode([]byte(content))
if block == nil {
return "", fmt.Errorf("failed to parse PEM block containing the public key")
return "", errors.New("failed to parse PEM block containing the public key")
}
if strings.Contains(block.Type, "PRIVATE") {
return "", ErrKeyIsPrivate
@ -226,7 +227,7 @@ func SSHNativeParsePublicKey(keyLine string) (string, int, error) {
// The ssh library can parse the key, so next we find out what key exactly we have.
switch pkeyType {
case ssh.KeyAlgoDSA:
case ssh.KeyAlgoDSA: //nolint:staticcheck
rawPub := struct {
Name string
P, Q, G, Y *big.Int

View file

@ -69,6 +69,9 @@ func (l *XORMLogBridge) Warn(v ...any) {
// Warnf show warning log
func (l *XORMLogBridge) Warnf(format string, v ...any) {
if format == "Table %s Column %s db default is %s, struct default is %s" || format == "Table %s Column %s db nullable is %v, struct nullable is %v" {
return
}
l.Log(stackLevel, log.WARN, format, v...)
}

View file

@ -0,0 +1,17 @@
-
id: 1001
run_id: 792
runner_id: 1
repo_id: 4
owner_id: 1
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
storage_path: "27/5/1730330775594233150.chunk"
file_size: 693147180559
file_compressed_size: 693147180559
content_encoding: "application/zip"
artifact_path: "big-file.zip"
artifact_name: "big-file"
status: 4
created_unix: 1730330775
updated_unix: 1730330775
expired_unix: 1738106775

View file

@ -74,3 +74,11 @@
is_private: false
created_unix: 1680454039
content: '4|' # issueId 5
- id: 10
user_id: 40
op_type: 1 # create repo
act_user_id: 40
repo_id: 60 # public
is_private: false
created_unix: 1577404800 # end of heatmap

View file

@ -471,3 +471,64 @@
need_approval: 0
approved_by: 0
event_payload: '{"head_commit":{"id":"5f22f7d0d95d614d25a5b68592adb345a4b5c7fd"}}'
# GET action run(s) test
-
id: 892
title: "successful push run"
repo_id: 63
owner_id: 2
workflow_id: "success.yaml"
index: 1
trigger_user_id: 2
ref: "refs/heads/main"
commit_sha: "97f29ee599c373c729132a5c46a046978311e0ee"
event: "push"
is_fork_pull_request: 0
status: 1 # success
started: 1683636528
stopped: 1683636626
created: 1683636108
updated: 1683636626
need_approval: 0
approved_by: 0
-
id: 893
title: "failed pull_request run"
repo_id: 63
owner_id: 2
workflow_id: "failed.yaml"
index: 2
trigger_user_id: 2
ref: "refs/heads/bugfix-1"
commit_sha: "35c5cddfc19397501ec8f4f7bb808a7c8f04445f"
event: "pull_request"
is_fork_pull_request: 0
status: 2 # failure
started: 1683636528
stopped: 1683636626
created: 1683636108
updated: 1683636626
need_approval: 0
approved_by: 0
-
id: 894
title: "running workflow_dispatch run"
repo_id: 63
owner_id: 2
workflow_id: "running.yaml"
index: 3
trigger_user_id: 2
ref: "refs/heads/main"
commit_sha: "97f29ee599c373c729132a5c46a046978311e0ee"
event: "workflow_dispatch"
is_fork_pull_request: 0
status: 6 # running
started: 1683636528
created: 1683636108
updated: 1683636626
need_approval: 0
approved_by: 0

View file

@ -17,3 +17,13 @@
id: 4
user_id: 31
follow_id: 33
-
id: 5
user_id: 4
follow_id: 8
-
id: 6
user_id: 5
follow_id: 8

View file

@ -18,7 +18,7 @@
id: 2
hook_id: 1
uuid: uuid2
is_delivered: false
is_delivered: true
-
id: 3
@ -40,4 +40,4 @@
id: 4
hook_id: 3
uuid: uuid4
is_delivered: false
is_delivered: true

View file

@ -795,3 +795,10 @@
type: 10
config: "{}"
created_unix: 946684810
-
id: 115
repo_id: 63
type: 10
config: "{}"
created_unix: 946684810

View file

@ -31,6 +31,8 @@
close_issues_via_commit_in_any_branch: false
created_unix: 1731254961
updated_unix: 1731254961
topics: '[]'
-
id: 2
owner_id: 2
@ -61,7 +63,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: true
topics: '[]'
-
id: 3
owner_id: 3
@ -94,6 +96,7 @@
close_issues_via_commit_in_any_branch: false
created_unix: 1700000001
updated_unix: 1700000001
topics: '[]'
-
id: 4
@ -125,6 +128,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 5
@ -158,6 +162,7 @@
close_issues_via_commit_in_any_branch: false
created_unix: 1700000002
updated_unix: 1700000002
topics: '[]'
-
id: 6
@ -190,6 +195,7 @@
close_issues_via_commit_in_any_branch: false
created_unix: 1710000001
updated_unix: 1710000001
topics: '[]'
-
id: 7
@ -222,6 +228,7 @@
close_issues_via_commit_in_any_branch: false
created_unix: 1710000003
updated_unix: 1710000003
topics: '[]'
-
id: 8
@ -254,6 +261,7 @@
close_issues_via_commit_in_any_branch: false
created_unix: 1710000002
updated_unix: 1710000002
topics: '[]'
-
id: 9
@ -284,6 +292,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 10
@ -315,6 +324,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 11
@ -346,6 +356,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 12
@ -376,6 +387,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 13
@ -406,6 +418,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 14
@ -437,6 +450,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 15
@ -468,6 +482,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 16
@ -499,6 +514,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 17
@ -529,6 +545,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 18
@ -559,6 +576,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 19
@ -589,6 +607,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 20
@ -619,6 +638,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 21
@ -649,6 +669,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 22
@ -679,6 +700,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 23
@ -709,6 +731,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 24
@ -739,6 +762,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 25
@ -769,6 +793,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 26
@ -799,6 +824,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 27
@ -829,6 +855,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 28
@ -859,6 +886,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 29
@ -889,6 +917,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 30
@ -919,6 +948,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 31
@ -950,6 +980,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 32 # org public repo
@ -982,6 +1013,7 @@
close_issues_via_commit_in_any_branch: false
created_unix: 1700000003
updated_unix: 1700000003
topics: '[]'
-
id: 33
@ -1013,6 +1045,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 34
@ -1043,6 +1076,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 35
@ -1073,6 +1107,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 36
@ -1104,6 +1139,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 37
@ -1135,6 +1171,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 38
@ -1166,6 +1203,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 39
@ -1197,6 +1235,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 40
@ -1228,6 +1267,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 41
@ -1259,6 +1299,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 42
@ -1290,6 +1331,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 43
@ -1320,6 +1362,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 44
@ -1351,6 +1394,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 45
@ -1381,6 +1425,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 46
@ -1412,6 +1457,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 47
@ -1443,6 +1489,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 48
@ -1474,6 +1521,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 49
@ -1506,6 +1554,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 50
@ -1537,6 +1586,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 51
@ -1568,6 +1618,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 52
@ -1599,6 +1650,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 53
@ -1627,6 +1679,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 54
@ -1639,6 +1692,7 @@
is_archived: false
is_private: true
status: 0
topics: '[]'
-
id: 55
@ -1651,6 +1705,7 @@
is_private: true
num_issues: 1
status: 0
topics: '[]'
-
id: 56
@ -1664,6 +1719,7 @@
is_private: true
status: 0
num_issues: 0
topics: '[]'
-
id: 57
@ -1677,6 +1733,7 @@
is_private: false
status: 0
num_issues: 0
topics: '[]'
-
id: 58 # org public repo
@ -1708,6 +1765,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 1059
@ -1721,6 +1779,7 @@
is_private: false
status: 0
num_issues: 0
topics: '[]'
-
id: 59
@ -1734,6 +1793,7 @@
is_private: true
status: 0
num_issues: 0
topics: '[]'
-
id: 60
@ -1765,6 +1825,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 61
@ -1796,6 +1857,7 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
- id: 62
owner_id: 2
@ -1826,3 +1888,36 @@
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'
-
id: 63
owner_id: 2
owner_name: user2
lower_name: test_action_run_search
name: test_action_run_search
default_branch: main
num_watches: 0
num_stars: 0
num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
num_milestones: 0
num_closed_milestones: 0
num_projects: 0
num_closed_projects: 0
is_private: true
is_empty: false
is_archived: false
is_mirror: false
status: 0
is_fork: false
fork_id: 0
is_template: false
template_id: 0
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
topics: '[]'

View file

@ -53,6 +53,7 @@
login_source: 0
login_name: user2
type: 0
website: https://keyoxide.org/eb114f5e6c0dc2bcdd183550a4b61a2dc5923710
salt: ZogKvWdyEx
max_repo_creation: -1
is_active: true
@ -69,7 +70,7 @@
num_followers: 2
num_following: 1
num_stars: 2
num_repos: 17
num_repos: 18
num_teams: 0
num_members: 0
visibility: 0
@ -93,7 +94,7 @@
login_name: org3
type: 1
salt: ZogKvWdyEx
max_repo_creation: -1
max_repo_creation: 1000
is_active: false
is_admin: false
is_restricted: false
@ -143,7 +144,7 @@
avatar_email: user4@example.com
use_custom_avatar: true
num_followers: 0
num_following: 1
num_following: 2
num_stars: 0
num_repos: 0
num_teams: 0
@ -181,7 +182,7 @@
avatar_email: user5@example.com
use_custom_avatar: true
num_followers: 0
num_following: 0
num_following: 1
num_stars: 0
num_repos: 1
num_teams: 0
@ -294,7 +295,7 @@
avatar: ""
avatar_email: user8@example.com
use_custom_avatar: true
num_followers: 1
num_followers: 3
num_following: 1
num_stars: 0
num_repos: 0

View file

@ -35,7 +35,7 @@ func Test_FederationHostValidation(t *testing.T) {
HostSchema: "https",
}
if res, _ := validation.IsValid(sut); res {
t.Errorf("sut should be invalid: HostFqdn empty")
t.Error("sut should be invalid: HostFqdn empty")
}
sut = FederationHost{
@ -48,7 +48,7 @@ func Test_FederationHostValidation(t *testing.T) {
HostSchema: "https",
}
if res, _ := validation.IsValid(sut); res {
t.Errorf("sut should be invalid: HostFqdn too long (len=256)")
t.Error("sut should be invalid: HostFqdn too long (len=256)")
}
sut = FederationHost{
@ -59,7 +59,7 @@ func Test_FederationHostValidation(t *testing.T) {
HostSchema: "https",
}
if res, _ := validation.IsValid(sut); res {
t.Errorf("sut should be invalid: NodeInfo invalid")
t.Error("sut should be invalid: NodeInfo invalid")
}
sut = FederationHost{
@ -72,7 +72,7 @@ func Test_FederationHostValidation(t *testing.T) {
HostSchema: "https",
}
if res, _ := validation.IsValid(sut); res {
t.Errorf("sut should be invalid: Future timestamp")
t.Error("sut should be invalid: Future timestamp")
}
sut = FederationHost{
@ -85,6 +85,6 @@ func Test_FederationHostValidation(t *testing.T) {
HostSchema: "https",
}
if res, _ := validation.IsValid(sut); res {
t.Errorf("sut should be invalid: HostFqdn lower case")
t.Error("sut should be invalid: HostFqdn lower case")
}
}

View file

@ -4,7 +4,7 @@
package forgefed
import (
"fmt"
"errors"
"reflect"
"strings"
"testing"
@ -28,7 +28,7 @@ func Test_NodeInfoWellKnownUnmarshalJSON(t *testing.T) {
},
"empty": {
item: []byte(``),
wantErr: fmt.Errorf("cannot parse JSON: cannot parse empty string; unparsed tail: \"\""),
wantErr: errors.New("cannot parse JSON: cannot parse empty string; unparsed tail: \"\""),
},
}
@ -74,7 +74,7 @@ func Test_NewNodeInfoWellKnown(t *testing.T) {
_, err := NewNodeInfoWellKnown([]byte(`invalid`))
if err == nil {
t.Errorf("error was expected here")
t.Error("error was expected here")
}
}
@ -87,6 +87,6 @@ func Test_NewNodeInfo(t *testing.T) {
_, err := NewNodeInfo([]byte(`invalid`))
if err == nil {
t.Errorf("error was expected here")
t.Error("error was expected here")
}
}

View file

@ -5,6 +5,7 @@ package forgejo_migrations //nolint:revive
import (
"context"
"errors"
"fmt"
"os"
@ -98,6 +99,10 @@ var migrations = []*Migration{
NewMigration("Add public key information to `FederatedUser` and `FederationHost`", AddPublicKeyInformationForFederation),
// v29 -> v30
NewMigration("Migrate `User.NormalizedFederatedURI` column to extract port & schema into FederatedHost", MigrateNormalizedFederatedURI),
// v30 -> v31
NewMigration("Normalize repository.topics to empty slice instead of null", SetTopicsAsEmptySlice),
// v31 -> v32
NewMigration("Migrate maven package name concatenation", ChangeMavenArtifactConcatenation),
}
// GetCurrentDBVersion returns the current Forgejo database version.
@ -130,7 +135,7 @@ func EnsureUpToDate(x *xorm.Engine) error {
}
if currentDB < 0 {
return fmt.Errorf("database has not been initialized")
return errors.New("database has not been initialized")
}
expected := ExpectedVersion()

View file

@ -0,0 +1,58 @@
// Copyright 2025 The Forgejo Authors.
// SPDX-License-Identifier: GPL-3.0-or-later
package forgejo_migrations //nolint:revive
import (
"xorm.io/xorm"
"xorm.io/xorm/schemas"
)
func SetTopicsAsEmptySlice(x *xorm.Engine) error {
var err error
switch x.Dialect().URI().DBType {
case schemas.MYSQL:
_, err = x.Exec("UPDATE `repository` SET topics = '[]' WHERE topics IS NULL OR topics = 'null'")
case schemas.SQLITE:
_, err = x.Exec("UPDATE `repository` SET topics = '[]' WHERE topics IS NULL OR topics = 'null'")
case schemas.POSTGRES:
_, err = x.Exec("UPDATE `repository` SET topics = '[]' WHERE topics IS NULL OR topics::text = 'null'")
}
if err != nil {
return err
}
if x.Dialect().URI().DBType == schemas.SQLITE {
sessMigration := x.NewSession()
defer sessMigration.Close()
if err := sessMigration.Begin(); err != nil {
return err
}
_, err = sessMigration.Exec("ALTER TABLE `repository` RENAME COLUMN `topics` TO `topics_backup`")
if err != nil {
return err
}
_, err = sessMigration.Exec("ALTER TABLE `repository` ADD COLUMN `topics` TEXT NOT NULL DEFAULT '[]'")
if err != nil {
return err
}
_, err = sessMigration.Exec("UPDATE `repository` SET `topics` = `topics_backup`")
if err != nil {
return err
}
_, err = sessMigration.Exec("ALTER TABLE `repository` DROP COLUMN `topics_backup`")
if err != nil {
return err
}
return sessMigration.Commit()
}
type Repository struct {
ID int64 `xorm:"pk autoincr"`
Topics []string `xorm:"TEXT JSON NOT NULL"`
}
return x.Sync(new(Repository))
}

View file

@ -0,0 +1,38 @@
// Copyright 2025 The Forgejo Authors.
// SPDX-License-Identifier: GPL-3.0-or-later
package forgejo_migrations //nolint:revive
import (
"testing"
migration_tests "forgejo.org/models/migrations/test"
"github.com/stretchr/testify/require"
)
func Test_SetTopicsAsEmptySlice(t *testing.T) {
type Repository struct {
ID int64 `xorm:"pk autoincr"`
Topics []string `xorm:"TEXT JSON"`
}
x, deferable := migration_tests.PrepareTestEnv(t, 0, new(Repository))
defer deferable()
if x == nil || t.Failed() {
return
}
require.NoError(t, SetTopicsAsEmptySlice(x))
var repos []Repository
require.NoError(t, x.Find(&repos))
for _, repo := range repos {
if repo.ID == 2 {
require.Equal(t, []string{"go", "dev"}, repo.Topics, "Valid topics should remain unchanged")
} else {
require.Equal(t, []string{}, repo.Topics, "NULL topics should be set to empty array")
}
}
}

View file

@ -0,0 +1,414 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package forgejo_migrations //nolint:revive
import (
"encoding/xml"
"fmt"
"regexp"
"slices"
"sort"
"strconv"
"strings"
"forgejo.org/models/packages"
"forgejo.org/modules/json"
"forgejo.org/modules/log"
"forgejo.org/modules/packages/maven"
packages_service "forgejo.org/services/packages"
"golang.org/x/net/context"
"xorm.io/xorm"
)
var getPackage = packages_service.GetPackageFileStream
type Snapshot struct {
baseVersion string
date string
time string
build int
}
type Metadata struct {
XMLName xml.Name `xml:"metadata"`
ModelVersion string `xml:"modelVersion,attr"`
GroupID string `xml:"groupId"`
ArtifactID string `xml:"artifactId"`
Version string `xml:"version"`
}
type mavenPackageResult struct {
PackageFile *packages.PackageFile `xorm:"extends"`
PackageVersion *packages.PackageVersion `xorm:"extends"`
Package *packages.Package `xorm:"extends"`
PackageName string `xorm:"-"`
Snapshot *Snapshot `xorm:"-"`
GroupID string `xorm:"-"`
ArtifactID string `xorm:"-"`
}
// ChangeMavenArtifactConcatenation resolves old dash-concatenated Maven coordinates and regenerates metadata.
// Note: runs per-owner in a single transaction; failures roll back all owners.
func ChangeMavenArtifactConcatenation(x *xorm.Engine) error {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
// get unique owner IDs of Maven packages
var ownerIDs []*int64
if err := sess.
Table("package").
Select("package.owner_id").
Where("package.type = 'maven'").
GroupBy("package.owner_id").
OrderBy("package.owner_id DESC").
Find(&ownerIDs); err != nil {
return err
}
for _, id := range ownerIDs {
if err := fixMavenArtifactPerOwner(sess, id); err != nil {
log.Error("owner %d migration failed: %v", id, err)
return err // rollback all
}
}
return sess.Commit()
}
func fixMavenArtifactPerOwner(sess *xorm.Session, ownerID *int64) error {
results, err := getMavenPackageResultsToUpdate(sess, ownerID)
if err != nil {
return err
}
if err = resolvePackageCollisions(results, sess); err != nil {
return err
}
if err = processPackageVersions(results, sess); err != nil {
return err
}
return processPackageFiles(results, sess)
}
// processPackageFiles updates Maven package files and versions in the database
// Returns an error if any database or processing operation fails.
func processPackageFiles(results []*mavenPackageResult, sess *xorm.Session) error {
processedVersion := make(map[string][]*mavenPackageResult)
for _, r := range results {
if r.Snapshot != nil {
key := fmt.Sprintf("%s:%s", r.PackageName, r.PackageVersion.LowerVersion)
processedVersion[key] = append(processedVersion[key], r)
}
// Only update version_id when it differs
if r.PackageVersion.ID != r.PackageFile.VersionID {
pattern := strings.TrimSuffix(r.PackageFile.Name, ".pom") + "%"
// Per routers/api/packages/maven/maven.go:338, POM files already have the `IsLead`, so no update needed for this prop
if _, err := sess.Exec("UPDATE package_file SET version_id = ? WHERE version_id = ? and name like ?", r.PackageVersion.ID, r.PackageFile.VersionID, pattern); err != nil {
return err
}
}
}
// If maven-metadata.xml is missing (snapshot path collision), skip regeneration
// Without this metadata, Maven cannot resolve snapshot details
for _, packageResults := range processedVersion {
sort.Slice(packageResults, func(i, j int) bool {
return packageResults[i].Snapshot.build > packageResults[j].Snapshot.build
})
rs := packageResults[0]
pf, md, err := parseMetadata(sess, rs)
if err != nil {
return err
}
if pf != nil && md != nil && md.GroupID == rs.GroupID && md.ArtifactID == rs.ArtifactID {
if pf.VersionID != rs.PackageFile.VersionID {
if _, err := sess.ID(pf.ID).Cols("version_id").Update(pf); err != nil {
return err
}
}
continue
}
log.Warn("no maven-metadata.xml found for (id: %d) [%s:%s]", rs.PackageVersion.ID, rs.PackageName, rs.PackageVersion.Version)
}
return nil
}
// parseMetadata retrieves metadata for a Maven package file from the database and decodes it into a Metadata object.
// Returns the associated PackageFile, Metadata, and any error encountered during processing.
func parseMetadata(sess *xorm.Session, snapshot *mavenPackageResult) (*packages.PackageFile, *Metadata, error) {
ctx := context.Background()
var pf packages.PackageFile
found, err := sess.Table(pf).
Where("version_id = ?", snapshot.PackageFile.VersionID). // still the old id
And("lower_name = ?", "maven-metadata.xml").
Get(&pf)
if err != nil {
return nil, nil, err
}
if !found {
return nil, nil, nil
}
s, _, _, err := getPackage(ctx, &pf)
if err != nil {
return nil, nil, err
}
defer s.Close()
dec := xml.NewDecoder(s)
var m Metadata
if err := dec.Decode(&m); err != nil {
return nil, nil, err
}
return &pf, &m, nil
}
// processPackageVersions processes Maven package versions by updating metadata or inserting new records as necessary.
// It avoids redundant updates by tracking already processed versions using a map. Returns an error on failure.
func processPackageVersions(results []*mavenPackageResult, sess *xorm.Session) error {
processedVersion := make(map[string]int64)
for _, r := range results {
key := fmt.Sprintf("%s:%s", r.PackageName, r.PackageVersion.Version)
if id, ok := processedVersion[key]; ok {
r.PackageVersion.ID = id
continue
}
// for non collisions, just update the metadata
if r.PackageVersion.PackageID == r.Package.ID {
if _, err := sess.ID(r.PackageVersion.ID).Cols("metadata_json").Update(r.PackageVersion); err != nil {
return err
}
} else {
log.Info("Create new maven package version for %s:%s", r.PackageName, r.PackageVersion.Version)
r.PackageVersion.ID = 0
r.PackageVersion.PackageID = r.Package.ID
if _, err := sess.Insert(r.PackageVersion); err != nil {
return err
}
}
processedVersion[key] = r.PackageVersion.ID
}
return nil
}
// getMavenPackageResultsToUpdate retrieves Maven package results that need updates based on the owner ID.
// It processes POM metadata, fixes package inconsistencies, and filters corrupted package versions.
func getMavenPackageResultsToUpdate(sess *xorm.Session, ownerID *int64) ([]*mavenPackageResult, error) {
ctx := context.Background()
var candidates []*mavenPackageResult
if err := sess.
Table("package_file").
Select("package_file.*, package_version.*, package.*").
Join("INNER", "package_version", "package_version.id = package_file.version_id").
Join("INNER", "package", "package.id = package_version.package_id").
Where("package_file.lower_name LIKE ?", "%.pom").
And("package.type = ?", "maven").
And("package.owner_id = ?", ownerID).
OrderBy("package_version.id DESC, package_file.id DESC").
Find(&candidates); err != nil {
return nil, err
}
var results []*mavenPackageResult
var corruptedVersionIDs []int64
// fetch actual metadata from blob as all packages needs to be fixed following the new string concatenation
for _, r := range candidates {
if err := processPomMetadata(ctx, r); err != nil {
// Skip corrupted versions; admin intervention may be needed to repair these files.
log.Warn("Failed to process package file [id: %d] ignoring package version[%d]: %v", r.PackageFile.ID, r.PackageVersion.ID, err)
corruptedVersionIDs = append(corruptedVersionIDs, r.PackageVersion.ID)
continue
}
results = append(results, r)
log.Debug("Resolved id [%d] from [%s:%s] to [%s:%s] [Snapshot: %v]", r.Package.ID, r.Package.Name, r.PackageVersion.Version, r.PackageName, r.PackageVersion.Version, r.Snapshot)
}
for _, corruptedVersionID := range corruptedVersionIDs {
for i := 0; i < len(results); {
if corruptedVersionID == results[i].PackageVersion.ID {
results = append(results[:i], results[i+1:]...)
} else {
i++
}
}
}
return results, nil
}
// resolvePackageCollisions handles name collisions by keeping the first existing record and inserting new Package records for subsequent collisions.
// Returns a map from PackageName to its resolved Package.ID.
func resolvePackageCollisions(results []*mavenPackageResult, sess *xorm.Session) error {
// Group new names by lowerName
collisions := make(map[string][]string)
for _, r := range results {
names := collisions[r.Package.LowerName]
if !slices.Contains(names, r.PackageName) {
collisions[r.Package.LowerName] = append(names, r.PackageName)
}
}
pkgIDByName := make(map[string]int64)
var err error
for _, r := range results {
list := collisions[r.Package.LowerName]
// update to the upcoming package name which is colon separated
r.Package.Name = r.PackageName
r.Package.LowerName = r.PackageName
// exiting entry
if id, ok := pkgIDByName[r.PackageName]; ok {
r.Package.ID = id
// first package kept the current id
} else if list[0] == r.PackageName {
pkgIDByName[r.PackageName] = r.Package.ID
if _, err = sess.ID(r.Package.ID).Cols("name", "lower_name").Update(r.Package); err != nil {
return err
}
// create a new entry
} else {
log.Info("Create new maven package for %s", r.Package.Name)
r.Package.ID = 0
if _, err = sess.Insert(r.Package); err != nil {
return err
}
pkgIDByName[r.PackageName] = r.Package.ID
}
}
return nil
}
// processPomMetadata processes a Maven package file, parses its POM metadata, and updates PackageVersion information.
func processPomMetadata(ctx context.Context, mpr *mavenPackageResult) error {
s, _, _, err := getPackage(ctx, mpr.PackageFile)
if err != nil {
return fmt.Errorf("unable to get package stream: %v", err)
}
defer s.Close()
actualPom, err := maven.ParsePackageMetaData(s)
if err != nil {
return fmt.Errorf("failed to parse POM metadata: %v", err)
}
raw, err := json.Marshal(actualPom)
if err != nil {
return fmt.Errorf("failed to marshal metadata: %v", err)
}
var currentPom *maven.Metadata
if err = json.Unmarshal([]byte(mpr.PackageVersion.MetadataJSON), &currentPom); err != nil {
return fmt.Errorf("failed to unmarshal metadata: %v", err)
}
// since the rest api can also be (ab)used to upload artifacts wrong, just ignore them
if isInvalidMatch(currentPom, actualPom) {
return fmt.Errorf("artifact mismatch: actual [%s] expected [%s]", actualPom.ArtifactID, currentPom.ArtifactID)
}
// this will also fix packages that missed its groupID
// Ref: https://codeberg.org/forgejo/forgejo/pulls/6329
mpr.PackageVersion.MetadataJSON = string(raw)
// Since Maven packages are case-sensitive, avoid potential clashes and clean-ups
// by enforcing consistent case handling similar to RPM packages.
mpr.PackageName = fmt.Sprintf("%s:%s", actualPom.GroupID, actualPom.ArtifactID)
mpr.GroupID = actualPom.GroupID
mpr.ArtifactID = actualPom.ArtifactID
if strings.HasSuffix(mpr.PackageVersion.Version, "-SNAPSHOT") {
snap, err := extraSnapshotDetails(currentPom, actualPom, mpr)
if err != nil {
return err
}
mpr.Snapshot = snap
} else {
// only snapshots are affected but kept in case of not complete fixtures
expectedFileName := fmt.Sprintf("%s-%s.pom", actualPom.ArtifactID, mpr.PackageVersion.Version)
if mpr.PackageFile.Name != expectedFileName {
log.Warn("invalid package file name - this is a collision which needs to be resolved expected [%s], actual [%s]", expectedFileName, mpr.PackageFile.Name)
}
}
return nil
}
// extraSnapshotDetails extracts detailed snapshot information
// Returns a Snapshot object encapsulating the extracted details or an error if the filename is invalid or parsing fails.
func extraSnapshotDetails(currentPom, actualPom *maven.Metadata, mpr *mavenPackageResult) (*Snapshot, error) {
pattern := `^%s-` +
`(?P<baseVersion>[\d\.]+)-` +
`(?P<date>\d{8})\.` +
`(?P<time>\d{6})-` +
`(?P<build>\d+)\.pom$`
re := regexp.MustCompile(fmt.Sprintf(pattern, regexp.QuoteMeta(currentPom.ArtifactID)))
if re.FindStringSubmatch(mpr.PackageFile.Name) == nil {
log.Warn("invalid package file name - this is a collision which needs to be resolved %s", mpr.PackageFile.Name)
}
re = regexp.MustCompile(fmt.Sprintf(pattern, regexp.QuoteMeta(actualPom.ArtifactID)))
match := re.FindStringSubmatch(mpr.PackageFile.Name)
if match == nil {
return nil, fmt.Errorf("invalid snapshot filename: %s", mpr.PackageFile.Name)
}
baseIdx := re.SubexpIndex("baseVersion")
dateIdx := re.SubexpIndex("date")
timeIdx := re.SubexpIndex("time")
buildIdx := re.SubexpIndex("build")
buildNum, _ := strconv.Atoi(match[buildIdx])
return &Snapshot{
baseVersion: match[baseIdx],
date: match[dateIdx],
time: match[timeIdx],
build: buildNum,
}, nil
}
// isInvalidMatch returns true if the stored metadatas groupID:artifactID
// differs from actual values—accounting for an earlier bug that sometimes omitted the groupID.
func isInvalidMatch(current, actual *maven.Metadata) bool {
bare := fmt.Sprintf("-%s", actual.ArtifactID)
full := fmt.Sprintf("%s-%s", actual.GroupID, actual.ArtifactID)
currentID := fmt.Sprintf("%s-%s", current.GroupID, current.ArtifactID)
return currentID != full && currentID != bare
}

View file

@ -0,0 +1,369 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package forgejo_migrations //nolint:revive
import (
"bytes"
"context"
"io"
"net/url"
"strings"
"testing"
migration_tests "forgejo.org/models/migrations/test"
"forgejo.org/models/packages"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type readSeekCloser struct {
*bytes.Reader
}
func (rsc readSeekCloser) Close() error {
// No resources to close, so we simply provide a no-op implementation.
return nil
}
func StringToReadSeekCloser(s string) io.ReadSeekCloser {
return readSeekCloser{Reader: bytes.NewReader([]byte(s))}
}
func Test_ChangeMavenArtifactConcatenation(t *testing.T) {
getPackage = func(ctx context.Context, pf *packages.PackageFile) (io.ReadSeekCloser, *url.URL, *packages.PackageFile, error) {
var data string
switch pf.BlobID {
case 1:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>com.example</groupId><artifactId>parent-project</artifactId><version>1.0-SNAPSHOT</version></project>`
case 3:
data = `<project><modelVersion>4.0.0</modelVersion><packaging></packaging><parent><groupId>com.example</groupId><artifactId>parent-project</artifactId></parent><groupId></groupId><artifactId>sub-module</artifactId><version>1.0-SNAPSHOT</version></project>`
case 6:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>com.example</groupId><artifactId>parent-project</artifactId><version>7.0.0</version></project>`
case 7:
data = `<project><modelVersion>4.0.0</modelVersion><packaging></packaging><parent><groupId>com.example</groupId><artifactId>parent-project</artifactId></parent><groupId></groupId><artifactId>sub-module</artifactId><version>7.0.0</version></project>`
case 9:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>com.example</groupId><artifactId>parent-project</artifactId><version>7.0.0</version></project>`
case 11:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>foo-</groupId><artifactId>bar</artifactId><version>1.0-SNAPSHOT</version></project>`
case 13:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>foo-</groupId><artifactId>bar</artifactId><version>7.0.0</version></project>`
case 14:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>foo</groupId><artifactId>-bar</artifactId><version>1.0-SNAPSHOT</version></project>`
case 16:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>foo</groupId><artifactId>-bar</artifactId><version>7.0.0</version></project>`
case 20:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>com.example</groupId><artifactId>parent-project</artifactId><version>8.0.0</version></project>`
case 21:
data = `<project><modelVersion>4.0.0</modelVersion><packaging></packaging><parent><groupId>com.example</groupId><artifactId>parent-project</artifactId></parent><groupId></groupId><artifactId>sub-module</artifactId><version>8.0.0</version></project>`
case 23:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>com.example</groupId><artifactId>parent-project</artifactId><version>8.0.0</version></project>`
case 26:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>foo-</groupId><artifactId>bar</artifactId><version>8.0.0</version></project>`
case 28:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>foo</groupId><artifactId>-bar</artifactId><version>8.0.0</version></project>`
case 32:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>com.example</groupId><artifactId>parent-project</artifactId><version>9.0.0</version></project>`
case 33:
data = `<project><modelVersion>4.0.0</modelVersion><packaging></packaging><parent><groupId>com.example</groupId><artifactId>parent-project</artifactId></parent><groupId></groupId><artifactId>sub-module</artifactId><version>9.0.0</version></project>`
case 35:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>com.example</groupId><artifactId>parent-project</artifactId><version>9.0.0</version></project>`
case 38:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>foo-</groupId><artifactId>bar</artifactId><version>9.0.0</version></project>`
case 40:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>foo</groupId><artifactId>-bar</artifactId><version>9.0.0</version></project>`
case 44:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>com.example</groupId><artifactId>parent-project</artifactId><version>10.0.0</version></project>`
case 45:
data = `<project><modelVersion>4.0.0</modelVersion><packaging></packaging><parent><groupId>com.example</groupId><artifactId>parent-project</artifactId></parent><groupId></groupId><artifactId>sub-module</artifactId><version>10.0.0</version></project>`
case 47:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>com.example</groupId><artifactId>parent-project</artifactId><version>10.0.0</version></project>`
case 50:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>foo-</groupId><artifactId>bar</artifactId><version>10.0.0</version></project>`
case 52:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>foo</groupId><artifactId>-bar</artifactId><version>10.0.0</version></project>`
case 56:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>com.example</groupId><artifactId>parent-project</artifactId><version>11.0.0</version></project>`
case 57:
data = `<project><modelVersion>4.0.0</modelVersion><packaging></packaging><parent><groupId>com.example</groupId><artifactId>parent-project</artifactId></parent><groupId></groupId><artifactId>sub-module</artifactId><version>11.0.0</version></project>`
case 59:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>com.example</groupId><artifactId>parent-project</artifactId><version>11.0.0</version></project>`
case 62:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>foo-</groupId><artifactId>bar</artifactId><version>11.0.0</version></project>`
case 64:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>foo</groupId><artifactId>-bar</artifactId><version>11.0.0</version></project>`
case 66:
data = `<project><modelVersion>4.0.0</modelVersion><packaging></packaging><parent><groupId>com.broken</groupId><artifactId>br-parent</artifactId></parent><groupId></groupId><artifactId>br-rest-webmvc</artifactId><version></version></project>`
case 68:
data = `<project><modelVersion>4.0.0</modelVersion><packaging></packaging><parent><groupId>com.broken</groupId><artifactId>br-parent</artifactId></parent><groupId></groupId><artifactId>br-openapi-base</artifactId><version></version></project>`
case 72:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><parent><groupId>de.loosetie</groupId><artifactId>lt-parent-kotlin</artifactId></parent><groupId>com.broken</groupId><artifactId>br-root</artifactId><version>1.2.4</version></project>`
case 74:
data = `<project><modelVersion>4.0.0</modelVersion><packaging></packaging><parent><groupId>com.broken</groupId><artifactId>br-parent</artifactId></parent><groupId></groupId><artifactId>br-repo-jooq</artifactId><version></version></project>`
case 76:
data = `<project><modelVersion>4.0.0</modelVersion><packaging></packaging><parent><groupId>com.broken</groupId><artifactId>br-parent</artifactId></parent><groupId></groupId><artifactId>br-repo-in-memory</artifactId><version></version></project>`
case 78:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><parent><groupId>com.broken</groupId><artifactId>br-root</artifactId></parent><groupId></groupId><artifactId>br-parent</artifactId><version></version></project>`
case 79:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>group</groupId><artifactId>bar-art</artifactId><version>11.0.0</version></project>`
case 80:
data = `<project><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><groupId>group-bar</groupId><artifactId>art</artifactId><version>11.0.0</version></project>`
case 55:
data = `<?xml version="1.0" encoding="UTF-8"?><metadata modelVersion="1.1.0"><groupId>com.example</groupId><artifactId>sub-module</artifactId><version>1.0-SNAPSHOT</version></metadata>`
case 53:
data = `<?xml version="1.0" encoding="UTF-8"?><metadata modelVersion="1.1.0"><groupId>com.example</groupId><artifactId>parent-project</artifactId><version>1.0-SNAPSHOT</version></metadata>`
case 63:
data = `<?xml version="1.0" encoding="UTF-8"?><metadata modelVersion="1.1.0"><groupId>foo</groupId><artifactId>-bar</artifactId><version>1.0-SNAPSHOT</version></metadata>`
default:
t.Fatalf("Unknown package file type: %d", pf.BlobID)
}
return StringToReadSeekCloser(data), nil, nil, nil
}
x, deferable := migration_tests.PrepareTestEnv(t, 0, new(packages.Package), new(packages.PackageFile), new(packages.PackageVersion), new(packages.PackageBlob))
defer deferable()
if x == nil || t.Failed() {
return
}
cnt, err := x.Table("package").Count()
require.NoError(t, err)
assert.EqualValues(t, 8, cnt)
cnt, err = x.Table("package_file").Count()
require.NoError(t, err)
assert.EqualValues(t, 87, cnt)
cnt, err = x.Table("package_version").Count()
require.NoError(t, err)
assert.EqualValues(t, 31, cnt)
cnt, err = x.Table("package_blob").Count()
require.NoError(t, err)
assert.EqualValues(t, 80, cnt)
require.NoError(t, ChangeMavenArtifactConcatenation(x))
var pks []*packages.Package
require.NoError(t, x.OrderBy("id").Find(&pks))
validatePackages(t, pks)
var pvs []*packages.PackageVersion
require.NoError(t, x.OrderBy("id").Find(&pvs))
validatePackageVersions(t, pvs)
var pfs []*packages.PackageFile
require.NoError(t, x.OrderBy("id").Find(&pfs))
validatePackageFiles(t, pfs)
}
func validatePackages(t *testing.T, pbs []*packages.Package) {
assertPackage := func(id, ownerID, repoID int64, name string) {
pb := pbs[id-1]
require.Equal(t, id, pb.ID)
require.Equal(t, ownerID, pb.OwnerID)
require.Equal(t, repoID, pb.RepoID)
require.Equal(t, name, pb.Name)
require.Equal(t, name, pb.LowerName)
require.Equal(t, packages.TypeMaven, pb.Type)
}
require.Len(t, pbs, 10)
assertPackage(1, 2, 0, "com.example:parent-project")
assertPackage(2, 2, 0, "com.example:sub-module")
assertPackage(3, 1, 0, "com.example:parent-project")
assertPackage(4, 1, 0, "com.example:sub-module")
assertPackage(5, 1, 0, "foo:-bar")
assertPackage(6, 8, 54, "com.broken:br-rest-webmvc")
// broken poms completely ignored as it is impossible to look up the correct metadata
assertPackage(7, 8, 54, "com.broken-br-openapi-base")
assertPackage(8, 1, 0, "group-bar:art")
// new created entries
assertPackage(9, 1, 0, "group:bar-art")
assertPackage(10, 1, 0, "foo-:bar")
}
func validatePackageVersions(t *testing.T, pvs []*packages.PackageVersion) {
require.Len(t, pvs, 38)
assertPackageVersion := func(id, packageId, creatorId, createdUnix int64, version, metadata string) {
pv := pvs[id-1]
require.Equal(t, id, pv.ID)
require.Equal(t, packageId, pv.PackageID)
require.Equal(t, creatorId, pv.CreatorID)
require.Equal(t, version, pv.Version)
require.Equal(t, strings.ToLower(version), pv.LowerVersion)
require.JSONEq(t, metadata, pv.MetadataJSON)
}
assertPackageVersion(1, 1, 1, 1746256357, "1.0-SNAPSHOT", `{"artifact_id":"parent-project","group_id":"com.example"}`)
assertPackageVersion(2, 2, 1, 1746256358, "1.0-SNAPSHOT", `{"artifact_id":"sub-module","group_id":"com.example"}`)
assertPackageVersion(3, 1, 1, 1746256360, "7.0.0", `{"artifact_id":"parent-project","group_id":"com.example"}`)
// added groupId
assertPackageVersion(4, 2, 1, 1746256361, "7.0.0", `{"artifact_id":"sub-module","group_id":"com.example"}`)
assertPackageVersion(5, 3, 1, 1746256364, "7.0.0", `{"artifact_id":"parent-project","group_id":"com.example"}`)
// added groupId
assertPackageVersion(6, 4, 1, 1746256365, "7.0.0", `{"artifact_id":"sub-module","group_id":"com.example"}`)
assertPackageVersion(7, 5, 1, 1746256367, "1.0-SNAPSHOT", `{"artifact_id":"-bar","group_id":"foo"}`)
assertPackageVersion(8, 5, 1, 1746256370, "7.0.0", `{"artifact_id":"-bar","group_id":"foo"}`)
assertPackageVersion(9, 1, 1, 1746256389, "8.0.0", `{"artifact_id":"parent-project","group_id":"com.example"}`)
// added groupId
assertPackageVersion(10, 2, 1, 1746256390, "8.0.0", `{"artifact_id":"sub-module","group_id":"com.example"}`)
assertPackageVersion(11, 3, 1, 1746256393, "8.0.0", `{"artifact_id":"parent-project","group_id":"com.example"}`)
// added groupId
assertPackageVersion(12, 4, 1, 1746256394, "8.0.0", `{"artifact_id":"sub-module","group_id":"com.example"}`)
assertPackageVersion(13, 5, 1, 1746256399, "8.0.0", `{"artifact_id":"-bar","group_id":"foo"}`)
assertPackageVersion(14, 1, 1, 1746256419, "9.0.0", `{"artifact_id":"parent-project","group_id":"com.example"}`)
// added groupId
assertPackageVersion(15, 2, 1, 1746256420, "9.0.0", `{"artifact_id":"sub-module","group_id":"com.example"}`)
assertPackageVersion(16, 3, 1, 1746256423, "9.0.0", `{"artifact_id":"parent-project","group_id":"com.example"}`)
// added groupId
assertPackageVersion(17, 4, 1, 1746256424, "9.0.0", `{"artifact_id":"sub-module","group_id":"com.example"}`)
assertPackageVersion(18, 5, 1, 1746256429, "9.0.0", `{"artifact_id":"-bar","group_id":"foo"}`)
assertPackageVersion(19, 1, 1, 1746256449, "10.0.0", `{"artifact_id":"parent-project","group_id":"com.example"}`)
assertPackageVersion(20, 2, 1, 1746256450, "10.0.0", `{"artifact_id":"sub-module","group_id":"com.example"}`)
assertPackageVersion(21, 3, 1, 1746256452, "10.0.0", `{"artifact_id":"parent-project","group_id":"com.example"}`)
assertPackageVersion(22, 4, 1, 1746256453, "10.0.0", `{"artifact_id":"sub-module","group_id":"com.example"}`)
assertPackageVersion(23, 5, 1, 1746256459, "10.0.0", `{"artifact_id":"-bar","group_id":"foo"}`)
assertPackageVersion(24, 1, 1, 1746256478, "11.0.0", `{"artifact_id":"parent-project","group_id":"com.example"}`)
assertPackageVersion(25, 2, 1, 1746256479, "11.0.0", `{"artifact_id":"sub-module","group_id":"com.example"}`)
assertPackageVersion(26, 3, 1, 1746256482, "11.0.0", `{"artifact_id":"parent-project","group_id":"com.example"}`)
assertPackageVersion(27, 4, 1, 1746256483, "11.0.0", `{"artifact_id":"sub-module","group_id":"com.example"}`)
assertPackageVersion(28, 5, 1, 1746256488, "11.0.0", `{"artifact_id":"-bar","group_id":"foo"}`)
// should be untouched at all. fixtures doesn't contain names
assertPackageVersion(29, 7, 6, 1746256488, "1.2.4", `{"group_id":"com.broken","artifact_id":"br-root","name":"Foo"}`)
// added group name
assertPackageVersion(30, 6, 6, 1746256488, "1.2.4", `{"artifact_id":"br-rest-webmvc", "group_id":"com.broken"}`)
assertPackageVersion(31, 8, 1, 1746256488, "11.0.0", `{"artifact_id":"art","group_id":"group-bar"}`)
// new entries
assertPackageVersion(32, 9, 1, 1746256488, "11.0.0", `{"artifact_id":"bar-art","group_id":"group"}`)
assertPackageVersion(33, 10, 1, 1746256488, "11.0.0", `{"artifact_id":"bar","group_id":"foo-"}`)
assertPackageVersion(34, 10, 1, 1746256488, "10.0.0", `{"artifact_id":"bar","group_id":"foo-"}`)
assertPackageVersion(35, 10, 1, 1746256488, "9.0.0", `{"artifact_id":"bar","group_id":"foo-"}`)
assertPackageVersion(36, 10, 1, 1746256488, "8.0.0", `{"artifact_id":"bar","group_id":"foo-"}`)
assertPackageVersion(37, 10, 1, 1746256488, "7.0.0", `{"artifact_id":"bar","group_id":"foo-"}`)
assertPackageVersion(38, 10, 1, 1746256488, "1.0-SNAPSHOT", `{"artifact_id":"bar","group_id":"foo-"}`)
}
func validatePackageFiles(t *testing.T, pfs []*packages.PackageFile) {
assertPackageVersion := func(pos, id, versionId, blobId, createdUnix int64, name string, isLead bool) {
pf := pfs[pos]
require.Equal(t, id, pf.ID)
require.Equal(t, versionId, pf.VersionID)
require.Equal(t, blobId, pf.BlobID)
require.Equal(t, name, pf.Name)
require.Equal(t, strings.ToLower(name), pf.LowerName)
require.Empty(t, pf.CompositeKey)
require.Equal(t, isLead, pf.IsLead)
require.EqualValues(t, createdUnix, pf.CreatedUnix)
require.Empty(t, pf.CompositeKey)
}
assertPackageVersion(0, 1, 1, 1, 1746256357, "parent-project-1.0-20250503.071237-1.pom", true)
assertPackageVersion(1, 3, 2, 3, 1746256358, "sub-module-1.0-20250503.071237-1.pom", true)
assertPackageVersion(2, 4, 2, 4, 1746256358, "sub-module-1.0-20250503.071237-1.jar", false)
assertPackageVersion(3, 6, 3, 6, 1746256360, "parent-project-7.0.0.pom", true)
assertPackageVersion(4, 7, 4, 7, 1746256361, "sub-module-7.0.0.pom", true)
assertPackageVersion(5, 8, 4, 8, 1746256361, "sub-module-7.0.0.jar", false)
assertPackageVersion(6, 9, 5, 9, 1746256364, "parent-project-7.0.0.pom", true)
assertPackageVersion(7, 10, 6, 7, 1746256365, "sub-module-7.0.0.pom", true)
assertPackageVersion(8, 11, 6, 10, 1746256365, "sub-module-7.0.0.jar", false)
// new versionId 7 -> 38
assertPackageVersion(9, 12, 38, 11, 1746256367, "bar-1.0-20250503.071248-1.pom", true)
// new versionId 37
assertPackageVersion(10, 14, 37, 13, 1746256370, "bar-7.0.0.pom", true)
assertPackageVersion(11, 15, 7, 14, 1746256373, "-bar-1.0-20250503.071253-2.pom", true)
assertPackageVersion(12, 17, 8, 16, 1746256375, "-bar-7.0.0.pom", true)
assertPackageVersion(13, 18, 1, 1, 1746256385, "parent-project-1.0-20250503.071306-2.pom", true)
assertPackageVersion(14, 20, 2, 3, 1746256386, "sub-module-1.0-20250503.071306-2.pom", true)
assertPackageVersion(15, 21, 2, 18, 1746256386, "sub-module-1.0-20250503.071306-2.jar", false)
assertPackageVersion(16, 23, 9, 20, 1746256389, "parent-project-8.0.0.pom", true)
assertPackageVersion(17, 24, 10, 21, 1746256390, "sub-module-8.0.0.pom", true)
assertPackageVersion(18, 25, 10, 22, 1746256390, "sub-module-8.0.0.jar", false)
assertPackageVersion(19, 26, 11, 23, 1746256393, "parent-project-8.0.0.pom", true)
assertPackageVersion(20, 27, 12, 21, 1746256394, "sub-module-8.0.0.pom", true)
assertPackageVersion(21, 28, 12, 24, 1746256394, "sub-module-8.0.0.jar", false)
// new versionId 7 -> 38
assertPackageVersion(22, 29, 38, 11, 1746256397, "bar-1.0-20250503.071317-3.pom", true)
assertPackageVersion(23, 31, 36, 26, 1746256399, "bar-8.0.0.pom", true)
assertPackageVersion(24, 32, 7, 14, 1746256402, "-bar-1.0-20250503.071323-4.pom", true)
assertPackageVersion(25, 34, 13, 28, 1746256405, "-bar-8.0.0.pom", true)
assertPackageVersion(26, 35, 1, 1, 1746256415, "parent-project-1.0-20250503.071335-3.pom", true)
assertPackageVersion(27, 37, 2, 3, 1746256416, "sub-module-1.0-20250503.071335-3.pom", true)
assertPackageVersion(28, 38, 2, 30, 1746256416, "sub-module-1.0-20250503.071335-3.jar", false)
assertPackageVersion(29, 40, 14, 32, 1746256419, "parent-project-9.0.0.pom", true)
assertPackageVersion(30, 41, 15, 33, 1746256420, "sub-module-9.0.0.pom", true)
assertPackageVersion(31, 42, 15, 34, 1746256420, "sub-module-9.0.0.jar", false)
assertPackageVersion(32, 43, 16, 35, 1746256423, "parent-project-9.0.0.pom", true)
assertPackageVersion(33, 44, 17, 33, 1746256424, "sub-module-9.0.0.pom", true)
assertPackageVersion(34, 45, 17, 36, 1746256424, "sub-module-9.0.0.jar", false)
// new versionId 7 -> 38
assertPackageVersion(35, 46, 38, 11, 1746256427, "bar-1.0-20250503.071347-5.pom", true)
// new versionId 18 -> 35
assertPackageVersion(36, 48, 35, 38, 1746256429, "bar-9.0.0.pom", true)
assertPackageVersion(37, 49, 7, 14, 1746256432, "-bar-1.0-20250503.071353-6.pom", true)
assertPackageVersion(38, 51, 18, 40, 1746256435, "-bar-9.0.0.pom", true)
assertPackageVersion(39, 52, 1, 1, 1746256445, "parent-project-1.0-20250503.071405-4.pom", true)
assertPackageVersion(40, 54, 2, 3, 1746256446, "sub-module-1.0-20250503.071405-4.pom", true)
assertPackageVersion(41, 55, 2, 42, 1746256446, "sub-module-1.0-20250503.071405-4.jar", false)
assertPackageVersion(42, 57, 19, 44, 1746256449, "parent-project-10.0.0.pom", true)
assertPackageVersion(43, 58, 20, 45, 1746256450, "sub-module-10.0.0.pom", true)
assertPackageVersion(44, 59, 20, 46, 1746256450, "sub-module-10.0.0.jar", false)
assertPackageVersion(45, 60, 21, 47, 1746256452, "parent-project-10.0.0.pom", true)
assertPackageVersion(46, 61, 22, 45, 1746256453, "sub-module-10.0.0.pom", true)
assertPackageVersion(47, 62, 22, 48, 1746256453, "sub-module-10.0.0.jar", false)
// new versionId 7 -> 38
assertPackageVersion(48, 63, 38, 11, 1746256456, "bar-1.0-20250503.071416-7.pom", true)
// new versionId 34
assertPackageVersion(49, 65, 34, 50, 1746256459, "bar-10.0.0.pom", true)
assertPackageVersion(50, 66, 7, 14, 1746256461, "-bar-1.0-20250503.071422-8.pom", true)
assertPackageVersion(51, 68, 23, 52, 1746256464, "-bar-10.0.0.pom", true)
assertPackageVersion(52, 69, 1, 1, 1746256474, "parent-project-1.0-20250503.071435-5.pom", true)
assertPackageVersion(53, 70, 1, 53, 1746256474, "maven-metadata.xml", false)
assertPackageVersion(54, 71, 2, 3, 1746256475, "sub-module-1.0-20250503.071435-5.pom", true)
assertPackageVersion(55, 72, 2, 54, 1746256475, "sub-module-1.0-20250503.071435-5.jar", false)
assertPackageVersion(56, 73, 2, 55, 1746256476, "maven-metadata.xml", false)
assertPackageVersion(57, 74, 24, 56, 1746256478, "parent-project-11.0.0.pom", true)
assertPackageVersion(58, 75, 25, 57, 1746256479, "sub-module-11.0.0.pom", true)
assertPackageVersion(59, 76, 25, 58, 1746256479, "sub-module-11.0.0.jar", false)
assertPackageVersion(60, 77, 26, 59, 1746256482, "parent-project-11.0.0.pom", true)
assertPackageVersion(61, 78, 27, 57, 1746256483, "sub-module-11.0.0.pom", true)
assertPackageVersion(62, 79, 27, 60, 1746256483, "sub-module-11.0.0.jar", false)
// new versionId 7 -> 38
assertPackageVersion(63, 80, 38, 11, 1746256486, "bar-1.0-20250503.071446-9.pom", true)
// new versionId 33
assertPackageVersion(64, 82, 33, 62, 1746256488, "bar-11.0.0.pom", true)
assertPackageVersion(65, 83, 7, 14, 1746256491, "-bar-1.0-20250503.071451-10.pom", true)
assertPackageVersion(66, 84, 7, 63, 1746256491, "maven-metadata.xml", false)
assertPackageVersion(67, 85, 28, 64, 1746256494, "-bar-11.0.0.pom", true)
assertPackageVersion(68, 86, 29, 75, 174625649444986, "br-repo-jooq-1.2.4-sources.jar", false)
assertPackageVersion(69, 87, 29, 65, 174625649446161, "br-rest-webmvc-1.2.4.jar", false)
assertPackageVersion(70, 88, 29, 68, 174625649444734, "br-openapi-base-1.2.4.pom", true)
assertPackageVersion(71, 89, 29, 69, 174625649444746, "br-openapi-base-1.2.4.jar", false)
assertPackageVersion(72, 90, 29, 70, 174625649444775, "br-openapi-base-1.2.4-sources.jar", false)
assertPackageVersion(73, 91, 29, 78, 174625649444852, "br-parent-1.2.4.pom", true)
assertPackageVersion(74, 92, 29, 76, 174625649444900, "br-repo-in-memory-1.2.4.pom", true)
assertPackageVersion(75, 93, 29, 73, 174625649444911, "br-repo-in-memory-1.2.4.jar", false)
assertPackageVersion(76, 94, 29, 77, 174625649444922, "br-repo-in-memory-1.2.4-sources.jar", false)
assertPackageVersion(77, 95, 29, 74, 174625649444953, "br-repo-jooq-1.2.4.pom", true)
assertPackageVersion(78, 96, 29, 67, 174625649444969, "br-repo-jooq-1.2.4.jar", false)
assertPackageVersion(79, 97, 29, 71, 174625649446161, "br-rest-webmvc-1.2.4-sources.jar", false)
assertPackageVersion(80, 98, 29, 66, 174625649446195, "br-rest-webmvc-1.2.4.pom", true)
assertPackageVersion(81, 99, 29, 72, 174625649446217, "br-root-1.2.4.pom", true)
assertPackageVersion(82, 100, 30, 66, 174625649446311, "br-rest-webmvc-1.2.4.pom", true)
assertPackageVersion(83, 101, 30, 65, 174625649446312, "br-rest-webmvc-1.2.4.jar", false)
assertPackageVersion(84, 102, 30, 71, 174625649446312, "br-rest-webmvc-1.2.4-sources.jar", false)
// new versionId 31 -> 32
assertPackageVersion(85, 103, 32, 79, 1746280832, "bar-art-11.0.0.pom", true)
assertPackageVersion(86, 104, 31, 80, 1746280843, "art-11.0.0.pom", true)
}

View file

@ -1,6 +1,6 @@
// Copyright 2018 The Gitea Authors.
// Copyright 2016 The Gogs Authors.
// All rights reserved.
// Copyright 2016 The Gogs Authors. All rights reserved.
// Copyright 2018 The Gitea Authors. All rights reserved.
// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package issues
@ -324,6 +324,9 @@ type Comment struct {
NewCommit string `xorm:"-"`
CommitsNum int64 `xorm:"-"`
IsForcePush bool `xorm:"-"`
// If you add new fields that might be used to store abusive content (mainly string fields),
// please also add them in the CommentData struct and the corresponding constructor.
}
func init() {
@ -649,8 +652,11 @@ func (c *Comment) LoadAssigneeUserAndTeam(ctx context.Context) error {
if c.Issue.Repo.Owner.IsOrganization() {
c.AssigneeTeam, err = organization.GetTeamByID(ctx, c.AssigneeTeamID)
if err != nil && !organization.IsErrTeamNotExist(err) {
return err
if err != nil {
if !organization.IsErrTeamNotExist(err) {
return err
}
c.AssigneeTeam = organization.NewGhostTeam()
}
}
}
@ -1149,6 +1155,11 @@ func UpdateComment(ctx context.Context, c *Comment, contentVersion int, doer *us
}
defer committer.Close()
// If the comment was reported as abusive, a shadow copy should be created before first update.
if err := IfNeededCreateShadowCopyForComment(ctx, c); err != nil {
return err
}
if err := c.LoadIssue(ctx); err != nil {
return err
}
@ -1184,6 +1195,12 @@ func UpdateComment(ctx context.Context, c *Comment, contentVersion int, doer *us
// DeleteComment deletes the comment
func DeleteComment(ctx context.Context, comment *Comment) error {
e := db.GetEngine(ctx)
// If the comment was reported as abusive, a shadow copy should be created before deletion.
if err := IfNeededCreateShadowCopyForComment(ctx, comment); err != nil {
return err
}
if _, err := e.ID(comment.ID).NoAutoCondition().Delete(comment); err != nil {
return err
}

View file

@ -6,6 +6,7 @@ package issues
import (
"context"
"errors"
"fmt"
"html/template"
"regexp"
@ -822,7 +823,7 @@ func (issue *Issue) MovePin(ctx context.Context, newPosition int) error {
}
if newPosition < 1 {
return fmt.Errorf("The Position can't be lower than 1")
return errors.New("The Position can't be lower than 1")
}
dbctx, committer, err := db.TxContext(ctx)

View file

@ -1,10 +1,12 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package issues
import (
"context"
"errors"
"fmt"
"strings"
@ -275,6 +277,11 @@ func ChangeIssueContent(ctx context.Context, issue *Issue, doer *user_model.User
}
}
// If the issue was reported as abusive, a shadow copy should be created before first update.
if err := IfNeededCreateShadowCopyForIssue(ctx, issue); err != nil {
return err
}
issue.Content = content
issue.ContentVersion = contentVersion + 1
@ -332,10 +339,10 @@ func NewIssueWithIndex(ctx context.Context, doer *user_model.User, opts NewIssue
}
if opts.Issue.Index <= 0 {
return fmt.Errorf("no issue index provided")
return errors.New("no issue index provided")
}
if opts.Issue.ID > 0 {
return fmt.Errorf("issue exist")
return errors.New("issue exist")
}
opts.Issue.Created = timeutil.TimeStampNanoNow()

106
models/issues/moderation.go Normal file
View file

@ -0,0 +1,106 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package issues
import (
"context"
"forgejo.org/models/moderation"
"forgejo.org/modules/json"
"forgejo.org/modules/timeutil"
)
// IssueData represents a trimmed down issue that is used for preserving
// only the fields needed for abusive content reports (mainly string fields).
type IssueData struct {
RepoID int64
Index int64
PosterID int64
Title string
Content string
ContentVersion int
CreatedUnix timeutil.TimeStamp
UpdatedUnix timeutil.TimeStamp
}
// newIssueData creates a trimmed down issue to be used just to create a JSON structure
// (keeping only the fields relevant for moderation purposes)
func newIssueData(issue *Issue) IssueData {
return IssueData{
RepoID: issue.RepoID,
Index: issue.Index,
PosterID: issue.PosterID,
Content: issue.Content,
Title: issue.Title,
ContentVersion: issue.ContentVersion,
CreatedUnix: issue.CreatedUnix,
UpdatedUnix: issue.UpdatedUnix,
}
}
// CommentData represents a trimmed down comment that is used for preserving
// only the fields needed for abusive content reports (mainly string fields).
type CommentData struct {
PosterID int64
IssueID int64
Content string
ContentVersion int
CreatedUnix timeutil.TimeStamp
UpdatedUnix timeutil.TimeStamp
}
// newCommentData creates a trimmed down comment to be used just to create a JSON structure
// (keeping only the fields relevant for moderation purposes)
func newCommentData(comment *Comment) CommentData {
return CommentData{
PosterID: comment.PosterID,
IssueID: comment.IssueID,
Content: comment.Content,
ContentVersion: comment.ContentVersion,
CreatedUnix: comment.CreatedUnix,
UpdatedUnix: comment.UpdatedUnix,
}
}
// IfNeededCreateShadowCopyForIssue checks if for the given issue there are any reports of abusive content submitted
// and if found a shadow copy of relevant issue fields will be stored into DB and linked to the above report(s).
// This function should be called before a issue is deleted or updated.
func IfNeededCreateShadowCopyForIssue(ctx context.Context, issue *Issue) error {
shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeIssue, issue.ID)
if err != nil {
return err
}
if shadowCopyNeeded {
issueData := newIssueData(issue)
content, err := json.Marshal(issueData)
if err != nil {
return err
}
return moderation.CreateShadowCopyForIssue(ctx, issue.ID, string(content))
}
return nil
}
// IfNeededCreateShadowCopyForComment checks if for the given comment there are any reports of abusive content submitted
// and if found a shadow copy of relevant comment fields will be stored into DB and linked to the above report(s).
// This function should be called before a comment is deleted or updated.
func IfNeededCreateShadowCopyForComment(ctx context.Context, comment *Comment) error {
shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeComment, comment.ID)
if err != nil {
return err
}
if shadowCopyNeeded {
commentData := newCommentData(comment)
content, err := json.Marshal(commentData)
if err != nil {
return err
}
return moderation.CreateShadowCopyForComment(ctx, comment.ID, string(content))
}
return nil
}

View file

@ -6,6 +6,7 @@ package issues
import (
"context"
"errors"
"fmt"
"io"
"regexp"
@ -795,7 +796,7 @@ func (pr *PullRequest) GetWorkInProgressPrefix(ctx context.Context) string {
// UpdateCommitDivergence update Divergence of a pull request
func (pr *PullRequest) UpdateCommitDivergence(ctx context.Context, ahead, behind int) error {
if pr.ID == 0 {
return fmt.Errorf("pull ID is 0")
return errors.New("pull ID is 0")
}
pr.CommitsAhead = ahead
pr.CommitsBehind = behind

View file

@ -5,6 +5,7 @@ package issues
import (
"context"
"errors"
"fmt"
"slices"
"strings"
@ -349,7 +350,7 @@ func CreateReview(ctx context.Context, opts CreateReviewOptions) (*Review, error
review.Type = ReviewTypeRequest
review.ReviewerTeamID = opts.ReviewerTeam.ID
} else {
return nil, fmt.Errorf("provide either reviewer or reviewer team")
return nil, errors.New("provide either reviewer or reviewer team")
}
if _, err := sess.Insert(review); err != nil {
@ -908,7 +909,7 @@ func MarkConversation(ctx context.Context, comment *Comment, doer *user_model.Us
// the PR writer , offfcial reviewer and poster can do it
func CanMarkConversation(ctx context.Context, issue *Issue, doer *user_model.User) (permResult bool, err error) {
if doer == nil || issue == nil {
return false, fmt.Errorf("issue or doer is nil")
return false, errors.New("issue or doer is nil")
}
if doer.ID != issue.PosterID {
@ -945,11 +946,11 @@ func DeleteReview(ctx context.Context, r *Review) error {
defer committer.Close()
if r.ID == 0 {
return fmt.Errorf("review is not allowed to be 0")
return errors.New("review is not allowed to be 0")
}
if r.Type == ReviewTypeRequest {
return fmt.Errorf("review request can not be deleted using this method")
return errors.New("review request can not be deleted using this method")
}
opts := FindCommentsOptions{

View file

@ -74,7 +74,7 @@ func RecreateTable(sess *xorm.Session, bean any) error {
}
newTableColumns := table.Columns()
if len(newTableColumns) == 0 {
return fmt.Errorf("no columns in new table")
return errors.New("no columns in new table")
}
hasID := false
for _, column := range newTableColumns {

View file

@ -0,0 +1,66 @@
- id: 1
owner_id: 2
repo_id: 0
type: maven
name: com.example-parent-project
lower_name: com.example-parent-project
semver_compatible: 0
is_internal: 0
- id: 2
owner_id: 2
repo_id: 0
type: maven
name: com.example-sub-module
lower_name: com.example-sub-module
semver_compatible: 0
is_internal: 0
- id: 3
owner_id: 1
repo_id: 0
type: maven
name: com.example-parent-project
lower_name: com.example-parent-project
semver_compatible: 0
is_internal: 0
- id: 4
owner_id: 1
repo_id: 0
type: maven
name: com.example-sub-module
lower_name: com.example-sub-module
semver_compatible: 0
is_internal: 0
- id: 5
owner_id: 1
repo_id: 0
type: maven
name: foo--bar
lower_name: foo--bar
semver_compatible: 0
is_internal: 0
# broken uploads
- id: 6
owner_id: 8
repo_id: 54
type: maven
name: com.broken-br-rest-webmvc
lower_name: com.broken-br-rest-webmvc
semver_compatible: 0
is_internal: 0
- id: 7
owner_id: 8
repo_id: 54
type: maven
name: com.broken-br-openapi-base
lower_name: com.broken-br-openapi-base
semver_compatible: 0
is_internal: 0
# collision
- id: 8
owner_id: 1
repo_id: 0
type: maven
name: group-bar-art
lower_name: group-bar-art
semver_compatible: 0
is_internal: 0

View file

@ -0,0 +1,641 @@
- id: 1
size: 1038
hash_md5: 6096f13928b6de3103a2bd4857fcf1fa
hash_sha1: bbdfeca76d178834b5750cd8d14b8a698847c554
hash_sha256: 82d2245520562132935dc1d0caa181df125587917de9f14ed9dfeb2f2f88f0f0
hash_sha512: 898896789a69a87252b44e13c253ecadfbe04867c8319931eccddf4fef57b04b066061792b515c17d4480e3bbafc82590781a0c3fd43c6a64abe0e5cff05e057
created_unix: 1746256357
hash_blake2b: null
- id: 2
size: 598
hash_md5: 390df8dad491a0aba1b5098801735d5b
hash_sha1: e96e2d1909a9dc88f6a34cdd2e34e2308d9a0283
hash_sha256: a292499ce09f7ec89fbcf97d7816dd7f9c90e3deb0163be5704a28e3eb0dc819
hash_sha512: 8ddb3201adbdb4bdf97d27f563db097a032f910fefcf64de1b09d533aaa44affb8622b037143ea620df1010bdba56e5206dff6bd9de4450e43ba214ac2bf67d9
created_unix: 1746256357
hash_blake2b: null
- id: 3
size: 763
hash_md5: bb4f70724a5c4197d91f9bf1e36e49c3
hash_sha1: 6fd741578f83b0f1d10dbc0a53f339e790d93c16
hash_sha256: e6e07ed923d3c316d4174449be423171bc46a8c6dad0c864d3d2b916eda5bb5f
hash_sha512: 773336c44e8cd5aa92c3b3c921418392661ae5a4c51305341e2766d39ad978e3dfdf4f4992bbcc88bdce3fb824b6a75b1e86a161211940d55e6a4715dab924c6
created_unix: 1746256358
hash_blake2b: null
- id: 4
size: 2099
hash_md5: 72dbbe63f0c475783e53cc8760183dbc
hash_sha1: c1f6bff52587f6cc11dd26549873e5dd4bfdf4df
hash_sha256: d79571250439111a13090e69b5dd5e3ba02f980fcf6aca46943f854a0762145f
hash_sha512: bda94c619b66d9c01e75385b2e77be630b38e70d8a4163894c059550d9c6bd8300656f9dbf6c81c06112061b18c4bdc32102019ed1dd151556bd02ba433175d4
created_unix: 1746256358
hash_blake2b: null
- id: 5
size: 765
hash_md5: dba88ff4468713b2de10b2151115a3cd
hash_sha1: f2ba30eda8fd818a8e435a720c242acd77db4498
hash_sha256: 5fc713fca3c8daeef084fc9bebc08c0dc49dfccbbd6da774180f6b57dab2a0b1
hash_sha512: 5b494fffce128d83a5a5891c2615709efa8ed2ab7bf557ba33c4d6cc4cc2386af11e74e0db28c162bb402a6e9cdfd270f2dadb033832e9cf313c6ac5ee5493b9
created_unix: 1746256358
hash_blake2b: null
- id: 6
size: 1031
hash_md5: 32aec2cc3a9369dbd7fd062267ddc0de
hash_sha1: 611b0d7b0e10574a87ca3fee6172f3b77b3fc824
hash_sha256: d53eaadc924150a5e5b7be4a0fdbbf20d2c996b01bb01f4388570815166fbabd
hash_sha512: f1b516945b8faf99e4943171c9b149114753b7cbe53707f901c7fa07f810662a8e2fe1a9dd4a7b1ee8989c4c26499c3e4ce5e233cadf5ee897dc5b4839353071
created_unix: 1746256360
hash_blake2b: null
- id: 7
size: 749
hash_md5: dadab7af17fffd4ba67b2161f0573840
hash_sha1: 8438861bf54c263273cfb3d85bd4aa060d983cfe
hash_sha256: 28acf9ad32b66ee2e02fe58e6c61aafd12fc299fdb9add83a6043244a5e211a6
hash_sha512: bf009a26ab2f863fe4571840fd5ea3586ceb02270e1d52319ae65a96709aa0b81cbbf95cbd28add5b8803e20431579a1e61cd98dc089febf4f471d309efb124a
created_unix: 1746256361
hash_blake2b: null
- id: 8
size: 2081
hash_md5: 65f26423829a7cda51535dfd8649927e
hash_sha1: c9436ace1e28aca3c546be746e8986f66a3beafd
hash_sha256: 9c2c053ac72e9fb571b6cd7a31019759126b5fae331753d8594cb8a0aa210e58
hash_sha512: 185728651b566828e8ee69cbd4d9fb331403c0f2e01bbf2e5be65d8e70a049390c78a08e5eff5ead7c070efaff7e3e7b4d13e8ca48d94f9b7a714be954259d94
created_unix: 1746256361
hash_blake2b: null
- id: 9
size: 1016
hash_md5: 8c19574fa57e395ce6f35d8d5644048d
hash_sha1: 0dea7bcebea9d4f506da68a5c3a592ab6bbb8150
hash_sha256: c31fcee243ed68fa575c7ffbacfacfdcb24001bd86e261475220d91603d4995c
hash_sha512: eb92ec4f81e9beba3473f4504f8bb387aeac632a70a5016b27b6527ac70dcc4c940fb940a225452892701b8e6ecfdcd3858b6bd74b87f94a3b5f5bb1de369645
created_unix: 1746256364
hash_blake2b: null
- id: 10
size: 2081
hash_md5: 42e97d6c79d8c7dfa4f80c90534a3a7e
hash_sha1: edeb74a2a1a05ad4851629b90b65463559976995
hash_sha256: 228213798b1af7033aed779446cb5a657b765f72dc47626ca2a0ad7aaca846e6
hash_sha512: 65d83d6a96e8b59ec0ae35a184a8bbe844e572eb7b2c66b022f92925dc31edcad02312e59d1312d944ee1ace163729e6d0ec95b1c97163c61a19b40b18156cca
created_unix: 1746256365
hash_blake2b: null
- id: 11
size: 947
hash_md5: a1aa1f55f9579b5b81d2b2c797677d3a
hash_sha1: 27f82af8e6580ad7fecad111665666cb08f90274
hash_sha256: ec8b8d74a0074f391e5b9fb25f4924c5dc30e8767df07096f7ea9b4f038fe0a2
hash_sha512: eb59b7154f208bfbc5da207b7fbe8288917d637bd2ad09f60e525d1208ab41a5a312d349715b527012f46b32351494bff7f5c6510b4521fda0c9164afc87b50c
created_unix: 1746256367
hash_blake2b: null
- id: 12
size: 580
hash_md5: a4e26488fa83b5945d1c0373fe07d6c0
hash_sha1: 891cde56160342b16e4e3eab187487b1e7d4c155
hash_sha256: 8d27bf11781cad6d864da15e7b04080a6b1c4f8908d2e5baba79b855f6049194
hash_sha512: ea05ff7b6e8566609063f3492c0b2551ae1275912b8c370c5345fadda9e3a408d0879c749a94160e180161eb7727a93d7a3a8aaa1dde892f75748fc4d62fece5
created_unix: 1746256368
hash_blake2b: null
- id: 13
size: 940
hash_md5: 2b1ec34161144a7100b6988347f80e4c
hash_sha1: 29e71c96e109efccb8a7f756c1828e16b19d5303
hash_sha256: 97b8e8d2bf656e30a60328054fe47cb901ebe3aa7a4c30fa7f4cdbe1b2600663
hash_sha512: 8fb46a5898cb29eebac4a73b7c02dbcfe5c596c8f273b8e4483ea5778a3475718e792ec50b031879146e6072e396b022fdc6ee1cbf83369acdc0ee341cc9090c
created_unix: 1746256370
hash_blake2b: null
- id: 14
size: 947
hash_md5: 086cba5b2139e72ea02ba0ba40bb1cef
hash_sha1: e62e69071a629c0c46465e132b530b33da90a49d
hash_sha256: 51c4395507768e97e7e6426f6acf0b054eb64fcdb5cff64b78680a013c8dbe14
hash_sha512: ced379957d362b70a60f188d6c8c24ef2b3bc9828b32314a629574dea760c17225f1ef91875e9906ba4063fcff522fcd1d8c47d0a9a1ee9c6fe6d28d35219aa3
created_unix: 1746256373
hash_blake2b: null
- id: 15
size: 580
hash_md5: 4091fac7a4f1754f86ddcc52734d5628
hash_sha1: 5478354b11895f4495337e6c1ffd1480a05c4558
hash_sha256: 930a7c95b67d8f6bcef5587a522b945f16840d7466ca45408b43a320ae28ec11
hash_sha512: 93b69bc6c84fd03e66d42c49620d1efed6f3354598b82937528347b67088ed4042705b85989cbb9ce59c256cfc9fef8a706c3f149511643ceafbc88d660404fb
created_unix: 1746256373
hash_blake2b: null
- id: 16
size: 940
hash_md5: 0714ceb54fbe397bd632a61e66498265
hash_sha1: d66dc74c7f5a092c266aaa18d236840e2ab1b7e7
hash_sha256: 6a8557a171e8808ed59b3e2c95f9499851e4a222654fa7320d3658c094bbc40d
hash_sha512: a6b34065742e12611f3df651d7b3c859d7f4d058bfb7aee3901e69755aa13342720530a8d4a8253f2696389db0d2df6ecb0d4068d7c747d7f480ec2fcb3846c1
created_unix: 1746256375
hash_blake2b: null
- id: 17
size: 598
hash_md5: 65e94a8db881efaf0944e26c8c8262f0
hash_sha1: 60b143474f8dd311ccdd3eb08a1589a3f8b757df
hash_sha256: 9ffad7e3c4c868a02c08b5956e67a4ae86b3a516ef56ca3345d6e8416e7585fd
hash_sha512: 1441415dce05f8f2d44027bc5c78133e8a757454d3b26171e576d154c23b81d97ae8ed59861ee072e15ece88dfb187d53cd052972af46d35880cabc9fe31d670
created_unix: 1746256385
hash_blake2b: null
- id: 18
size: 2099
hash_md5: 8e3ec93159d2190db2f80778756d33af
hash_sha1: 65a0719f6c8a19768852a7314fc3672c6f992347
hash_sha256: 2bdd4a5ebe1a6ac3043cb56485051a8b3332a16b6461899c3fd298156ebae4e6
hash_sha512: e33122f943f62ba64bdf16c9249495e643b836c756252de9d001069de6367792bfdfe22e00f689663b88e661309cd5d1af2a7dc6ab24a96e5542db065dfa5230
created_unix: 1746256386
hash_blake2b: null
- id: 19
size: 765
hash_md5: 823b9b5f66afb761bfd3db0d5f7e7b35
hash_sha1: 822357b0ba18bb302272bd77227361beaac414d8
hash_sha256: 255da7936e3a5df32cd3ef0d8af7ecb9b273aee15d08932806ffa3feb0a15368
hash_sha512: ef0a455f0c7eb01403c51d85676a95d912622af4ea428fbf44818bcfd1b0a06d628decd27079f77d7913255ebfa0837411dbabf1b87d08caee3ca950a752f04a
created_unix: 1746256386
hash_blake2b: null
- id: 20
size: 1031
hash_md5: a3952027a659d6199f4b5f6534465bd4
hash_sha1: e14f78cd8cf1561cce5dabbee03778be99f298f1
hash_sha256: 1bc987782dc3aa48b2fedca48c13bbc752d7e55c642d5b2194db88940652cef0
hash_sha512: 34298f6f1cccddd5489d6bee98a3bb16efdfe78edf38c71c2641aa49e316bc8efc1fda3423026343336d1e76211ac535678c7f0b144b6580cb3eae8c9dcd37f2
created_unix: 1746256389
hash_blake2b: null
- id: 21
size: 749
hash_md5: 35decba84a82a8b52e9b81c29574ed82
hash_sha1: 1397366dbecb13de99f8caf9eb758afd23951824
hash_sha256: 060fb4fddb2d6b85cc5d49c531aedebdafd10638447ec7582122c2d6a05874bf
hash_sha512: 4dad39a4cf8d99f866492181bbda829a58123d1e5c70df06f83b2dc9690ccec149795f493ff814a7f2e32003033b47ea8d37ddbcce970f1a248a92b1ed791769
created_unix: 1746256390
hash_blake2b: null
- id: 22
size: 2081
hash_md5: 8f02a5a705e82c7b4cd279714095f43e
hash_sha1: 0cd14ac45ee96a739e74921b11fd39145240be23
hash_sha256: bbc981db64f0cb352740e76df76452ccc1c0a5c0bc73929d63cbd12033976cf3
hash_sha512: 66bb3adec4666246c27844f89eaed600863a6e93143064d4edba1494efc474e095997e462480bd928680f090639205b8917486815181f3399115f21ce1233ad4
created_unix: 1746256390
hash_blake2b: null
- id: 23
size: 1016
hash_md5: 9f446caee85b293c8b7aafadf38973d4
hash_sha1: 446a2c6fc2dda6ad2e00818afdff8771211ed08d
hash_sha256: 1379abf81c435e90cc4e9658bd7d6fad4437bf92bbc8610c2e0ed12e57e03150
hash_sha512: ed7a09670cd9cc74ce977c2a24f6ed98e2d72de7abc37ccf077c56f78320bcb37fdc702743837ded3d25d8b4dcab75e0559649486ba589a657a1b72954c2d8fb
created_unix: 1746256393
hash_blake2b: null
- id: 24
size: 2081
hash_md5: 799454b1553262175f9f151a037e922e
hash_sha1: d56f9adcd116fd93d11d3692440bd6bd033c9e5a
hash_sha256: 4c6051a3051e44e747f38cc3fe96d20a2bc4272dc25cc0c374eca4d3bd45884b
hash_sha512: 7243936532054ee059bf8bc996a23e80ff5ec90ce5724235ec91e13ed501693dfe881f78e70dde01cb2f3ad1de428fdf0c86a740dc473f7401159bb1719ad59a
created_unix: 1746256394
hash_blake2b: null
- id: 25
size: 580
hash_md5: 7ca05a7b4ef9c35504122ea71260a7ac
hash_sha1: dc9091893ec24b36d8e88ec1d64e58fed315744c
hash_sha256: f6cb7d1d54e02ddf93e1f1d9a9131f12770d19cc2432982f4af33a42f9d2f8ea
hash_sha512: d011cb7f41761ec032fa1c0ecd5733fe152d07a498f7d048e7ff0fe9e04cbc472594795ee427c86d71e104cc36bb4765fe652457bf02f6cfafb39bce1554e9e5
created_unix: 1746256397
hash_blake2b: null
- id: 26
size: 940
hash_md5: 979ff1d9d17988d15d74a9125f2ec53f
hash_sha1: aa540f051f67d77d27edc2468addd77f50a9e134
hash_sha256: 1983f79e2cc3b39feada90a938d6b06772e87ec74a40540a347bad3f367824fb
hash_sha512: 2c8b53b796990c028cafe26d20183b4a3ddb0221274756d68a81c8cd3f00dc1de2e8346a9e9f83ccc119e94bc058576f96bc633be6a6499806877c42912d1208
created_unix: 1746256399
hash_blake2b: null
- id: 27
size: 580
hash_md5: e8c6c698b114078242994350e2c59cfe
hash_sha1: 965f728ce742af25f364f840e05b9cd8c21e4505
hash_sha256: b83a283c38737abcbb23462117ef35e6ee4a4d86513a2440bb4d41fa0e117dc9
hash_sha512: 418aaf3bcd26cf63856e36ddba8c972ea99a764c25ed9fcfe6003236f6cc1e812885bf153d6f8a1b9fe712a3af04d4d99ea4d87a57ce06663f285cc1388a34b5
created_unix: 1746256402
hash_blake2b: null
- id: 28
size: 940
hash_md5: 4831302596f19f696b56d59564d1991d
hash_sha1: 92e6531a2e7bc42727d4ff455efa8f1408c38c55
hash_sha256: b24b3aeb82d6dd3d532f6047e0b697b062fb10f2c4e1849b11eb1a7494450578
hash_sha512: ef61c607cec6ab378cbf2522c985fe699ab2843072068e0809f06ae02c7d9b8476663104ffded3af3c96335d0db817bb78dbbc062dc48ed84b104826cb4642a6
created_unix: 1746256405
hash_blake2b: null
- id: 29
size: 598
hash_md5: 56a57f536a07cfd465c022e69bfc064d
hash_sha1: 4fd298455cddb9a0381410e5e05bcebb54d66639
hash_sha256: e8b37be762d1ab0fc8bbc297ba1b27c507d0098a287973c3b7003772839d81ce
hash_sha512: e1ed956616b3314eb247cf24586b36ce90435635ae40b57814c7a6cf0ef92c38bf13fbc2f07895ca6f67dfb9c3b9c0b4cf5556d69320b424dca989d1e4f74856
created_unix: 1746256415
hash_blake2b: null
- id: 30
size: 2099
hash_md5: 4bc4c156c6a6a6d421e6b2a0f9bad601
hash_sha1: 7c8746c62bb8418d1570fd0884cf9f846e055ffa
hash_sha256: 3f56bd9d542d9b944c47fb946277d72445ef2d34dfee108ea3a8c2d06dbca50b
hash_sha512: 7b47cc1e588c8e358cc29aa2135bcccb0f919bfbd9bc26b8342a352f4fdde3f3d2b5ad935c1efd6fc2d9e7cec5b1dc5c3e6630c9b3b979e47ff4fbce7863ea3d
created_unix: 1746256416
hash_blake2b: null
- id: 31
size: 765
hash_md5: 5c30fe5aa7e261d6c6575cc8265697a1
hash_sha1: cdc6f4ed95e723af264f3b6aae46bd0a98deacea
hash_sha256: fc9e302e423b982dad68720d5f940452a312cf3c287a96dc66582460265a1b01
hash_sha512: 079c9cdc9a919dac4225f42c26ffb34d4d115db8ab5e51cbcdd2b248f06935dfcb169f0a9e04f49b143615dafd5a6742ed231a1be13bc5c814af75ae173bd0e0
created_unix: 1746256416
hash_blake2b: null
- id: 32
size: 1031
hash_md5: 0a379e399f7918290e303348c8cd6b07
hash_sha1: ab53de2814344cfd8998d209c6927a415df147ff
hash_sha256: 2267f735bd581417efae14f797ece94779fcea40ab28ac700734c0ee38b284e2
hash_sha512: 967ebc462c40ecda18ad36abba96534459d8c653c14628ea2d8a2f1e18b313b2cf07d36080cce0aed921780b7717913ab1a77063632845d0daebb4d93bee595e
created_unix: 1746256419
hash_blake2b: null
- id: 33
size: 749
hash_md5: 861a7d2013e965382bb9404175aa4f44
hash_sha1: caf88fc255776f9460c5bd689973a66f528d447f
hash_sha256: 9c592110208bcd6624661c36180ce96f238dbe6c6f9ad9749a0d3df828172743
hash_sha512: f7a6a16bf8c972fc12871030e3b681c1341b2eccc047f64904b2e6139f8f41015387d517e1ae0e9a19fde8dd0c3d9188db3e433120618f096e7894d3646c765e
created_unix: 1746256420
hash_blake2b: null
- id: 34
size: 2081
hash_md5: d05558896004f742e8bbe6bbb318deda
hash_sha1: 2537f2f2127f5bfeb43ab064aaabb8278ad4bc08
hash_sha256: 4e931208c50f458bd523ef5ef55c5367335c62d558be4e24615dd200a75c378d
hash_sha512: 8a4fece3a1dd51c870c160d526e58fc96c3282dd770a1e9ce38b39492cda676dd48ecf258ba3282ed081997142503967a7114646481cde6d5b7eb7be49fcff37
created_unix: 1746256420
hash_blake2b: null
- id: 35
size: 1016
hash_md5: 835a5a3fe19145a134e4a0508fbb9411
hash_sha1: 707c30bed611042d57c52d0db2bcd0f609016a5a
hash_sha256: 650248c779adbb13c81316902d4b7b27ef294530b0bbd484a3f3a1d369ca995b
hash_sha512: 81810ccaf38ad57a9e384336f872d2f39da9474abd648024e8da6733c9d0fd214757563cc7c8a7dcff12cd3e64ba4b694afa6854a6f55bd4e5a53152eef6a7f2
created_unix: 1746256423
hash_blake2b: null
- id: 36
size: 2081
hash_md5: 599464902943b0e7499180d1c2547754
hash_sha1: cab101f1bb3a73e91c3d75f7897067fb29c7747a
hash_sha256: 5e960e634845737a49b2bf758496e2a42cfb227ab24455a9d182bde7f597d4a6
hash_sha512: 759dde21a878216a6e629bdbd91e2fa4dcc04b7c56116683b5c7433eba277ca3f17daf918c4575130dd14a6d2dcf453bc7861f7e99b7abf150c3b058a299743e
created_unix: 1746256424
hash_blake2b: null
- id: 37
size: 580
hash_md5: 6e23997166dfb6e2bf3cddb0d7a7f6ff
hash_sha1: a9972937cb840e3c961fa00ed8512c45fa9d75a3
hash_sha256: c95111d06b75e32cf4d507b818544dbb5245bc0b7dd8b3a1f5d8479772a06b1e
hash_sha512: ca549fae977e7fcda088e3211d362a3636c47bd914e2c5ce08ddff2703e60b06dbd66339bae819c881425364f679f1839b1e0685f3bced32747a969d00775a8c
created_unix: 1746256427
hash_blake2b: null
- id: 38
size: 940
hash_md5: bda9707822e39ed52a6d0b9bb5e9af96
hash_sha1: f6ea322c0b9c6211747129636a59ff95e5dbb165
hash_sha256: 364f44514fb924cfcddc84c31bbf3dccf9060d9e3ef1e3e6dd28f5789a79e4c6
hash_sha512: 7636858249476a81c886c529997eb55ef8c36c52430f0e98050f628fcd440dc09e57f43f88e272f827f86c95451aa4b1927a8d860f970427d1df8348590e7869
created_unix: 1746256429
hash_blake2b: null
- id: 39
size: 580
hash_md5: 5b373c943e67125442c5a573eece9404
hash_sha1: 84b40e3373b8c172d8c302dd304f792838a3f788
hash_sha256: d6355300d7e51c649efe19b3419d0dec57d42d4c75079e4ca06ea081f8260f5c
hash_sha512: bd926b535bd19f60d69d76a858db2c48f6057223bc23a3575a4fb49fc9ea455250f2bdedad09efdb098c4ac16c6e3d2451a6c182ef191f076276de790448506f
created_unix: 1746256432
hash_blake2b: null
- id: 40
size: 940
hash_md5: e3817ea0b2f58dd5cc570c0d337a2ecd
hash_sha1: 2acc853133cb0ddb46a546fb31f7b4f23df1c676
hash_sha256: 775271730e0c4b923a81f539ce97d1b21a1db78ca201065dc886fd42d4e0251b
hash_sha512: cb28a65b2f5f7e1e9ec45f6151ed322ee751de5266f413940655de7d739ebf2f7ac9168ccd9676e5b1696d3dcfb57d0bbc404141dfb321a2d28c1a6dcf32b0a7
created_unix: 1746256435
hash_blake2b: null
- id: 41
size: 598
hash_md5: fb3df5314eda77cfc0da782d86a1574e
hash_sha1: 874c3e4aeaad0dcb91f955357c008ed3de52883f
hash_sha256: a1f80fc25f3a4cacc21c4ab3e263cc722f7e44bc829a38e7fa927853737cbf9c
hash_sha512: b27519f9792ac72b715ff591895deba46d4c6455ea04a4b416e8c5a13eb234176ebd85a21cee5b473f3baa679c6c03cfb72d6d552dee7baa9e7d75535c776557
created_unix: 1746256445
hash_blake2b: null
- id: 42
size: 2099
hash_md5: f12303e4cfd6cb446c8411f8c2c5c54d
hash_sha1: 74af4b51471a501f0fd65ff19498cda87c8252d3
hash_sha256: 9fde3ad1d6dc79a40d9ef1c41bb22d00db53df558c0f4d305aaddc0dc2cc0bba
hash_sha512: 0f19704ff822157723138bbbf027d418a601cbe0fd21db16478d6e870ad74b80084d5aa35d529a2c6555237cfea19a6c1f955df0c4b00b5ed94f0f1b42465bd6
created_unix: 1746256446
hash_blake2b: null
- id: 43
size: 765
hash_md5: 0ee9f9dbd150f2fba1f2d151243a23ea
hash_sha1: 521db5e9e0e2d25e8b80c9a168ece7263e0b548c
hash_sha256: 034554ebaf63c4c20d9290e86b45375616b4e289a28e480f58e25a791c361639
hash_sha512: 23ba0f0847083365fbbd0c4de0d71a6560e50781f413e1546a11e9d9d4aad7daee1116b5b94844ee0a431f4d70796561a337a648ec052ac3c5e4bad5accf73ea
created_unix: 1746256446
hash_blake2b: null
- id: 44
size: 1032
hash_md5: c4c544a801fb99281c6c473c22e0545b
hash_sha1: 1c686f728522cb8387a8415a259c62c1af8a0fd1
hash_sha256: ce8c82a53d379498784a981f3c69d847947420fb7e1077f5a2b3d9f41fb7c8ed
hash_sha512: d9d1bb2a1bcc7886f8c2fd5a413cd954fe2043d4a81dbb1f9ace06a1676684047a7ef2bdc27c9d3f9b800657dbf7226acc0f32b71fc6dc0c9e2662a5c64f7fea
created_unix: 1746256449
hash_blake2b: null
- id: 45
size: 751
hash_md5: 89b82f20b8be3ea17a068b46e7497428
hash_sha1: 27f1389a68febf6786e0d79c698dd1d66c9abfd2
hash_sha256: db4ea60e526f85e9829a45e2d75c1f48509e7429a79f7a5d24bc536596c66ab5
hash_sha512: b441c6ba00bfcca18adaa976ac37bbf9cab17cac8e7d24e54bc40122dec33781faec7b07db97b3dcaefec7ff8a4251e96728772995d91d196691d8e3494b5768
created_unix: 1746256450
hash_blake2b: null
- id: 46
size: 2081
hash_md5: 96d24b1f1bec73579c5dbf85ee52b332
hash_sha1: 4f407843baaa58116c1022b73f5e411f1c531840
hash_sha256: 6866d5d85796b5fc8576310f8ed1d9d9c85825727007809cfc9ff666f1dbdbf0
hash_sha512: 8c7c62afaeec008d7c6bc8e390f4adbb11c4ca11337aa1c6832ba83de19f0c75d52ea4fd72a822acdc4f567158120119ed121e0b68213db7a7a7a4f3a731f8fb
created_unix: 1746256450
hash_blake2b: null
- id: 47
size: 1017
hash_md5: 71a491bc2a2bbb08ec5b3682d09c3f1f
hash_sha1: a126e70bed9837bf4609f32d74b20d2ca3e47b59
hash_sha256: 2bcacea4eefed11c6f6a23da44ddd575689460262af046a37872e32aa76b79c3
hash_sha512: d647d5f7122d76928ac349bdb147fad8b2cd68b7be75a4f580c768feaf86c5e1f7ec484786b6c81324b9df00329cc89217cb00d379138b40adfc81f74d06e6bb
created_unix: 1746256452
hash_blake2b: null
- id: 48
size: 2081
hash_md5: 26e05a44a2e1989b418aac50feda163e
hash_sha1: 224cefa64086537d1d571644abc2180803128035
hash_sha256: ea09c61dff5dec488b378b2ce0d4dd64891394606aa318dd57d5f2158d7441fd
hash_sha512: 4ca6c77760a11e44f7826b3a23bd5b607ab1e323f47aa76acd9226f1771551fa4dd45c58002c8d3ba6fc4867ed17ad34edb1695a9ab94a31afb8255862633976
created_unix: 1746256453
hash_blake2b: null
- id: 49
size: 580
hash_md5: a9e65f66ed8ba4769f366fc93142ae07
hash_sha1: c170e29851dc3fb44e115a907de7152efd5fb85e
hash_sha256: 8ae9c630848588ab6bafebbc6ebcb3b6fe329350e89894852db5c55cc52a4c2c
hash_sha512: e0bdaf7430a5176640c0fd57910e1b3e98b4dcc0d34a5a4e898be6c30ccd2d3b59c22dcf94619e3349003ba7b7882ab43216d6175471becffd874143d74ace17
created_unix: 1746256456
hash_blake2b: null
- id: 50
size: 941
hash_md5: accc00b46ba0c6abc545ed79c2a2f88b
hash_sha1: 44a47e8601fe1785bbabc21dbc9efc61ff16d47f
hash_sha256: 6f514c55dca294288b5821f415cd24ba11649f001edcfe219e1052d252fd49d8
hash_sha512: 9ee7ebd7280fdb51f46610a7923212236b668e7a62a083bae7f2e2f1472d06cb0a9d0f38069e5537fa3b8689f13abeb3e38a1e8432d55f247a06c0522e74374e
created_unix: 1746256459
hash_blake2b: null
- id: 51
size: 580
hash_md5: 700171de0c83b136769d88af94c1b9b3
hash_sha1: c3baca0fb897aeb9462f3718369a30515a68e415
hash_sha256: 96eec3e81eebd724a3ef0e6215e337543b3979f2a3355a193933dc7489aa4616
hash_sha512: bfb5bb19da1aa65d7cc78ad4f3c57769aefef90ffd5edab3ecd0db5272550bb0666de00acb2a053e03a9473e09d9a77b5026e46bbe18cf669ec4e953b49256c9
created_unix: 1746256462
hash_blake2b: null
- id: 52
size: 941
hash_md5: 613b6c98a027fc929ecdb06482859626
hash_sha1: 39878c4f57ff81bcdc2ce191c44a9a9b31384646
hash_sha256: 78fe74176a46873c6fe0c519270641ea45b3e879b1d4caa595bf67dc69d760b0
hash_sha512: 16c23e52c02fa0d30dd60704299a375eb6ca75c3fb471fa3501720e53118254bc8888dbf44f75c3cdb2a98ac0ff9ae6fff72e9026fa9a241eaa25841f4fbdaaf
created_unix: 1746256464
hash_blake2b: null
- id: 53
size: 598
hash_md5: f3015c6cc30b064cd13392a252b61038
hash_sha1: 5b6a6ba01238ec187d18ed02f080456d9b0d8416
hash_sha256: c331e46fb09c0bcc4aa8019b39d9ed7ddfacbcd139407557f6089f36e5861c57
hash_sha512: 64d91206800c8fcc22e449591952289fb83eff85dcc2bf898f2daab66d40ff4e8f9b665335b7a6bd2c0663d78a2ddf1debef923f3306f6813dae98caf02a0f95
created_unix: 1746256474
hash_blake2b: 1ae012597dbf2d40644c82e06dc6267d88638571b0c669c62e59d15463c289e170f602420e0147da1f9524cea5095f63868bc36ab5630383360e8101b3796d49
- id: 54
size: 2099
hash_md5: 84d83643c58073eb48d88ae591920e50
hash_sha1: ef931c2b87b9e2c10b7bbb2bb592cb5dbe82aed7
hash_sha256: 8b554f322b8fce952f2221bf08be8447b8606e2ce54d8846b5edcc80ec1fbafd
hash_sha512: a2b1a5321f67ed8a324e17903497fac845169cbbb49cc3dfdad9800b164877a7d8f5fcc8f9d6a8e2b394cb13c9a512b27e39149f3b017dd28f26cfcb168fc4c6
created_unix: 1746256475
hash_blake2b: e4ca11cdd35926a89604589782e538c8140911fef2dad8a934685b33ad841e3cc96ea9d36c90ae413147ce2cabe6c5517045320049fbb3b0069f3620df753bfc
- id: 55
size: 765
hash_md5: 1e9a236068db2f888b41ad1737e26764
hash_sha1: 03c5b9eb6e2fea31d3e774c51d3719227810414b
hash_sha256: b117af5eaca1f723b7fc3646853298bbffddbb02c63af5cf09d853f0585afb89
hash_sha512: c24ff96a33c8eb5f96a212923e1381d41e2813be978772f407aac30090c8657558427b40cbc129dc8b43b96c988ab888703ef5d5e139dff32bf727041a90fca5
created_unix: 1746256476
hash_blake2b: f4549d79377a1d01fd8c0a19d4a015dfcc50b58820d57ec7bd14fdc5fbaa8166fdd11432200d728d5f1052e54cab2069c8f80818616377c1baa1013fc7679356
- id: 56
size: 1032
hash_md5: 6cda2dee8f57ccc1730f40917a6ec85c
hash_sha1: 9689d59af6cf615be6ac21106dcbcc79250b2909
hash_sha256: 012e2bbd675130a45b6b6255142254428a86b35c5cffd17546331e9f1bbadb55
hash_sha512: 06b79141428bb93d1ba14a82f132dd068ac4b68db3e55029a25ff4bd2be6274a33898f263dc225eed1186462f7eb3fa4e7a24925e996dda0dd1bdbd265eef6b4
created_unix: 1746256478
hash_blake2b: 0e148046a9984fe45d2109d227ed31b58329afc532eee4817d2113584ff034aa1c5ff09d44240c715d9a80a1a3ea89480bfb425e173df154aedde6398e2ec347
- id: 57
size: 751
hash_md5: 00c8d9ca6462b0e95a30325307a1d855
hash_sha1: 525a79e7dfe6553e5e4b4057e80e090d48b43c84
hash_sha256: 5b74c950ced8330e3dae432f6c5b0330c6c8e3a72f706f2d8128db989640a02a
hash_sha512: 0777a9af5eb936fe5c1a28bb5df26c06db7e290f858cb1ac2e7717236c9be949e8bb214d4007b865ed9d22776b5abd4f8f1e16f862beb9d2db7388f16c9f0ac4
created_unix: 1746256479
hash_blake2b: 65ed24d1a672a0ec358359f0f9b1dcefce0b64fb8ebaf0342dcf5fd9826b3664a95ac54628a9c245ddfac0843ff36f70de3dff288d5782c54539e1a95c0466d4
- id: 58
size: 2082
hash_md5: 610e6b411ad6a885613f3df4e022e8c2
hash_sha1: 5090aa11b88cc5c22895fdcd9e303eb5f89b414f
hash_sha256: b87955056b214c1404e58c6e60d2e8b203c249ed141ba80ef6e2d22e4d62d187
hash_sha512: c70e98ee7f6b22b427aed9232abdab8380385c841cb6d93af25328bc0a4ed0d1aa4342b363850fde06a1c4f68b0b63c2da99fa36e5934aed5314cdcf6e47e830
created_unix: 1746256479
hash_blake2b: e3d37152d65d1874f932f18ad0325b8bad850dd220e6f81f99ce58b3117a7f9ee40a3467992d8be78988af7434a5303f264cb5bdf4d4ab66752904a55fe4ac44
- id: 59
size: 1017
hash_md5: 155aada9969270a2d3cc9e7cbd72398d
hash_sha1: 6f7140f45bbf17a98d8b51f87c6672c30cbfe114
hash_sha256: 3ff912d0c3e4a91d6f5394d8af0a361ca7f183ff6f8a606a32e68e80d4341300
hash_sha512: 04558b2154b25463060f6ac999426e816682c7b6e866bf3b9198b4b6cf41288f762439871858831bee627559906e920b478c21525e3ddf85b9896d55840775c5
created_unix: 1746256482
hash_blake2b: 485c3a16ff1036dfbc05cc55bbb175d5e782b16a97bc4b47fb29b3af824abab016f808fc6cdc96690c4f657b4acadd69bbcf0dec7dc478c91dd09137b7cb3e2f
- id: 60
size: 2082
hash_md5: dc9d45bb32e8f3490634bd513cbf9ece
hash_sha1: ea91296f560ab8eb978b7ed0db8efe2fd0e8b413
hash_sha256: acf6b1fc9b5b93d9832b6c59e58feb352fd8b07e04356b4b60bbc35dd77c3aac
hash_sha512: ff31a45035f8092995bd6ff8e55e6fa35cdcaa4aeaf296da0382657d080541f49687a5adeccbde9ebac0fdbe814ac1e080e7f266bb38acfacd038ce0a6b6a632
created_unix: 1746256483
hash_blake2b: e24f6cd27d8702026d8d750734fd28d36b1621d8bc7bad18b403b61d0390829a31db679aeea559b7b3f7df27212cad968415d67a72c33abf5744cd61e96e8080
- id: 61
size: 580
hash_md5: efddf19ae15f14a76ad89882c5687f1e
hash_sha1: 4241c3e18d179d31a02032e6fbb31014adf9f3f5
hash_sha256: eee31b528413c52085cc0b0aa0245ac472cfd364e80fde1c5386e78e75372890
hash_sha512: cb752b230dd53a380573dcbd2d406a497ad9279b4fff5fe7de493c34d354de35f343788673830f59912651c1eab81da04513f3ecef0ab6a30dc2f1b39fd9701c
created_unix: 1746256486
hash_blake2b: 02729bb20121140d8f8904adc712dd7432fb1117bba8a3d878b86aa59afdb3e6d26e3d597b072b0364666fea48c2c5721d5d3bdabb12241267d7fbed0668c927
- id: 62
size: 941
hash_md5: 0aeb1040c1716c32a6ee5a3da7e4b7ca
hash_sha1: b7cca777ecd4e4d969f2e4c5e91d30fc7cb75070
hash_sha256: e2f0da45a23db0feddb37e44fbf6600e69187530e52234fe1bcc7ce3d1bd2a28
hash_sha512: c3394a2adb2bc062cd0015751b5a0d64c7e2600a9dd15b7153782bc9c45f57a734bd6ef7d12d35f48bf2b281e5949d8846fd61612f162c227d19a8a0de1f2ce9
created_unix: 1746256488
hash_blake2b: 3fe0bc2e796722fa3c26146ab7f5690eba647ce9463e94266998c68cefc2a834092f92ecddd70d98d1e8774808ebd87df429248c61e73144332b8cd8a571b4b0
- id: 63
size: 582
hash_md5: 1262bc37cc2b04b123ae5c1e7a739c4c
hash_sha1: 2990625d65fed9184ea3da0553722c128dc962c4
hash_sha256: 49ef9afc4951add83afa570b9fef916c5bd22d236fd74752dbfb29944dac4db9
hash_sha512: 9012ae4e297da75320c057c78220f34c64889a1905c49467f4fce7400f87b2957ffa9b1f93b7048746d84320081ec7aa69338e2d57422e53cf3c06a5365f87ef
created_unix: 1746256491
hash_blake2b: a822759b7592218d9571f96ed3039dc934aab2b175476530926ff8bd874b94b05a9c6a275d8b3c2b50fc2efa67fd6045edb659bad9aa11e83044e53cc54564ec
- id: 64
size: 941
hash_md5: 53561653378116758da59c1e5682d024
hash_sha1: ac27f3a6e7bd048aaa784273b6bfc92030708065
hash_sha256: 1da9c5d5281b8467a86fe7fb015f3ebf1807d3d975c8555959399b91628d7b8f
hash_sha512: a97b7265b7df742c903d68b2358f0d14cfc2511c20d2c61a8f2a24505a5fbeaff624b2dd47eca988a3466f55ce7910c0831e7c1617b41a5e560dd584f8b8c28d
created_unix: 1746256494
hash_blake2b: b6a911084595fdcddbab8c70e5482a2cf038bfde93b27258978a42465d894b92725f9abc81646d28a9c4b80a773c6347aaf43cf7d2067135a15edc2f1ad40b08
# broken uploads
- id: 65
size: 66356
hash_md5: 298a5b68e9783c9f193f3818b6362c0b
hash_sha1: 20eecad725f7c40a579fd06f438fa366e9dc5887
hash_sha256: 6b6802be078be4c07b38b9d0a156521459c81d2cc742be8cf115398890baf95c
hash_sha512: 983db2a7ccdf88c8765bacaf6da98eaea04aebd42e1e67943172e7e862c8f62a88cfc96adaf59bb3ad65d889c39e7281f55eb7d706b281782e4c9d0690091d2f
created_unix: 1734346161
hash_blake2b: null
- id: 66
size: 2968
hash_md5: fa67973e250e94fb4a888ebdb4fa86d7
hash_sha1: dce5f1b461883dd50f9191d52aaab01a5a06e578
hash_sha256: aae8a7ee5a446ffbb59eb3e3a70ff8809b61cdc235105397e8550b30d12d145b
hash_sha512: 6b2f0e33da8204e72903a8f13f6ab4b77ef6b387453777e308088d8c44b925c1b1d226bd8836e32af89ce96a586de0ac0d7ebd351a9534fde7de73634b2fa802
created_unix: 1734346195
hash_blake2b: null
- id: 67
size: 64135
hash_md5: d9c454fa89301aed28ffc712cd230c56
hash_sha1: 9c4b65d14fda070c3248e9ad6da78ef118cd7bec
hash_sha256: 9a41e5fec8381a75c41c0e4cc5df56546bbc0ac70019e9ec3716bdf3f7c0da0e
hash_sha512: 04e706e34c62602ab129dcb3e817fe4d3b6843b823686c375cebc3cb6adfb758227cf02aca015a808d340a3aeda7424e9ce35de14456d1a8d40874782239f250
created_unix: 1734344969
hash_blake2b: null
- id: 68
size: 1053
hash_md5: d686ee5f6ce4b91cc317354f49cde759
hash_sha1: 8ff8b4d60a1062fe468ff5c310833151932ec628
hash_sha256: fd61ac88b2ad7760fd4798613bae50994a85e5346002409837f221a5120de03f
hash_sha512: d1705bb7dad6b70a6a9041276bfc41e1e265ac0b8ba65f0ee3bd680e3ad681d813f555b82e81ba162eaae37e5453846fb569c9d64498a69efcfbfa42bd75b82e
created_unix: 1734344734
hash_blake2b: null
- id: 69
size: 24995
hash_md5: 555c619b35ccc37ef448e0627e1c91bc
hash_sha1: 2715bef2d294e8ef9bd8142b8cb92b4f20930792
hash_sha256: 90769f198e34910cf842b36e1ae790e801656fa9dc8c072c8ce06a031c195dcb
hash_sha512: 1d1f5f1badc779bb387259dcd6bf4deec7b8d711e67b62b2c6c85c1f2a6aab3ed463c82e7f5c7d2060bddbd6797ffd070b4120a7d66eea8a57ee345de2822c8e
created_unix: 1734344746
hash_blake2b: null
- id: 70
size: 6307
hash_md5: 70833f96aba4e4aae6a66459e1f48640
hash_sha1: e049c72bc8f5990c1e63c0a9ed17c12b0096efe6
hash_sha256: 55f6f783455b4737078b1b7ec9361299ec336ad0982fdc70e8f861c69be847ca
hash_sha512: 8ac7ab63762560227892717c23a1ba2cd6b3f0a59cd8cfcaa86076c65e63dcbac8f0c47213dab21677f7583b672487ddbff28a4aa2446581e25f5d98edc70eca
created_unix: 1734344775
hash_blake2b: null
- id: 71
size: 14826
hash_md5: 426d22a7b0b57a8a00681191c4f22ae6
hash_sha1: 69781f39415518ae823d8745ce4720e743172019
hash_sha256: 0fd63dcc4af83babbdff690441ea9182a640ed9d40b1312cdb1b457b2a03f38f
hash_sha512: 681fa091abad08a33950f6a308f46ead8c9af8529a42032835c03c960f4578a744b44bd4773aef73438b25b42cc51302498424aead7198eff623f96de6914176
created_unix: 1734346161
hash_blake2b: null
- id: 72
size: 863
hash_md5: 9acc0ca46cfe4c2adfe0b6e5062a8a15
hash_sha1: 0d946b74eac616923103506ee350d82f6c9066bd
hash_sha256: f6641da8427411dcf13c15e7e10cabef802383d859301ffc2de0a49b0f213fc3
hash_sha512: 4ca12e3af7ca08d58307c610042c31775683011b47345064f213312211134cfe5919c71ba4211019565052364262ced0d9b7dffd058199a36c741f2fef39e2af
created_unix: 1734346217
hash_blake2b: null
- id: 73
size: 10272
hash_md5: 6a4f701abfb602e3838ba300f9cb6ccc
hash_sha1: 0e30a16b8e1b74c51abd08e33dc5341404b71e3b
hash_sha256: 0c3e7c7143843491b221af0225ad4205b48ab871aa66ff00bce4d3954b9e0987
hash_sha512: b917580c12f09044e117adca76bd1551df5fb92af1860851700b232d07445b2e8293bb73a9c18bfecc6e6d5dbb6df86d954d6a5d93d0442847f6168125fc91be
created_unix: 1734344911
hash_blake2b: null
- id: 74
size: 4229
hash_md5: 260b6982c5690d674ac546b3bcc743dc
hash_sha1: 8e3214aba795ae5849386dac742deb01caf04b8b
hash_sha256: ee2e1fe649bf8fc670b9ea5f34815d66c85ba7f8fcd861a557afb0aa9679f652
hash_sha512: 6c58c7d0eca2943f3a4a5a2785b961e00d18bf1a9d246879b487120e27f986a0364edb842748875f25244df1c9f939fb705086ad437fce4890f7c8e2b31a79ab
created_unix: 1734344953
hash_blake2b: null
- id: 75
size: 12410
hash_md5: c511f1c9b10762bd9f907e834d7d01cb
hash_sha1: 4998c52d0d23dd3d218e2d9d336f24c0c073601e
hash_sha256: cdcf5d6a2c273647885dc4d1a3da4d64922042a4a0bb16beaf561f29b7c59209
hash_sha512: 6db8db82f436cec9066b4174a280e0e5bbd21cc829cb2beaf2071fe6ff1f8c77a5a7e5e8a168a41551a21c98bf03e6441375c87dc64a0ef2646720f348d3927d
created_unix: 1734344986
hash_blake2b: null
- id: 76
size: 770
hash_md5: f123e1549bdb0cf99f89a8944f25d8f0
hash_sha1: 01507b6530a5b134a5c660765e0f552cdb5131d2
hash_sha256: 81bbd81ba91c24024c7d4790fa1cd5c25acfb7b9c7c4fa9814e0d9f399b6b622
hash_sha512: 0738901ad6678a9ab202d5a0356d8bc49c1ed1ec21922718ff706b0990446f78b87adf3bfd25273db8a8910dd7b19f8c232f469b1f65dacffafb6fde29182104
created_unix: 1734344900
hash_blake2b: null
- id: 77
size: 4034
hash_md5: 4bf84ceb6e877a385f40ad51166bfc1a
hash_sha1: eaf6225714366c3a05d4c56c20b034efd8a5f037
hash_sha256: f375ec85318662e1789bd34750e578cb2fcf99c2bf005ecc06fabf64e23daafc
hash_sha512: 470c5ed2bf14a7ed426453ff026ad2269bbd503ba8c2cd767c89793530c77a7e7e503c68ee542d125bbceda3b49cdc5c846931981eac739a865ffb0cabbe4d89
created_unix: 1734344922
hash_blake2b: null
- id: 78
size: 6088
hash_md5: 0a4d805af108af059f9af308168ddcfb
hash_sha1: bef8a000beaec4bbe5d68b008868d76c90f3f25d
hash_sha256: 5b055f5fa634434bdcde8b8174c783d768abb5a253ccef648bb522af603836ae
hash_sha512: 9254409431fa52da3144960c272e7aaa0bb646297601cbc6bf88f6c736903aa70943cb56fdbddfac096c10444de25bfd53084b2cf3139ef5b650aae38dd42c26
created_unix: 1734344852
hash_blake2b: null
- id: 79
size: 946
hash_md5: 3f78acb32897fd77fe467b9776744c12
hash_sha1: 3d70df43777d56ad3388ad0036979fcb77443b4c
hash_sha256: f9b8b7d3050abecc367bc1523cf5ec17282b5d7181ee833ced319cf382f92616
hash_sha512: 2886114ebaef9a77b21d7240591d376a0ef898986882e25e86e0633d465bb9e8096355a4ca6273eb9cf532f02efadd5195b786249986d7c2fafe40f94a8c5ca9
created_unix: 1746280832
hash_blake2b: cef2628cf51cca9907bdcc69f4e6cbc65c121d37eccb9eccc29fb11179a09af67dccbe0eb04fcd2b87b23925f96082f33feae5c9c550307ab6c6f260aa532f8b
- id: 80
size: 946
hash_md5: 222067385a69840b897fb1177a404876
hash_sha1: d56d3378e8492c6ceee3f967f41123fe59082770
hash_sha256: 3710e14622091c60701b5ee6357d80681ad3b236baf639d2f848887f0cf681a4
hash_sha512: 2de5f0f547b60272ce3da6933309f7c967b23a839c0b7321ae952c229241091033e0640c9e64a199b10936ea64bbc3520ff56a5b6cf339856ca3d089bd5d5487
created_unix: 1746280843
hash_blake2b: e7e3034bcbc26fba242f2d1c8ec4f653e175178e303b1d120adfed0eec2f6c674b29d5fec4ac2ecb862f491682af480c7d3234be633ad9b288cfdc29cd020beb

View file

@ -0,0 +1,698 @@
- id: 1
version_id: 1
blob_id: 1
name: parent-project-1.0-20250503.071237-1.pom
lower_name: parent-project-1.0-20250503.071237-1.pom
composite_key: ""
is_lead: 1
created_unix: 1746256357
- id: 3
version_id: 2
blob_id: 3
name: sub-module-1.0-20250503.071237-1.pom
lower_name: sub-module-1.0-20250503.071237-1.pom
composite_key: ""
is_lead: 1
created_unix: 1746256358
- id: 4
version_id: 2
blob_id: 4
name: sub-module-1.0-20250503.071237-1.jar
lower_name: sub-module-1.0-20250503.071237-1.jar
composite_key: ""
is_lead: 0
created_unix: 1746256358
- id: 6
version_id: 3
blob_id: 6
name: parent-project-7.0.0.pom
lower_name: parent-project-7.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256360
- id: 7
version_id: 4
blob_id: 7
name: sub-module-7.0.0.pom
lower_name: sub-module-7.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256361
- id: 8
version_id: 4
blob_id: 8
name: sub-module-7.0.0.jar
lower_name: sub-module-7.0.0.jar
composite_key: ""
is_lead: 0
created_unix: 1746256361
- id: 9
version_id: 5
blob_id: 9
name: parent-project-7.0.0.pom
lower_name: parent-project-7.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256364
- id: 10
version_id: 6
blob_id: 7
name: sub-module-7.0.0.pom
lower_name: sub-module-7.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256365
- id: 11
version_id: 6
blob_id: 10
name: sub-module-7.0.0.jar
lower_name: sub-module-7.0.0.jar
composite_key: ""
is_lead: 0
created_unix: 1746256365
- id: 12
version_id: 7
blob_id: 11
name: bar-1.0-20250503.071248-1.pom
lower_name: bar-1.0-20250503.071248-1.pom
composite_key: ""
is_lead: 1
created_unix: 1746256367
- id: 14
version_id: 8
blob_id: 13
name: bar-7.0.0.pom
lower_name: bar-7.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256370
- id: 15
version_id: 7
blob_id: 14
name: -bar-1.0-20250503.071253-2.pom
lower_name: -bar-1.0-20250503.071253-2.pom
composite_key: ""
is_lead: 1
created_unix: 1746256373
- id: 17
version_id: 8
blob_id: 16
name: -bar-7.0.0.pom
lower_name: -bar-7.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256375
- id: 18
version_id: 1
blob_id: 1
name: parent-project-1.0-20250503.071306-2.pom
lower_name: parent-project-1.0-20250503.071306-2.pom
composite_key: ""
is_lead: 1
created_unix: 1746256385
- id: 20
version_id: 2
blob_id: 3
name: sub-module-1.0-20250503.071306-2.pom
lower_name: sub-module-1.0-20250503.071306-2.pom
composite_key: ""
is_lead: 1
created_unix: 1746256386
- id: 21
version_id: 2
blob_id: 18
name: sub-module-1.0-20250503.071306-2.jar
lower_name: sub-module-1.0-20250503.071306-2.jar
composite_key: ""
is_lead: 0
created_unix: 1746256386
- id: 23
version_id: 9
blob_id: 20
name: parent-project-8.0.0.pom
lower_name: parent-project-8.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256389
- id: 24
version_id: 10
blob_id: 21
name: sub-module-8.0.0.pom
lower_name: sub-module-8.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256390
- id: 25
version_id: 10
blob_id: 22
name: sub-module-8.0.0.jar
lower_name: sub-module-8.0.0.jar
composite_key: ""
is_lead: 0
created_unix: 1746256390
- id: 26
version_id: 11
blob_id: 23
name: parent-project-8.0.0.pom
lower_name: parent-project-8.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256393
- id: 27
version_id: 12
blob_id: 21
name: sub-module-8.0.0.pom
lower_name: sub-module-8.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256394
- id: 28
version_id: 12
blob_id: 24
name: sub-module-8.0.0.jar
lower_name: sub-module-8.0.0.jar
composite_key: ""
is_lead: 0
created_unix: 1746256394
- id: 29
version_id: 7
blob_id: 11
name: bar-1.0-20250503.071317-3.pom
lower_name: bar-1.0-20250503.071317-3.pom
composite_key: ""
is_lead: 1
created_unix: 1746256397
- id: 31
version_id: 13
blob_id: 26
name: bar-8.0.0.pom
lower_name: bar-8.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256399
- id: 32
version_id: 7
blob_id: 14
name: -bar-1.0-20250503.071323-4.pom
lower_name: -bar-1.0-20250503.071323-4.pom
composite_key: ""
is_lead: 1
created_unix: 1746256402
- id: 34
version_id: 13
blob_id: 28
name: -bar-8.0.0.pom
lower_name: -bar-8.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256405
- id: 35
version_id: 1
blob_id: 1
name: parent-project-1.0-20250503.071335-3.pom
lower_name: parent-project-1.0-20250503.071335-3.pom
composite_key: ""
is_lead: 1
created_unix: 1746256415
- id: 37
version_id: 2
blob_id: 3
name: sub-module-1.0-20250503.071335-3.pom
lower_name: sub-module-1.0-20250503.071335-3.pom
composite_key: ""
is_lead: 1
created_unix: 1746256416
- id: 38
version_id: 2
blob_id: 30
name: sub-module-1.0-20250503.071335-3.jar
lower_name: sub-module-1.0-20250503.071335-3.jar
composite_key: ""
is_lead: 0
created_unix: 1746256416
- id: 40
version_id: 14
blob_id: 32
name: parent-project-9.0.0.pom
lower_name: parent-project-9.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256419
- id: 41
version_id: 15
blob_id: 33
name: sub-module-9.0.0.pom
lower_name: sub-module-9.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256420
- id: 42
version_id: 15
blob_id: 34
name: sub-module-9.0.0.jar
lower_name: sub-module-9.0.0.jar
composite_key: ""
is_lead: 0
created_unix: 1746256420
- id: 43
version_id: 16
blob_id: 35
name: parent-project-9.0.0.pom
lower_name: parent-project-9.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256423
- id: 44
version_id: 17
blob_id: 33
name: sub-module-9.0.0.pom
lower_name: sub-module-9.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256424
- id: 45
version_id: 17
blob_id: 36
name: sub-module-9.0.0.jar
lower_name: sub-module-9.0.0.jar
composite_key: ""
is_lead: 0
created_unix: 1746256424
- id: 46
version_id: 7
blob_id: 11
name: bar-1.0-20250503.071347-5.pom
lower_name: bar-1.0-20250503.071347-5.pom
composite_key: ""
is_lead: 1
created_unix: 1746256427
- id: 48
version_id: 18
blob_id: 38
name: bar-9.0.0.pom
lower_name: bar-9.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256429
- id: 49
version_id: 7
blob_id: 14
name: -bar-1.0-20250503.071353-6.pom
lower_name: -bar-1.0-20250503.071353-6.pom
composite_key: ""
is_lead: 1
created_unix: 1746256432
- id: 51
version_id: 18
blob_id: 40
name: -bar-9.0.0.pom
lower_name: -bar-9.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256435
- id: 52
version_id: 1
blob_id: 1
name: parent-project-1.0-20250503.071405-4.pom
lower_name: parent-project-1.0-20250503.071405-4.pom
composite_key: ""
is_lead: 1
created_unix: 1746256445
- id: 54
version_id: 2
blob_id: 3
name: sub-module-1.0-20250503.071405-4.pom
lower_name: sub-module-1.0-20250503.071405-4.pom
composite_key: ""
is_lead: 1
created_unix: 1746256446
- id: 55
version_id: 2
blob_id: 42
name: sub-module-1.0-20250503.071405-4.jar
lower_name: sub-module-1.0-20250503.071405-4.jar
composite_key: ""
is_lead: 0
created_unix: 1746256446
- id: 57
version_id: 19
blob_id: 44
name: parent-project-10.0.0.pom
lower_name: parent-project-10.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256449
- id: 58
version_id: 20
blob_id: 45
name: sub-module-10.0.0.pom
lower_name: sub-module-10.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256450
- id: 59
version_id: 20
blob_id: 46
name: sub-module-10.0.0.jar
lower_name: sub-module-10.0.0.jar
composite_key: ""
is_lead: 0
created_unix: 1746256450
- id: 60
version_id: 21
blob_id: 47
name: parent-project-10.0.0.pom
lower_name: parent-project-10.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256452
- id: 61
version_id: 22
blob_id: 45
name: sub-module-10.0.0.pom
lower_name: sub-module-10.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256453
- id: 62
version_id: 22
blob_id: 48
name: sub-module-10.0.0.jar
lower_name: sub-module-10.0.0.jar
composite_key: ""
is_lead: 0
created_unix: 1746256453
- id: 63
version_id: 7
blob_id: 11
name: bar-1.0-20250503.071416-7.pom
lower_name: bar-1.0-20250503.071416-7.pom
composite_key: ""
is_lead: 1
created_unix: 1746256456
- id: 65
version_id: 23
blob_id: 50
name: bar-10.0.0.pom
lower_name: bar-10.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256459
- id: 66
version_id: 7
blob_id: 14
name: -bar-1.0-20250503.071422-8.pom
lower_name: -bar-1.0-20250503.071422-8.pom
composite_key: ""
is_lead: 1
created_unix: 1746256461
- id: 68
version_id: 23
blob_id: 52
name: -bar-10.0.0.pom
lower_name: -bar-10.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256464
- id: 69
version_id: 1
blob_id: 1
name: parent-project-1.0-20250503.071435-5.pom
lower_name: parent-project-1.0-20250503.071435-5.pom
composite_key: ""
is_lead: 1
created_unix: 1746256474
- id: 70
version_id: 1
blob_id: 53
name: maven-metadata.xml
lower_name: maven-metadata.xml
composite_key: ""
is_lead: 0
created_unix: 1746256474
- id: 71
version_id: 2
blob_id: 3
name: sub-module-1.0-20250503.071435-5.pom
lower_name: sub-module-1.0-20250503.071435-5.pom
composite_key: ""
is_lead: 1
created_unix: 1746256475
- id: 72
version_id: 2
blob_id: 54
name: sub-module-1.0-20250503.071435-5.jar
lower_name: sub-module-1.0-20250503.071435-5.jar
composite_key: ""
is_lead: 0
created_unix: 1746256475
- id: 73
version_id: 2
blob_id: 55
name: maven-metadata.xml
lower_name: maven-metadata.xml
composite_key: ""
is_lead: 0
created_unix: 1746256476
- id: 74
version_id: 24
blob_id: 56
name: parent-project-11.0.0.pom
lower_name: parent-project-11.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256478
- id: 75
version_id: 25
blob_id: 57
name: sub-module-11.0.0.pom
lower_name: sub-module-11.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256479
- id: 76
version_id: 25
blob_id: 58
name: sub-module-11.0.0.jar
lower_name: sub-module-11.0.0.jar
composite_key: ""
is_lead: 0
created_unix: 1746256479
- id: 77
version_id: 26
blob_id: 59
name: parent-project-11.0.0.pom
lower_name: parent-project-11.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256482
- id: 78
version_id: 27
blob_id: 57
name: sub-module-11.0.0.pom
lower_name: sub-module-11.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256483
- id: 79
version_id: 27
blob_id: 60
name: sub-module-11.0.0.jar
lower_name: sub-module-11.0.0.jar
composite_key: ""
is_lead: 0
created_unix: 1746256483
- id: 80
version_id: 7
blob_id: 11
name: bar-1.0-20250503.071446-9.pom
lower_name: bar-1.0-20250503.071446-9.pom
composite_key: ""
is_lead: 1
created_unix: 1746256486
- id: 82
version_id: 28
blob_id: 62
name: bar-11.0.0.pom
lower_name: bar-11.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256488
- id: 83
version_id: 7
blob_id: 14
name: -bar-1.0-20250503.071451-10.pom
lower_name: -bar-1.0-20250503.071451-10.pom
composite_key: ""
is_lead: 1
created_unix: 1746256491
- id: 84
version_id: 7
blob_id: 63
name: maven-metadata.xml
lower_name: maven-metadata.xml
composite_key: ""
is_lead: 0
created_unix: 1746256491
- id: 85
version_id: 28
blob_id: 64
name: -bar-11.0.0.pom
lower_name: -bar-11.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746256494
# broken uploads
- id: 86
version_id: 29
blob_id: 75
name: br-repo-jooq-1.2.4-sources.jar
lower_name: br-repo-jooq-1.2.4-sources.jar
composite_key: ""
is_lead: 0
created_unix: 174625649444986
- id: 87
version_id: 29
blob_id: 65
name: br-rest-webmvc-1.2.4.jar
lower_name: br-rest-webmvc-1.2.4.jar
composite_key: ""
is_lead: 0
created_unix: 174625649446161
- id: 88
version_id: 29
blob_id: 68
name: br-openapi-base-1.2.4.pom
lower_name: br-openapi-base-1.2.4.pom
composite_key: ""
is_lead: 1
created_unix: 174625649444734
- id: 89
version_id: 29
blob_id: 69
name: br-openapi-base-1.2.4.jar
lower_name: br-openapi-base-1.2.4.jar
composite_key: ""
is_lead: 0
created_unix: 174625649444746
- id: 90
version_id: 29
blob_id: 70
name: br-openapi-base-1.2.4-sources.jar
lower_name: br-openapi-base-1.2.4-sources.jar
composite_key: ""
is_lead: 0
created_unix: 174625649444775
- id: 91
version_id: 29
blob_id: 78
name: br-parent-1.2.4.pom
lower_name: br-parent-1.2.4.pom
composite_key: ""
is_lead: 1
created_unix: 174625649444852
- id: 92
version_id: 29
blob_id: 76
name: br-repo-in-memory-1.2.4.pom
lower_name: br-repo-in-memory-1.2.4.pom
composite_key: ""
is_lead: 1
created_unix: 174625649444900
- id: 93
version_id: 29
blob_id: 73
name: br-repo-in-memory-1.2.4.jar
lower_name: br-repo-in-memory-1.2.4.jar
composite_key: ""
is_lead: 0
created_unix: 174625649444911
- id: 94
version_id: 29
blob_id: 77
name: br-repo-in-memory-1.2.4-sources.jar
lower_name: br-repo-in-memory-1.2.4-sources.jar
composite_key: ""
is_lead: 0
created_unix: 174625649444922
- id: 95
version_id: 29
blob_id: 74
name: br-repo-jooq-1.2.4.pom
lower_name: br-repo-jooq-1.2.4.pom
composite_key: ""
is_lead: 1
created_unix: 174625649444953
- id: 96
version_id: 29
blob_id: 67
name: br-repo-jooq-1.2.4.jar
lower_name: br-repo-jooq-1.2.4.jar
composite_key: ""
is_lead: 0
created_unix: 174625649444969
- id: 97
version_id: 29
blob_id: 71
name: br-rest-webmvc-1.2.4-sources.jar
lower_name: br-rest-webmvc-1.2.4-sources.jar
composite_key: ""
is_lead: 0
created_unix: 174625649446161
- id: 98
version_id: 29
blob_id: 66
name: br-rest-webmvc-1.2.4.pom
lower_name: br-rest-webmvc-1.2.4.pom
composite_key: ""
is_lead: 1
created_unix: 174625649446195
- id: 99
version_id: 29
blob_id: 72
name: br-root-1.2.4.pom
lower_name: br-root-1.2.4.pom
composite_key: ""
is_lead: 1
created_unix: 174625649446217
- id: 100
version_id: 30
blob_id: 66
name: br-rest-webmvc-1.2.4.pom
lower_name: br-rest-webmvc-1.2.4.pom
composite_key: ""
is_lead: 1
created_unix: 174625649446311
- id: 101
version_id: 30
blob_id: 65
name: br-rest-webmvc-1.2.4.jar
lower_name: br-rest-webmvc-1.2.4.jar
composite_key: ""
is_lead: 0
created_unix: 174625649446312
- id: 102
version_id: 30
blob_id: 71
name: br-rest-webmvc-1.2.4-sources.jar
lower_name: br-rest-webmvc-1.2.4-sources.jar
composite_key: ""
is_lead: 0
created_unix: 174625649446312
# collision
- id: 103
version_id: 31
blob_id: 79
name: bar-art-11.0.0.pom
lower_name: bar-art-11.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746280832
- id: 104
version_id: 31
blob_id: 80
name: art-11.0.0.pom
lower_name: art-11.0.0.pom
composite_key: ""
is_lead: 1
created_unix: 1746280843

View file

@ -0,0 +1,281 @@
- id: 1
package_id: 1
creator_id: 1
version: 1.0-SNAPSHOT
lower_version: 1.0-snapshot
created_unix: 1746256357
is_internal: 0
metadata_json: '{"group_id":"com.example","artifact_id":"parent-project","name":"Example :: Parent "}'
download_count: 0
- id: 2
package_id: 2
creator_id: 1
version: 1.0-SNAPSHOT
lower_version: 1.0-snapshot
created_unix: 1746256358
is_internal: 0
metadata_json: '{"group_id":"com.example","artifact_id":"sub-module","name":"Example :: SubModule ","dependencies":[{"group_id":"junit","artifact_id":"junit","version":"3.8.1"}]}'
download_count: 0
- id: 3
package_id: 1
creator_id: 1
version: 7.0.0
lower_version: 7.0.0
created_unix: 1746256360
is_internal: 0
metadata_json: '{"group_id":"com.example","artifact_id":"parent-project","name":"Example :: Parent "}'
download_count: 0
- id: 4
package_id: 2
creator_id: 1
version: 7.0.0
lower_version: 7.0.0
created_unix: 1746256361
is_internal: 0
metadata_json: '{"artifact_id":"sub-module","name":"Example :: SubModule ","dependencies":[{"group_id":"junit","artifact_id":"junit","version":"3.8.1"}]}'
download_count: 0
- id: 5
package_id: 3
creator_id: 1
version: 7.0.0
lower_version: 7.0.0
created_unix: 1746256364
is_internal: 0
metadata_json: '{"group_id":"com.example","artifact_id":"parent-project","name":"Example :: Parent "}'
download_count: 0
- id: 6
package_id: 4
creator_id: 1
version: 7.0.0
lower_version: 7.0.0
created_unix: 1746256365
is_internal: 0
metadata_json: '{"artifact_id":"sub-module","name":"Example :: SubModule ","dependencies":[{"group_id":"junit","artifact_id":"junit","version":"3.8.1"}]}'
download_count: 0
- id: 7
package_id: 5
creator_id: 1
version: 1.0-SNAPSHOT
lower_version: 1.0-snapshot
created_unix: 1746256367
is_internal: 0
metadata_json: '{"group_id":"foo","artifact_id":"-bar","name":"Example :: Parent "}'
download_count: 0
- id: 8
package_id: 5
creator_id: 1
version: 7.0.0
lower_version: 7.0.0
created_unix: 1746256370
is_internal: 0
metadata_json: '{"group_id":"foo","artifact_id":"-bar","name":"Example :: Parent "}'
download_count: 0
- id: 9
package_id: 1
creator_id: 1
version: 8.0.0
lower_version: 8.0.0
created_unix: 1746256389
is_internal: 0
metadata_json: '{"group_id":"com.example","artifact_id":"parent-project","name":"Example :: Parent "}'
download_count: 0
- id: 10
package_id: 2
creator_id: 1
version: 8.0.0
lower_version: 8.0.0
created_unix: 1746256390
is_internal: 0
metadata_json: '{"artifact_id":"sub-module","name":"Example :: SubModule ","dependencies":[{"group_id":"junit","artifact_id":"junit","version":"3.8.1"}]}'
download_count: 0
- id: 11
package_id: 3
creator_id: 1
version: 8.0.0
lower_version: 8.0.0
created_unix: 1746256393
is_internal: 0
metadata_json: '{"group_id":"com.example","artifact_id":"parent-project","name":"Example :: Parent "}'
download_count: 0
- id: 12
package_id: 4
creator_id: 1
version: 8.0.0
lower_version: 8.0.0
created_unix: 1746256394
is_internal: 0
metadata_json: '{"artifact_id":"sub-module","name":"Example :: SubModule ","dependencies":[{"group_id":"junit","artifact_id":"junit","version":"3.8.1"}]}'
download_count: 0
- id: 13
package_id: 5
creator_id: 1
version: 8.0.0
lower_version: 8.0.0
created_unix: 1746256399
is_internal: 0
metadata_json: '{"group_id":"foo","artifact_id":"-bar","name":"Example :: Parent "}'
download_count: 0
- id: 14
package_id: 1
creator_id: 1
version: 9.0.0
lower_version: 9.0.0
created_unix: 1746256419
is_internal: 0
metadata_json: '{"group_id":"com.example","artifact_id":"parent-project","name":"Example :: Parent "}'
download_count: 0
- id: 15
package_id: 2
creator_id: 1
version: 9.0.0
lower_version: 9.0.0
created_unix: 1746256420
is_internal: 0
metadata_json: '{"artifact_id":"sub-module","name":"Example :: SubModule ","dependencies":[{"group_id":"junit","artifact_id":"junit","version":"3.8.1"}]}'
download_count: 0
- id: 16
package_id: 3
creator_id: 1
version: 9.0.0
lower_version: 9.0.0
created_unix: 1746256423
is_internal: 0
metadata_json: '{"group_id":"com.example","artifact_id":"parent-project","name":"Example :: Parent "}'
download_count: 0
- id: 17
package_id: 4
creator_id: 1
version: 9.0.0
lower_version: 9.0.0
created_unix: 1746256424
is_internal: 0
metadata_json: '{"artifact_id":"sub-module","name":"Example :: SubModule ","dependencies":[{"group_id":"junit","artifact_id":"junit","version":"3.8.1"}]}'
download_count: 0
- id: 18
package_id: 5
creator_id: 1
version: 9.0.0
lower_version: 9.0.0
created_unix: 1746256429
is_internal: 0
metadata_json: '{"group_id":"foo","artifact_id":"-bar","name":"Example :: Parent "}'
download_count: 0
- id: 19
package_id: 1
creator_id: 1
version: 10.0.0
lower_version: 10.0.0
created_unix: 1746256449
is_internal: 0
metadata_json: '{"group_id":"com.example","artifact_id":"parent-project","name":"Example :: Parent "}'
download_count: 0
- id: 20
package_id: 2
creator_id: 1
version: 10.0.0
lower_version: 10.0.0
created_unix: 1746256450
is_internal: 0
metadata_json: '{"group_id":"com.example","artifact_id":"sub-module","name":"Example :: SubModule ","dependencies":[{"group_id":"junit","artifact_id":"junit","version":"3.8.1"}]}'
download_count: 0
- id: 21
package_id: 3
creator_id: 1
version: 10.0.0
lower_version: 10.0.0
created_unix: 1746256452
is_internal: 0
metadata_json: '{"group_id":"com.example","artifact_id":"parent-project","name":"Example :: Parent "}'
download_count: 0
- id: 22
package_id: 4
creator_id: 1
version: 10.0.0
lower_version: 10.0.0
created_unix: 1746256453
is_internal: 0
metadata_json: '{"group_id":"com.example","artifact_id":"sub-module","name":"Example :: SubModule ","dependencies":[{"group_id":"junit","artifact_id":"junit","version":"3.8.1"}]}'
download_count: 0
- id: 23
package_id: 5
creator_id: 1
version: 10.0.0
lower_version: 10.0.0
created_unix: 1746256459
is_internal: 0
metadata_json: '{"group_id":"foo","artifact_id":"-bar","name":"Example :: Parent "}'
download_count: 0
- id: 24
package_id: 1
creator_id: 1
version: 11.0.0
lower_version: 11.0.0
created_unix: 1746256478
is_internal: 0
metadata_json: '{"group_id":"com.example","artifact_id":"parent-project","name":"Example :: Parent "}'
download_count: 0
- id: 25
package_id: 2
creator_id: 1
version: 11.0.0
lower_version: 11.0.0
created_unix: 1746256479
is_internal: 0
metadata_json: '{"group_id":"com.example","artifact_id":"sub-module","name":"Example :: SubModule ","dependencies":[{"group_id":"junit","artifact_id":"junit","version":"3.8.1"}]}'
download_count: 0
- id: 26
package_id: 3
creator_id: 1
version: 11.0.0
lower_version: 11.0.0
created_unix: 1746256482
is_internal: 0
metadata_json: '{"group_id":"com.example","artifact_id":"parent-project","name":"Example :: Parent "}'
download_count: 0
- id: 27
package_id: 4
creator_id: 1
version: 11.0.0
lower_version: 11.0.0
created_unix: 1746256483
is_internal: 0
metadata_json: '{"group_id":"com.example","artifact_id":"sub-module","name":"Example :: SubModule ","dependencies":[{"group_id":"junit","artifact_id":"junit","version":"3.8.1"}]}'
download_count: 0
- id: 28
package_id: 5
creator_id: 1
version: 11.0.0
lower_version: 11.0.0
created_unix: 1746256488
is_internal: 0
metadata_json: '{"group_id":"foo","artifact_id":"-bar","name":"Example :: Parent "}'
download_count: 0
# broken uploads
- id: 29
package_id: 7
creator_id: 6
version: 1.2.4
lower_version: 1.2.4
created_unix: 1746256488
is_internal: 0
metadata_json: '{"group_id":"com.broken","artifact_id":"br-root","name":"Foo"}'
download_count: 0
- id: 30
package_id: 6
creator_id: 6
version: 1.2.4
lower_version: 1.2.4
created_unix: 1746256488
is_internal: 0
metadata_json: '{"artifact_id":"br-rest-webmvc","name":"Foo :: REST :: Web MVC","dependencies":[{"group_id":"com.broken","artifact_id":"br-base"},{"group_id":"jakarta.servlet","artifact_id":"jakarta.servlet-api"},{"group_id":"jakarta.validation","artifact_id":"jakarta.validation-api"},{"group_id":"org.springframework","artifact_id":"spring-webmvc"},{"group_id":"org.springframework.hateoas","artifact_id":"spring-hateoas"},{"group_id":"org.springframework.boot","artifact_id":"spring-boot-starter-test"},{"group_id":"com.broken","artifact_id":"br-test"},{"group_id":"com.broken","artifact_id":"br-repo-in-memory"},{"group_id":"org.hibernate.validator","artifact_id":"hibernate-validator"},{"group_id":"org.glassfish.expressly","artifact_id":"expressly"}]}'
download_count: 0
# collision
- id: 31
package_id: 8
creator_id: 1
version: 11.0.0
lower_version: 11.0.0
created_unix: 1746256488
is_internal: 0
metadata_json: '{"group_id":"group-bar","artifact_id":"art","name":"Example :: Parent "}'
download_count: 0

View file

@ -0,0 +1,9 @@
# type Repository struct {
# ID int64 `xorm:"pk autoincr"`
# Topics []string `xorm:"TEXT JSON"`
# }
-
id: 1
-
id: 2
topics: '["go", "dev"]'

View file

@ -6,6 +6,7 @@ package migrations
import (
"context"
"errors"
"fmt"
"forgejo.org/models/db"
@ -412,7 +413,7 @@ func EnsureUpToDate(x *xorm.Engine) error {
}
if currentDB < 0 {
return fmt.Errorf("database has not been initialized")
return errors.New("database has not been initialized")
}
if minDBVersion > currentDB {

View file

@ -5,6 +5,7 @@ package v1_13 //nolint
import (
"context"
"errors"
"fmt"
"strings"
@ -83,7 +84,7 @@ func SetDefaultPasswordToArgon2(x *xorm.Engine) error {
newTableColumns := table.Columns()
if len(newTableColumns) == 0 {
return fmt.Errorf("no columns in new table")
return errors.New("no columns in new table")
}
hasID := false
for _, column := range newTableColumns {

View file

@ -4,7 +4,7 @@
package v1_14 //nolint
import (
"fmt"
"errors"
"strconv"
"forgejo.org/modules/log"
@ -72,7 +72,7 @@ func UpdateCodeCommentReplies(x *xorm.Engine) error {
case setting.Database.Type.IsSQLite3():
sqlCmd = sqlSelect + sqlTail + " LIMIT " + strconv.Itoa(batchSize) + " OFFSET " + strconv.Itoa(start)
default:
return fmt.Errorf("Unsupported database type")
return errors.New("Unsupported database type")
}
if err := sess.SQL(sqlCmd).Find(&comments); err != nil {

View file

@ -73,12 +73,12 @@ func Test_DeleteOrphanedIssueLabels(t *testing.T) {
// Now test what is left
if _, ok := postMigration[2]; ok {
t.Errorf("Orphaned Label[2] survived the migration")
t.Error("Orphaned Label[2] survived the migration")
return
}
if _, ok := postMigration[5]; ok {
t.Errorf("Orphaned Label[5] survived the migration")
t.Error("Orphaned Label[5] survived the migration")
return
}

View file

@ -5,6 +5,7 @@ package v1_17 //nolint
import (
"context"
"errors"
"fmt"
"forgejo.org/models/migrations/base"
@ -29,7 +30,7 @@ func DropOldCredentialIDColumn(x *xorm.Engine) error {
}
if !credentialIDBytesExists {
// looks like 221 hasn't properly run
return fmt.Errorf("webauthn_credential does not have a credential_id_bytes column... it is not safe to run this migration")
return errors.New("webauthn_credential does not have a credential_id_bytes column... it is not safe to run this migration")
}
// Create webauthnCredential table

View file

@ -5,7 +5,7 @@ package v1_21 //nolint
import (
"context"
"fmt"
"errors"
"forgejo.org/models/db"
"forgejo.org/modules/timeutil"
@ -57,7 +57,7 @@ func AddBranchTable(x *xorm.Engine) error {
if err != nil {
return err
} else if !has {
return fmt.Errorf("no admin user found")
return errors.New("no admin user found")
}
branches := make([]Branch, 0, 100)

View file

@ -0,0 +1,177 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package moderation
import (
"context"
"database/sql"
"errors"
"slices"
"forgejo.org/models/db"
"forgejo.org/modules/log"
"forgejo.org/modules/timeutil"
"xorm.io/builder"
)
// ReportStatusType defines the statuses a report (of abusive content) can have.
type ReportStatusType int
const (
// ReportStatusTypeOpen represents the status of open reports that were not yet handled in any way.
ReportStatusTypeOpen ReportStatusType = iota + 1 // 1
// ReportStatusTypeHandled represents the status of valid reports, that have been acted upon.
ReportStatusTypeHandled // 2
// ReportStatusTypeIgnored represents the status of ignored reports, that were closed without any action.
ReportStatusTypeIgnored // 3
)
type (
// AbuseCategoryType defines the categories in which a user can include the reported content.
AbuseCategoryType int
// AbuseCategoryItem defines a pair of value and it's corresponding translation key
// (used to add options within the dropdown shown when new reports are submitted).
AbuseCategoryItem struct {
Value AbuseCategoryType
TranslationKey string
}
)
const (
AbuseCategoryTypeOther AbuseCategoryType = iota + 1 // 1 (Other violations of platform rules)
AbuseCategoryTypeSpam // 2
AbuseCategoryTypeMalware // 3
AbuseCategoryTypeIllegalContent // 4
)
// GetAbuseCategoriesList returns a list of pairs with the available abuse category types
// and their corresponding translation keys
func GetAbuseCategoriesList() []AbuseCategoryItem {
return []AbuseCategoryItem{
{AbuseCategoryTypeSpam, "moderation.abuse_category.spam"},
{AbuseCategoryTypeMalware, "moderation.abuse_category.malware"},
{AbuseCategoryTypeIllegalContent, "moderation.abuse_category.illegal_content"},
{AbuseCategoryTypeOther, "moderation.abuse_category.other_violations"},
}
}
// ReportedContentType defines the types of content that can be reported
// (i.e. user/organization profile, repository, issue/pull, comment).
type ReportedContentType int
const (
// ReportedContentTypeUser should be used when reporting abusive users or organizations.
ReportedContentTypeUser ReportedContentType = iota + 1 // 1
// ReportedContentTypeRepository should be used when reporting a repository with abusive content.
ReportedContentTypeRepository // 2
// ReportedContentTypeIssue should be used when reporting an issue or pull request with abusive content.
ReportedContentTypeIssue // 3
// ReportedContentTypeComment should be used when reporting a comment with abusive content.
ReportedContentTypeComment // 4
)
var allReportedContentTypes = []ReportedContentType{
ReportedContentTypeUser,
ReportedContentTypeRepository,
ReportedContentTypeIssue,
ReportedContentTypeComment,
}
func (t ReportedContentType) IsValid() bool {
return slices.Contains(allReportedContentTypes, t)
}
// AbuseReport represents a report of abusive content.
type AbuseReport struct {
ID int64 `xorm:"pk autoincr"`
Status ReportStatusType `xorm:"INDEX NOT NULL DEFAULT 1"`
// The ID of the user who submitted the report.
ReporterID int64 `xorm:"NOT NULL"`
// Reported content type: user/organization profile, repository, issue/pull or comment.
ContentType ReportedContentType `xorm:"INDEX NOT NULL"`
// The ID of the reported item (based on ContentType: user, repository, issue or comment).
ContentID int64 `xorm:"NOT NULL"`
// The abuse category selected by the reporter.
Category AbuseCategoryType `xorm:"INDEX NOT NULL"`
// Remarks provided by the reporter.
Remarks string
// The ID of the corresponding shadow-copied content when exists; otherwise null.
ShadowCopyID sql.NullInt64 `xorm:"DEFAULT NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
}
var ErrSelfReporting = errors.New("reporting yourself is not allowed")
func init() {
// RegisterModel will create the table if does not already exist
// or any missing columns if the table was previously created.
// It will not drop or rename existing columns (when struct has changed).
db.RegisterModel(new(AbuseReport))
}
// IsShadowCopyNeeded reports whether one or more reports were already submitted
// for contentType and contentID and not yet linked to a shadow copy (regardless their status).
func IsShadowCopyNeeded(ctx context.Context, contentType ReportedContentType, contentID int64) (bool, error) {
return db.GetEngine(ctx).Cols("id").Where(builder.IsNull{"shadow_copy_id"}).Exist(
&AbuseReport{ContentType: contentType, ContentID: contentID},
)
}
// AlreadyReportedByAndOpen returns if doerID has already submitted a report for contentType and contentID that is still Open.
func AlreadyReportedByAndOpen(ctx context.Context, doerID int64, contentType ReportedContentType, contentID int64) bool {
reported, _ := db.GetEngine(ctx).Exist(&AbuseReport{
Status: ReportStatusTypeOpen,
ReporterID: doerID,
ContentType: contentType,
ContentID: contentID,
})
return reported
}
// ReportAbuse creates a new abuse report in the DB with 'Open' status.
// If the reported content is the user profile of the reporter ErrSelfReporting is returned.
// If there is already an open report submitted by the same user for the same content,
// the request will be ignored without returning an error (and a warning will be logged).
func ReportAbuse(ctx context.Context, report *AbuseReport) error {
if report.ContentType == ReportedContentTypeUser && report.ReporterID == report.ContentID {
return ErrSelfReporting
}
if AlreadyReportedByAndOpen(ctx, report.ReporterID, report.ContentType, report.ContentID) {
log.Warn("Seems that user %d wanted to report again the content with type %d and ID %d; this request will be ignored.", report.ReporterID, report.ContentType, report.ContentID)
return nil
}
report.Status = ReportStatusTypeOpen
_, err := db.GetEngine(ctx).Insert(report)
return err
}
/*
// MarkAsHandled will change the status to 'Handled' for all reports linked to the same item (user, repository, issue or comment).
func MarkAsHandled(ctx context.Context, contentType ReportedContentType, contentID int64) error {
return updateStatus(ctx, contentType, contentID, ReportStatusTypeHandled)
}
// MarkAsIgnored will change the status to 'Ignored' for all reports linked to the same item (user, repository, issue or comment).
func MarkAsIgnored(ctx context.Context, contentType ReportedContentType, contentID int64) error {
return updateStatus(ctx, contentType, contentID, ReportStatusTypeIgnored)
}
// updateStatus will set the provided status for any reports linked to the item with the given type and ID.
func updateStatus(ctx context.Context, contentType ReportedContentType, contentID int64, status ReportStatusType) error {
_, err := db.GetEngine(ctx).Where(builder.Eq{
"content_type": contentType,
"content_id": contentID,
}).Cols("status").Update(&AbuseReport{Status: status})
return err
}
*/

View file

@ -0,0 +1,76 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package moderation
import (
"context"
"database/sql"
"fmt"
"forgejo.org/models/db"
"forgejo.org/modules/log"
"forgejo.org/modules/timeutil"
"xorm.io/builder"
)
type AbuseReportShadowCopy struct {
ID int64 `xorm:"pk autoincr"`
RawValue string `xorm:"NOT NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
}
// Returns the ID encapsulated in a sql.NullInt64 struct.
func (sc AbuseReportShadowCopy) NullableID() sql.NullInt64 {
return sql.NullInt64{Int64: sc.ID, Valid: sc.ID > 0}
}
func init() {
// RegisterModel will create the table if does not already exist
// or any missing columns if the table was previously created.
// It will not drop or rename existing columns (when struct has changed).
db.RegisterModel(new(AbuseReportShadowCopy))
}
func CreateShadowCopyForUser(ctx context.Context, userID int64, content string) error {
return createShadowCopy(ctx, ReportedContentTypeUser, userID, content)
}
func CreateShadowCopyForRepository(ctx context.Context, repoID int64, content string) error {
return createShadowCopy(ctx, ReportedContentTypeRepository, repoID, content)
}
func CreateShadowCopyForIssue(ctx context.Context, issueID int64, content string) error {
return createShadowCopy(ctx, ReportedContentTypeIssue, issueID, content)
}
func CreateShadowCopyForComment(ctx context.Context, commentID int64, content string) error {
return createShadowCopy(ctx, ReportedContentTypeComment, commentID, content)
}
func createShadowCopy(ctx context.Context, contentType ReportedContentType, contentID int64, content string) error {
err := db.WithTx(ctx, func(ctx context.Context) error {
sess := db.GetEngine(ctx)
shadowCopy := &AbuseReportShadowCopy{RawValue: content}
affected, err := sess.Insert(shadowCopy)
if err != nil {
return err
} else if affected == 0 {
log.Warn("Something went wrong while trying to create the shadow copy for reported content with type %d and ID %d.", contentType, contentID)
}
_, err = sess.Where(builder.Eq{
"content_type": contentType,
"content_id": contentID,
}).And(builder.IsNull{"shadow_copy_id"}).Update(&AbuseReport{ShadowCopyID: shadowCopy.NullableID()})
if err != nil {
return fmt.Errorf("could not link the shadow copy (%d) to reported content with type %d and ID %d - %w", shadowCopy.ID, contentType, contentID, err)
}
return nil
})
return err
}

View file

@ -0,0 +1,5 @@
-
id: 1000
uid: 4
org_id: 22
is_public: true

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