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 {
|
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
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{},
|
Flags: []cli.Flag{},
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
CmdActions(ctx),
|
CmdActions(ctx),
|
||||||
|
CmdF3(ctx),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,7 +200,6 @@ func newMainApp(subCmds ...*cli.Command) *cli.App {
|
||||||
CmdMigrate,
|
CmdMigrate,
|
||||||
CmdKeys,
|
CmdKeys,
|
||||||
CmdDoctor,
|
CmdDoctor,
|
||||||
CmdF3,
|
|
||||||
CmdManager,
|
CmdManager,
|
||||||
CmdEmbedded,
|
CmdEmbedded,
|
||||||
CmdMigrateStorage,
|
CmdMigrateStorage,
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
Loading…
Reference in a new issue