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:
commit
9835fbefa2
6 changed files with 150 additions and 187 deletions
136
cmd/f3.go
136
cmd/f3.go
|
@ -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
|
||||
}
|
|
@ -132,8 +132,8 @@ func validateSecret(secret string) error {
|
|||
}
|
||||
|
||||
func RunRegister(ctx context.Context, cliCtx *cli.Context) error {
|
||||
if !ContextGetNoInit(ctx) {
|
||||
var cancel context.CancelFunc
|
||||
if !ContextGetNoInit(ctx) {
|
||||
ctx, cancel = installSignals(ctx)
|
||||
defer cancel()
|
||||
|
||||
|
|
137
cmd/forgejo/f3.go
Normal file
137
cmd/forgejo/f3.go
Normal 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
|
||||
}
|
|
@ -36,6 +36,7 @@ func CmdForgejo(ctx context.Context) *cli.Command {
|
|||
Flags: []cli.Flag{},
|
||||
Subcommands: []*cli.Command{
|
||||
CmdActions(ctx),
|
||||
CmdF3(ctx),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -200,7 +200,6 @@ func newMainApp(subCmds ...*cli.Command) *cli.App {
|
|||
CmdMigrate,
|
||||
CmdKeys,
|
||||
CmdDoctor,
|
||||
CmdF3,
|
||||
CmdManager,
|
||||
CmdEmbedded,
|
||||
CmdMigrateStorage,
|
||||
|
|
|
@ -3,38 +3,28 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/cmd"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/services/migrations"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/urfave/cli/v2"
|
||||
f3_forges "lab.forgefriends.org/friendlyforgeformat/gof3/forges"
|
||||
f3_util "lab.forgefriends.org/friendlyforgeformat/gof3/util"
|
||||
)
|
||||
|
||||
func Test_CmdF3(t *testing.T) {
|
||||
onGiteaRun(t, func(*testing.T, *url.URL) {
|
||||
AllowLocalNetworks := setting.Migrations.AllowLocalNetworks
|
||||
setting.F3.Enabled = true
|
||||
setting.Migrations.AllowLocalNetworks = true
|
||||
defer test.MockVariable(&setting.F3.Enabled, true)()
|
||||
defer test.MockVariable(&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
|
||||
// a http call fails with "...migration can only call allowed HTTP servers..."
|
||||
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
|
||||
|
@ -54,24 +44,10 @@ func Test_CmdF3(t *testing.T) {
|
|||
//
|
||||
// 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
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stdout = w
|
||||
|
||||
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
|
||||
output, err := cmdForgejoCaptureOutput(t, []string{"forgejo", "forgejo-cli", "f3", "--import", "--directory", fixture.ForgeRoot.GetDirectory()})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "imported\n", output)
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -79,23 +55,9 @@ func Test_CmdF3(t *testing.T) {
|
|||
//
|
||||
directory := t.TempDir()
|
||||
{
|
||||
realStdout := os.Stdout // Backup Stdout
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stdout = w
|
||||
|
||||
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
|
||||
|
||||
output, err := cmdForgejoCaptureOutput(t, []string{"forgejo", "forgejo-cli", "f3", "--export", "--no-pull-request", "--user", fixture.UserFormat.UserName, "--repository", fixture.ProjectFormat.Name, "--directory", directory})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "exported\n", output)
|
||||
}
|
||||
|
||||
//
|
Loading…
Reference in a new issue