[CLI] implement forgejo-cli
(cherry picked from commit 2555e315f7561302484b15576d34c5da0d4cdb12) (cherry picked from commit 987df92935ecc03e36f1754b7cbe7a81df8f000f) (cherry picked from commit 78e7eee59ae43b0e38ff5ab4cdf7fbca33cb6789)
This commit is contained in:
parent
f9618b8896
commit
935fb85e8b
3 changed files with 200 additions and 1 deletions
124
cmd/forgejo/forgejo.go
Normal file
124
cmd/forgejo/forgejo.go
Normal file
|
@ -0,0 +1,124 @@
|
|||
// Copyright The Forgejo Authors.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package forgejo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
type key int
|
||||
|
||||
const (
|
||||
noInstallSignalsKey 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 ContextSetNoInstallSignals(ctx context.Context, value bool) context.Context {
|
||||
return context.WithValue(ctx, noInstallSignalsKey, value)
|
||||
}
|
||||
|
||||
func ContextGetNoInstallSignals(ctx context.Context) bool {
|
||||
value, ok := ctx.Value(noInstallSignalsKey).(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
|
||||
}
|
||||
|
||||
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.ContextSetNoInstallSignals(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…
Reference in a new issue