Merge pull request '[F3] move f3 under forgejo-cli' (#1148) from dachary/forgejo:wip-f3-cli into forgejo-f3

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/1148
This commit is contained in:
Loïc Dachary 2023-07-27 18:34:07 +00:00
commit 9835fbefa2
6 changed files with 150 additions and 187 deletions

136
cmd/f3.go
View file

@ -1,136 +0,0 @@
// SPDX-License-Identifier: MIT
package cmd
import (
"context"
"fmt"
auth_model "code.gitea.io/gitea/models/auth"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/services/f3/util"
"github.com/urfave/cli/v2"
f3_types "lab.forgefriends.org/friendlyforgeformat/gof3/config/types"
f3_common "lab.forgefriends.org/friendlyforgeformat/gof3/forges/common"
f3_format "lab.forgefriends.org/friendlyforgeformat/gof3/format"
)
var CmdF3 = &cli.Command{
Name: "f3",
Usage: "Friendly Forge Format (F3) format export/import.",
Description: "Import or export a repository from or to the Friendly Forge Format (F3) format.",
Action: runF3,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "directory",
Value: "./f3",
Usage: "Path of the directory where the F3 dump is stored",
},
&cli.StringFlag{
Name: "user",
Value: "",
Usage: "The name of the user who owns the repository",
},
&cli.StringFlag{
Name: "repository",
Value: "",
Usage: "The name of the repository",
},
&cli.StringFlag{
Name: "authentication-source",
Value: "",
Usage: "The name of the authentication source matching the forge of origin",
},
&cli.BoolFlag{
Name: "no-pull-request",
Usage: "Do not dump pull requests",
},
&cli.BoolFlag{
Name: "import",
Usage: "Import from the directory",
},
&cli.BoolFlag{
Name: "export",
Usage: "Export to the directory",
},
},
}
func runF3(ctx *cli.Context) error {
stdCtx, cancel := installSignals()
defer cancel()
if err := initDB(stdCtx); err != nil {
return err
}
if err := git.InitSimple(stdCtx); err != nil {
return err
}
return RunF3(stdCtx, ctx)
}
func getAuthenticationSource(ctx context.Context, authenticationSource string) (*auth_model.Source, error) {
source, err := auth_model.GetSourceByName(ctx, authenticationSource)
if err != nil {
if auth_model.IsErrSourceNotExist(err) {
return nil, nil
}
return nil, err
}
return source, nil
}
func RunF3(stdCtx context.Context, ctx *cli.Context) error {
doer, err := user_model.GetAdminUser(stdCtx)
if err != nil {
return err
}
features := f3_types.AllFeatures
if ctx.Bool("no-pull-request") {
features.PullRequests = false
}
var sourceID int64
sourceName := ctx.String("authentication-source")
source, err := getAuthenticationSource(stdCtx, sourceName)
if err != nil {
return fmt.Errorf("error retrieving the authentication-source %s %v", sourceName, err)
}
if source != nil {
sourceID = source.ID
}
forgejo := util.ForgejoForgeRoot(features, doer, sourceID)
f3 := util.F3ForgeRoot(features, ctx.String("directory"))
if ctx.Bool("export") {
forgejo.Forge.Users.List(stdCtx)
user := forgejo.Forge.Users.GetFromFormat(stdCtx, &f3_format.User{UserName: ctx.String("user")})
if user.IsNil() {
return fmt.Errorf("%s is not a known user", ctx.String("user"))
}
user.Projects.List(stdCtx)
project := user.Projects.GetFromFormat(stdCtx, &f3_format.Project{Name: ctx.String("repository")})
if project.IsNil() {
return fmt.Errorf("%s/%s is not a known repository", ctx.String("user"), ctx.String("repository"))
}
options := f3_common.NewMirrorOptionsRecurse(user, project)
f3.Forge.Mirror(stdCtx, forgejo.Forge, options)
fmt.Println("exported")
} else if ctx.Bool("import") {
options := f3_common.NewMirrorOptionsRecurse()
forgejo.Forge.Mirror(stdCtx, f3.Forge, options)
fmt.Println("imported")
} else {
return fmt.Errorf("either --import or --export must be specified")
}
return nil
}

View file

@ -132,8 +132,8 @@ func validateSecret(secret string) error {
} }
func RunRegister(ctx context.Context, cliCtx *cli.Context) error { func RunRegister(ctx context.Context, cliCtx *cli.Context) error {
var cancel context.CancelFunc
if !ContextGetNoInit(ctx) { if !ContextGetNoInit(ctx) {
var cancel context.CancelFunc
ctx, cancel = installSignals(ctx) ctx, cancel = installSignals(ctx)
defer cancel() defer cancel()

137
cmd/forgejo/f3.go Normal file
View file

@ -0,0 +1,137 @@
// SPDX-License-Identifier: MIT
package forgejo
import (
"context"
"fmt"
auth_model "code.gitea.io/gitea/models/auth"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/services/f3/util"
"github.com/urfave/cli/v2"
f3_types "lab.forgefriends.org/friendlyforgeformat/gof3/config/types"
f3_common "lab.forgefriends.org/friendlyforgeformat/gof3/forges/common"
f3_format "lab.forgefriends.org/friendlyforgeformat/gof3/format"
)
func CmdF3(ctx context.Context) *cli.Command {
return &cli.Command{
Name: "f3",
Usage: "Friendly Forge Format (F3) format export/import.",
Description: "Import or export a repository from or to the Friendly Forge Format (F3) format.",
Action: prepareWorkPathAndCustomConf(ctx, func(cliCtx *cli.Context) error { return RunF3(ctx, cliCtx) }),
Flags: []cli.Flag{
&cli.StringFlag{
Name: "directory",
Value: "./f3",
Usage: "Path of the directory where the F3 dump is stored",
},
&cli.StringFlag{
Name: "user",
Value: "",
Usage: "The name of the user who owns the repository",
},
&cli.StringFlag{
Name: "repository",
Value: "",
Usage: "The name of the repository",
},
&cli.StringFlag{
Name: "authentication-source",
Value: "",
Usage: "The name of the authentication source matching the forge of origin",
},
&cli.BoolFlag{
Name: "no-pull-request",
Usage: "Do not dump pull requests",
},
&cli.BoolFlag{
Name: "import",
Usage: "Import from the directory",
},
&cli.BoolFlag{
Name: "export",
Usage: "Export to the directory",
},
},
}
}
func getAuthenticationSource(ctx context.Context, authenticationSource string) (*auth_model.Source, error) {
source, err := auth_model.GetSourceByName(ctx, authenticationSource)
if err != nil {
if auth_model.IsErrSourceNotExist(err) {
return nil, nil
}
return nil, err
}
return source, nil
}
func RunF3(ctx context.Context, cliCtx *cli.Context) error {
var cancel context.CancelFunc
if !ContextGetNoInit(ctx) {
ctx, cancel = installSignals(ctx)
defer cancel()
if err := initDB(ctx); err != nil {
return err
}
if err := git.InitSimple(ctx); err != nil {
return err
}
}
doer, err := user_model.GetAdminUser(ctx)
if err != nil {
return err
}
features := f3_types.AllFeatures
if cliCtx.Bool("no-pull-request") {
features.PullRequests = false
}
var sourceID int64
sourceName := cliCtx.String("authentication-source")
source, err := getAuthenticationSource(ctx, sourceName)
if err != nil {
return fmt.Errorf("error retrieving the authentication-source %s %v", sourceName, err)
}
if source != nil {
sourceID = source.ID
}
forgejo := util.ForgejoForgeRoot(features, doer, sourceID)
f3 := util.F3ForgeRoot(features, cliCtx.String("directory"))
if cliCtx.Bool("export") {
forgejo.Forge.Users.List(ctx)
user := forgejo.Forge.Users.GetFromFormat(ctx, &f3_format.User{UserName: cliCtx.String("user")})
if user.IsNil() {
return fmt.Errorf("%s is not a known user", cliCtx.String("user"))
}
user.Projects.List(ctx)
project := user.Projects.GetFromFormat(ctx, &f3_format.Project{Name: cliCtx.String("repository")})
if project.IsNil() {
return fmt.Errorf("%s/%s is not a known repository", cliCtx.String("user"), cliCtx.String("repository"))
}
options := f3_common.NewMirrorOptionsRecurse(user, project)
f3.Forge.Mirror(ctx, forgejo.Forge, options)
fmt.Fprintln(ContextGetStdout(ctx), "exported")
} else if cliCtx.Bool("import") {
options := f3_common.NewMirrorOptionsRecurse()
forgejo.Forge.Mirror(ctx, f3.Forge, options)
fmt.Fprintln(ContextGetStdout(ctx), "imported")
} else {
return fmt.Errorf("either --import or --export must be specified")
}
return nil
}

View file

@ -36,6 +36,7 @@ func CmdForgejo(ctx context.Context) *cli.Command {
Flags: []cli.Flag{}, Flags: []cli.Flag{},
Subcommands: []*cli.Command{ Subcommands: []*cli.Command{
CmdActions(ctx), CmdActions(ctx),
CmdF3(ctx),
}, },
} }
} }

View file

@ -200,7 +200,6 @@ func newMainApp(subCmds ...*cli.Command) *cli.App {
CmdMigrate, CmdMigrate,
CmdKeys, CmdKeys,
CmdDoctor, CmdDoctor,
CmdF3,
CmdManager, CmdManager,
CmdEmbedded, CmdEmbedded,
CmdMigrateStorage, CmdMigrateStorage,

View file

@ -3,38 +3,28 @@
package integration package integration
import ( import (
"bytes"
"context" "context"
"io"
"net/url" "net/url"
"os"
"testing" "testing"
"code.gitea.io/gitea/cmd"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/services/migrations" "code.gitea.io/gitea/services/migrations"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/urfave/cli/v2"
f3_forges "lab.forgefriends.org/friendlyforgeformat/gof3/forges" f3_forges "lab.forgefriends.org/friendlyforgeformat/gof3/forges"
f3_util "lab.forgefriends.org/friendlyforgeformat/gof3/util" f3_util "lab.forgefriends.org/friendlyforgeformat/gof3/util"
) )
func Test_CmdF3(t *testing.T) { func Test_CmdF3(t *testing.T) {
onGiteaRun(t, func(*testing.T, *url.URL) { onGiteaRun(t, func(*testing.T, *url.URL) {
AllowLocalNetworks := setting.Migrations.AllowLocalNetworks defer test.MockVariable(&setting.F3.Enabled, true)()
setting.F3.Enabled = true defer test.MockVariable(&setting.Migrations.AllowLocalNetworks, true)()
setting.Migrations.AllowLocalNetworks = true // Gitea SDK (go-sdk) need to parse the AppVer from server response, so we must set it to a valid version string.
defer test.MockVariable(&setting.AppVer, "1.16.0")
// without migrations.Init() AllowLocalNetworks = true is not effective and // without migrations.Init() AllowLocalNetworks = true is not effective and
// a http call fails with "...migration can only call allowed HTTP servers..." // a http call fails with "...migration can only call allowed HTTP servers..."
migrations.Init() migrations.Init()
AppVer := setting.AppVer
// Gitea SDK (go-sdk) need to parse the AppVer from server response, so we must set it to a valid version string.
setting.AppVer = "1.16.0"
defer func() {
setting.Migrations.AllowLocalNetworks = AllowLocalNetworks
setting.AppVer = AppVer
}()
// //
// Step 1: create a fixture // Step 1: create a fixture
@ -54,24 +44,10 @@ func Test_CmdF3(t *testing.T) {
// //
// Step 2: import the fixture into Gitea // Step 2: import the fixture into Gitea
// //
cmd.CmdF3.Action = func(ctx *cli.Context) error { return cmd.RunF3(context.Background(), ctx) }
{ {
realStdout := os.Stdout // Backup Stdout output, err := cmdForgejoCaptureOutput(t, []string{"forgejo", "forgejo-cli", "f3", "--import", "--directory", fixture.ForgeRoot.GetDirectory()})
r, w, _ := os.Pipe() assert.NoError(t, err)
os.Stdout = w assert.EqualValues(t, "imported\n", output)
app := cli.NewApp()
app.Writer = w
app.ErrWriter = w
app.Commands = []*cli.Command{cmd.CmdF3}
assert.NoError(t, app.Run([]string{"forgejo", "f3", "--import", "--directory", fixture.ForgeRoot.GetDirectory()}))
w.Close()
var buf bytes.Buffer
io.Copy(&buf, r)
commandOutput := buf.String()
assert.EqualValues(t, "imported\n", commandOutput)
os.Stdout = realStdout
} }
// //
@ -79,23 +55,9 @@ func Test_CmdF3(t *testing.T) {
// //
directory := t.TempDir() directory := t.TempDir()
{ {
realStdout := os.Stdout // Backup Stdout output, err := cmdForgejoCaptureOutput(t, []string{"forgejo", "forgejo-cli", "f3", "--export", "--no-pull-request", "--user", fixture.UserFormat.UserName, "--repository", fixture.ProjectFormat.Name, "--directory", directory})
r, w, _ := os.Pipe() assert.NoError(t, err)
os.Stdout = w assert.EqualValues(t, "exported\n", output)
app := cli.NewApp()
app.Writer = w
app.ErrWriter = w
app.Commands = []*cli.Command{cmd.CmdF3}
assert.NoError(t, app.Run([]string{"forgejo", "f3", "--export", "--no-pull-request", "--user", fixture.UserFormat.UserName, "--repository", fixture.ProjectFormat.Name, "--directory", directory}))
w.Close()
var buf bytes.Buffer
io.Copy(&buf, r)
commandOutput := buf.String()
assert.EqualValues(t, "exported\n", commandOutput)
os.Stdout = realStdout
} }
// //