[CLI] implement forgejo-cli
(cherry picked from commit2555e315f7) (cherry picked from commit51b9c9092e) [CLI] implement forgejo-cli (squash) support initDB (cherry picked from commit5c31ae602a)
This commit is contained in:
		
							parent
							
								
									e28a47741d
								
							
						
					
					
						commit
						bbf76489a7
					
				
					 3 changed files with 221 additions and 1 deletions
				
			
		
							
								
								
									
										145
									
								
								cmd/forgejo/forgejo.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								cmd/forgejo/forgejo.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,145 @@
 | 
			
		|||
// Copyright The Forgejo Authors.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package forgejo
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/signal"
 | 
			
		||||
	"syscall"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models/db"
 | 
			
		||||
	"code.gitea.io/gitea/modules/log"
 | 
			
		||||
	"code.gitea.io/gitea/modules/private"
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
 | 
			
		||||
	"github.com/urfave/cli"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type key int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	noInitKey key = iota + 1
 | 
			
		||||
	noExitKey
 | 
			
		||||
	stdoutKey
 | 
			
		||||
	stderrKey
 | 
			
		||||
	stdinKey
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func CmdForgejo(ctx context.Context) cli.Command {
 | 
			
		||||
	return cli.Command{
 | 
			
		||||
		Name:        "forgejo-cli",
 | 
			
		||||
		Usage:       "Forgejo CLI",
 | 
			
		||||
		Flags:       []cli.Flag{},
 | 
			
		||||
		Subcommands: []cli.Command{},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ContextSetNoInit(ctx context.Context, value bool) context.Context {
 | 
			
		||||
	return context.WithValue(ctx, noInitKey, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ContextGetNoInit(ctx context.Context) bool {
 | 
			
		||||
	value, ok := ctx.Value(noInitKey).(bool)
 | 
			
		||||
	return ok && value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ContextSetNoExit(ctx context.Context, value bool) context.Context {
 | 
			
		||||
	return context.WithValue(ctx, noExitKey, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ContextGetNoExit(ctx context.Context) bool {
 | 
			
		||||
	value, ok := ctx.Value(noExitKey).(bool)
 | 
			
		||||
	return ok && value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ContextSetStderr(ctx context.Context, value io.Writer) context.Context {
 | 
			
		||||
	return context.WithValue(ctx, stderrKey, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ContextGetStderr(ctx context.Context) io.Writer {
 | 
			
		||||
	value, ok := ctx.Value(stderrKey).(io.Writer)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return os.Stderr
 | 
			
		||||
	}
 | 
			
		||||
	return value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ContextSetStdout(ctx context.Context, value io.Writer) context.Context {
 | 
			
		||||
	return context.WithValue(ctx, stdoutKey, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ContextGetStdout(ctx context.Context) io.Writer {
 | 
			
		||||
	value, ok := ctx.Value(stderrKey).(io.Writer)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return os.Stdout
 | 
			
		||||
	}
 | 
			
		||||
	return value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ContextSetStdin(ctx context.Context, value io.Reader) context.Context {
 | 
			
		||||
	return context.WithValue(ctx, stdinKey, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ContextGetStdin(ctx context.Context) io.Reader {
 | 
			
		||||
	value, ok := ctx.Value(stdinKey).(io.Reader)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return os.Stdin
 | 
			
		||||
	}
 | 
			
		||||
	return value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// copied from ../cmd.go
 | 
			
		||||
func initDB(ctx context.Context) error {
 | 
			
		||||
	setting.MustInstalled()
 | 
			
		||||
	setting.LoadDBSetting()
 | 
			
		||||
	setting.InitSQLLoggersForCli(log.INFO)
 | 
			
		||||
 | 
			
		||||
	if setting.Database.Type == "" {
 | 
			
		||||
		log.Fatal(`Database settings are missing from the configuration file: %q.
 | 
			
		||||
Ensure you are running in the correct environment or set the correct configuration file with -c.
 | 
			
		||||
If this is the intended configuration file complete the [database] section.`, setting.CustomConf)
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.InitEngine(ctx); err != nil {
 | 
			
		||||
		return fmt.Errorf("unable to initialize the database using the configuration in %q. Error: %w", setting.CustomConf, err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// copied from ../cmd.go
 | 
			
		||||
func installSignals(ctx context.Context) (context.Context, context.CancelFunc) {
 | 
			
		||||
	ctx, cancel := context.WithCancel(ctx)
 | 
			
		||||
	go func() {
 | 
			
		||||
		// install notify
 | 
			
		||||
		signalChannel := make(chan os.Signal, 1)
 | 
			
		||||
 | 
			
		||||
		signal.Notify(
 | 
			
		||||
			signalChannel,
 | 
			
		||||
			syscall.SIGINT,
 | 
			
		||||
			syscall.SIGTERM,
 | 
			
		||||
		)
 | 
			
		||||
		select {
 | 
			
		||||
		case <-signalChannel:
 | 
			
		||||
		case <-ctx.Done():
 | 
			
		||||
		}
 | 
			
		||||
		cancel()
 | 
			
		||||
		signal.Reset()
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	return ctx, cancel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleCliResponseExtra(ctx context.Context, extra private.ResponseExtra) error {
 | 
			
		||||
	if false && extra.UserMsg != "" {
 | 
			
		||||
		if _, err := fmt.Fprintf(ContextGetStdout(ctx), "%s", extra.UserMsg); err != nil {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if ContextGetNoExit(ctx) {
 | 
			
		||||
		return extra.Error
 | 
			
		||||
	}
 | 
			
		||||
	return cli.NewExitError(extra.Error, 1)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								main.go
									
										
									
									
									
								
							
							
						
						
									
										38
									
								
								main.go
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -6,13 +6,16 @@
 | 
			
		|||
package main // import "code.gitea.io/gitea"
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/cmd"
 | 
			
		||||
	"code.gitea.io/gitea/cmd/forgejo"
 | 
			
		||||
	"code.gitea.io/gitea/modules/log"
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -81,6 +84,39 @@ DEFAULT CONFIGURATION:
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	path, err := os.Executable()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	executable := filepath.Base(path)
 | 
			
		||||
 | 
			
		||||
	var subCmds []cli.Command
 | 
			
		||||
 | 
			
		||||
	//
 | 
			
		||||
	// If the executable is forgejo-cli, provide a Forgejo specific CLI
 | 
			
		||||
	// that is NOT compatible with Gitea.
 | 
			
		||||
	//
 | 
			
		||||
	if executable == "forgejo-cli" {
 | 
			
		||||
		subCmds = []cli.Command{
 | 
			
		||||
			forgejo.CmdActions(context.Background()),
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		//
 | 
			
		||||
		// Otherwise provide a Gitea compatible CLI which includes Forgejo
 | 
			
		||||
		// specific additions under the forgejo-cli subcommand. It allows
 | 
			
		||||
		// admins to migration from Gitea to Forgejo by replacing the gitea
 | 
			
		||||
		// binary and rename it to forgejo if they want.
 | 
			
		||||
		//
 | 
			
		||||
		subCmds = []cli.Command{
 | 
			
		||||
			forgejo.CmdForgejo(context.Background()),
 | 
			
		||||
			cmd.CmdActions,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mainApp(subCmds...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func mainApp(subCmds ...cli.Command) {
 | 
			
		||||
	app := cli.NewApp()
 | 
			
		||||
	app.Name = "Gitea"
 | 
			
		||||
	app.Usage = "A painless self-hosted Git service"
 | 
			
		||||
| 
						 | 
				
			
			@ -104,9 +140,9 @@ func main() {
 | 
			
		|||
		cmd.CmdMigrateStorage,
 | 
			
		||||
		cmd.CmdDumpRepository,
 | 
			
		||||
		cmd.CmdRestoreRepository,
 | 
			
		||||
		cmd.CmdActions,
 | 
			
		||||
		cmdHelp, // TODO: the "help" sub-command was used to show the more information for "work path" and "custom config", in the future, it should avoid doing so
 | 
			
		||||
	}
 | 
			
		||||
	subCmdWithIni = append(subCmdWithIni, subCmds...)
 | 
			
		||||
	// these sub-commands do not need the config file, and they do not depend on any path or environment variable.
 | 
			
		||||
	subCmdStandalone := []cli.Command{
 | 
			
		||||
		cmd.CmdCert,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										39
									
								
								tests/integration/cmd_forgejo_test.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								tests/integration/cmd_forgejo_test.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,39 @@
 | 
			
		|||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package integration
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"context"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/cmd/forgejo"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
	"github.com/urfave/cli"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func cmdForgejoCaptureOutput(t *testing.T, args []string) (string, error) {
 | 
			
		||||
	r, w, err := os.Pipe()
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	set := flag.NewFlagSet("forgejo-cli", 0)
 | 
			
		||||
	assert.NoError(t, set.Parse(args))
 | 
			
		||||
	cliContext := cli.NewContext(&cli.App{Writer: w, ErrWriter: w}, set, nil)
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	ctx = forgejo.ContextSetNoInit(ctx, true)
 | 
			
		||||
	ctx = forgejo.ContextSetNoExit(ctx, true)
 | 
			
		||||
	ctx = forgejo.ContextSetStdout(ctx, w)
 | 
			
		||||
	ctx = forgejo.ContextSetStderr(ctx, w)
 | 
			
		||||
	if len(stdin) > 0 {
 | 
			
		||||
		ctx = forgejo.ContextSetStdin(ctx, strings.NewReader(strings.Join(stdin, "")))
 | 
			
		||||
	}
 | 
			
		||||
	err = forgejo.CmdForgejo(ctx).Run(cliContext)
 | 
			
		||||
	w.Close()
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	io.Copy(&buf, r)
 | 
			
		||||
	return buf.String(), err
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue