[CLI] implement forgejo-cli

(cherry picked from commit 2555e315f7561302484b15576d34c5da0d4cdb12)
(cherry picked from commit 987df92935ecc03e36f1754b7cbe7a81df8f000f)
(cherry picked from commit 78e7eee59ae43b0e38ff5ab4cdf7fbca33cb6789)
This commit is contained in:
Earl Warren 2023-07-09 14:52:21 +02:00
parent f9618b8896
commit 935fb85e8b
No known key found for this signature in database
GPG key ID: 0579CB2928A78A00
3 changed files with 200 additions and 1 deletions

124
cmd/forgejo/forgejo.go Normal file
View 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
View file

@ -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,

View 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
}