snapshot of https://github.com/ory/keto.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
CI / Run lints and checks (push) Failing after 47s
CI / Run tests (push) Failing after 1s
CI / Run race tests (push) Failing after 5s
CI / Build CLI docs (push) Has been skipped
CI / Run buf actions (push) Failing after 5s
CodeQL / Analyze (go) (push) Failing after 3s
CodeQL / Analyze (javascript) (push) Failing after 2s
Docker Image Scanners / scanners (push) Failing after 3s
Format / format (push) Failing after 3s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Draft newsletter (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped

This commit is contained in:
upstream-mirror
2026-09-16 01:08:34 +05:00
commit 9456a4e922
2560 changed files with 120911 additions and 0 deletions

31
oryx/cmdx/env.go Normal file
View File

@@ -0,0 +1,31 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package cmdx
// EnvVarExamplesHelpMessage returns a string containing documentation on how to use environment variables.
func EnvVarExamplesHelpMessage(name string) string {
return `This command exposes a variety of controls via environment variables. Here are some examples on how to
configure environment variables:
Linux / macOS:
$ export FOO=bar
$ export BAZ=bar
$ ` + name + ` ...
$ FOO=bar BAZ=bar ` + name + ` ...
Docker:
$ docker run -e FOO=bar -e BAZ=bar ...
Windows (cmd):
> set FOO=bar
> set BAZ=bar
> ` + name + ` ...
Windows (powershell):
> $env:FOO = "bar"
> $env:BAZ = "bar"
> ` + name + `
`
}

247
oryx/cmdx/helper.go Normal file
View File

@@ -0,0 +1,247 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package cmdx
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"testing"
"golang.org/x/sync/errgroup"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/stretchr/testify/require"
"github.com/pkg/errors"
)
var (
// ErrNoPrintButFail is returned to detect a failure state that was already reported to the user in some way
ErrNoPrintButFail = fmt.Errorf("this error should never be printed")
debugStdout, debugStderr = io.Discard, io.Discard
)
func init() {
if os.Getenv("DEBUG") != "" {
debugStdout = os.Stdout
debugStderr = os.Stderr
}
}
// FailSilently is supposed to be used within a commands RunE function.
// It silences cobras error handling and returns the ErrNoPrintButFail error.
func FailSilently(cmd *cobra.Command) error {
cmd.SilenceErrors = true
cmd.SilenceUsage = true
return errors.WithStack(ErrNoPrintButFail)
}
// Must fatals with the optional message if err is not nil.
// Deprecated: do not use this function in commands, as it makes it impossible to test them. Instead, return the error.
func Must(err error, message string, args ...interface{}) {
if err == nil {
return
}
_, _ = fmt.Fprintf(os.Stderr, message+"\n", args...)
os.Exit(1)
}
// CheckResponse fatals if err is nil or the response.StatusCode does not match the expectedStatusCode
// Deprecated: do not use this function in commands, as it makes it impossible to test them. Instead, return the error.
func CheckResponse(err error, expectedStatusCode int, response *http.Response) {
Must(err, "Command failed because error occurred: %s", err)
if response.StatusCode != expectedStatusCode {
out, err := io.ReadAll(response.Body)
if err != nil {
out = []byte{}
}
pretty, err := json.MarshalIndent(json.RawMessage(out), "", "\t")
if err == nil {
out = pretty
}
Fatalf(
`Command failed because status code %d was expected but code %d was received.
Response payload:
%s`,
expectedStatusCode,
response.StatusCode,
out,
)
}
}
// FormatResponse takes an object and prints a json.MarshalIdent version of it or fatals.
// Deprecated: do not use this function in commands, as it makes it impossible to test them. Instead, return the error.
func FormatResponse(o interface{}) string {
out, err := json.MarshalIndent(o, "", "\t")
Must(err, `Command failed because an error occurred while prettifying output: %s`, err)
return string(out)
}
// Fatalf prints to os.Stderr and exists with code 1.
// Deprecated: do not use this function in commands, as it makes it impossible to test them. Instead, return the error.
func Fatalf(message string, args ...interface{}) {
if len(args) > 0 {
_, _ = fmt.Fprintf(os.Stderr, message+"\n", args...)
} else {
_, _ = fmt.Fprintln(os.Stderr, message)
}
os.Exit(1)
}
// CallbackWriter will execute each callback once the message is received.
// The full matched message is passed to the callback. An error returned from the callback is returned by Write.
type CallbackWriter struct {
Callbacks map[string]func([]byte) error
buf bytes.Buffer
}
func (c *CallbackWriter) Write(msg []byte) (int, error) {
for p, cb := range c.Callbacks {
if bytes.Contains(msg, []byte(p)) {
if err := cb(msg); err != nil {
return 0, err
}
}
}
return c.buf.Write(msg)
}
func (c *CallbackWriter) String() string {
return c.buf.String()
}
var _ io.Writer = (*CallbackWriter)(nil)
func prepareCmd(cmd *cobra.Command, stdIn io.Reader, stdOut, stdErr io.Writer, args []string) {
cmd.SetIn(stdIn)
outs := []io.Writer{debugStdout}
if stdOut != nil {
outs = append(outs, stdOut)
}
cmd.SetOut(io.MultiWriter(outs...))
errs := []io.Writer{debugStderr}
if stdErr != nil {
errs = append(errs, stdErr)
}
cmd.SetErr(io.MultiWriter(errs...))
if args == nil {
args = []string{}
}
cmd.SetArgs(args)
}
// ExecBackgroundCtx runs the cobra command in the background.
func ExecBackgroundCtx(ctx context.Context, cmd *cobra.Command, stdIn io.Reader, stdOut, stdErr io.Writer, args ...string) *errgroup.Group {
prepareCmd(cmd, stdIn, stdOut, stdErr, args)
eg := &errgroup.Group{}
eg.Go(func() error {
defer cmd.SetIn(nil)
return cmd.ExecuteContext(ctx)
})
return eg
}
// Exec runs the provided cobra command with the given reader as STD_IN and the given args.
// Returns STD_OUT, STD_ERR and the error from the execution.
func Exec(t testing.TB, cmd *cobra.Command, stdIn io.Reader, args ...string) (string, string, error) {
return ExecCtx(t.Context(), cmd, stdIn, args...)
}
func ExecCtx(ctx context.Context, cmd *cobra.Command, stdIn io.Reader, args ...string) (string, string, error) {
stdOut, stdErr := &bytes.Buffer{}, &bytes.Buffer{}
prepareCmd(cmd, stdIn, stdOut, stdErr, args)
// needs to be on a separate line to ensure that the output buffers are read AFTER the command ran
err := cmd.ExecuteContext(ctx)
return stdOut.String(), stdErr.String(), err
}
// ExecNoErr is a helper that assumes a successful run from Exec.
// Returns STD_OUT.
func ExecNoErr(t testing.TB, cmd *cobra.Command, args ...string) string {
return ExecNoErrCtx(t.Context(), t, cmd, args...)
}
func ExecNoErrCtx(ctx context.Context, t require.TestingT, cmd *cobra.Command, args ...string) string {
stdOut, stdErr, err := ExecCtx(ctx, cmd, nil, args...)
if err == nil {
require.Len(t, stdErr, 0, "std_out: %s\nstd_err: %s", stdOut, stdErr)
} else {
require.ErrorIsf(t, err, context.Canceled, "std_out: %s\nstd_err: %s", stdOut, stdErr)
}
return stdOut
}
// ExecExpectedErr is a helper that assumes a failing run from Exec returning ErrNoPrintButFail
// Returns STD_ERR.
func ExecExpectedErr(t testing.TB, cmd *cobra.Command, args ...string) string {
return ExecExpectedErrCtx(t.Context(), t, cmd, args...)
}
func ExecExpectedErrCtx(ctx context.Context, t require.TestingT, cmd *cobra.Command, args ...string) string {
stdOut, stdErr, err := ExecCtx(ctx, cmd, nil, args...)
require.True(t, errors.Is(err, ErrNoPrintButFail), "std_out: %s\nstd_err: %s", stdOut, stdErr)
require.Len(t, stdOut, 0, stdErr)
return stdErr
}
type CommandExecuter struct {
New func() *cobra.Command
Ctx context.Context
PersistentArgs []string
}
func (c *CommandExecuter) Exec(stdin io.Reader, args ...string) (string, string, error) {
return ExecCtx(c.Ctx, c.New(), stdin, append(c.PersistentArgs, args...)...)
}
func (c *CommandExecuter) ExecBackground(stdin io.Reader, stdOut, stdErr io.Writer, args ...string) *errgroup.Group {
return ExecBackgroundCtx(c.Ctx, c.New(), stdin, stdOut, stdErr, append(c.PersistentArgs, args...)...)
}
func (c *CommandExecuter) ExecNoErr(t require.TestingT, args ...string) string {
return ExecNoErrCtx(c.Ctx, t, c.New(), append(c.PersistentArgs, args...)...)
}
func (c *CommandExecuter) ExecExpectedErr(t require.TestingT, args ...string) string {
return ExecExpectedErrCtx(c.Ctx, t, c.New(), append(c.PersistentArgs, args...)...)
}
type URL struct {
url.URL
}
var _ pflag.Value = (*URL)(nil)
func (u *URL) Set(s string) error {
uu, err := url.Parse(s)
if err != nil {
return err
}
u.URL = *uu
return nil
}
func (*URL) Type() string {
return "url"
}

125
oryx/cmdx/http.go Normal file
View File

@@ -0,0 +1,125 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package cmdx
import (
"crypto/tls"
"fmt"
"net/http"
"net/url"
"os"
"strings"
"time"
"github.com/hashicorp/go-retryablehttp"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/ory/x/httpx"
)
const (
envKeyEndpoint = "ORY_SDK_URL"
FlagEndpoint = "endpoint"
FlagSkipTLSVerify = "skip-tls-verify"
FlagHeaders = "http-header"
)
// Remote returns the remote endpoint for the given command.
func Remote(cmd *cobra.Command) (string, error) {
endpoint, err := cmd.Flags().GetString(FlagEndpoint)
if err != nil {
return "", errors.WithStack(err)
}
if endpoint != "" {
return strings.TrimRight(endpoint, "/"), nil
} else if endpoint := os.Getenv("ORY_SDK_URL"); endpoint != "" {
return strings.TrimRight(endpoint, "/"), nil
}
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "To execute this command, the endpoint URL must point to the URL where Ory is located. To set the endpoint URL, use flag `--endpoint` or environment variable `ORY_SDK_URL`.")
return "", FailSilently(cmd)
}
// RemoteURI returns the remote URI for the given command.
func RemoteURI(cmd *cobra.Command) (*url.URL, error) {
remote, err := Remote(cmd)
if err != nil {
return nil, err
}
endpoint, err := url.ParseRequestURI(remote)
if err != nil {
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "Could not parse endpoint URL: %s", err)
return nil, err
}
return endpoint, nil
}
// NewClient creates a new HTTP client.
func NewClient(cmd *cobra.Command) (*http.Client, *url.URL, error) {
endpoint, err := cmd.Flags().GetString(FlagEndpoint)
if err != nil {
return nil, nil, errors.WithStack(err)
}
if endpoint == "" {
endpoint = os.Getenv(envKeyEndpoint)
}
if endpoint == "" {
return nil, nil, errors.Errorf("you have to set the remote endpoint, try --help for details")
}
u, err := url.Parse(strings.TrimRight(endpoint, "/"))
if err != nil {
return nil, nil, errors.Wrapf(err, `could not parse the endpoint URL "%s"`, endpoint)
}
hc := retryablehttp.NewClient().StandardClient()
hc.Timeout = time.Second * 10
rawHeaders, err := cmd.Flags().GetStringSlice(FlagHeaders)
if err != nil {
return nil, nil, errors.WithStack(err)
}
header := http.Header{}
for _, h := range rawHeaders {
parts := strings.Split(h, ":")
if len(parts) != 2 {
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Unable to parse `--http-header` flag. Format of flag value is a `: ` delimited string like `--http-header 'Some-Header: some-values; other values`. Received: %v", rawHeaders)
return nil, nil, FailSilently(cmd)
}
for k := range parts {
parts[k] = strings.TrimSpace(parts[k])
}
header.Add(parts[0], parts[1])
}
skipVerify, err := cmd.Flags().GetBool(FlagSkipTLSVerify)
if err != nil {
return nil, nil, errors.WithStack(err)
}
rt := httpx.NewTransportWithHeader(header)
rt.RoundTripper = &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: skipVerify, //nolint:gosec // This is a false positive
},
}
hc.Transport = rt
return hc, u, nil
}
// RegisterHTTPClientFlags registers HTTP client configuration flags.
func RegisterHTTPClientFlags(flags *pflag.FlagSet) {
flags.StringP(FlagEndpoint, FlagEndpoint[:1], "", fmt.Sprintf("The API URL this command should target. Alternatively set using the %s environmental variable.", envKeyEndpoint))
flags.Bool(FlagSkipTLSVerify, false, "Do not verify TLS certificates. Useful when dealing with self-signed certificates. Do not use in production!")
flags.StringSliceP(FlagHeaders, "H", []string{}, "A list of additional HTTP headers to set. HTTP headers is separated by a `: `, for example: `-H 'Authorization: bearer some-token'`.")
}

137
oryx/cmdx/noise_printer.go Normal file
View File

@@ -0,0 +1,137 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package cmdx
import (
"fmt"
"io"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
type ConditionalPrinter struct {
w io.Writer
print bool
}
const (
FlagQuiet = "quiet"
)
func RegisterNoiseFlags(flags *pflag.FlagSet) {
flags.BoolP(FlagQuiet, FlagQuiet[:1], false, "Be quiet with output printing.")
}
// NewLoudOutPrinter returns a ConditionalPrinter that
// only prints to cmd.OutOrStdout when --quiet is not set
func NewLoudOutPrinter(cmd *cobra.Command) *ConditionalPrinter {
quiet, err := cmd.Flags().GetBool(FlagQuiet)
if err != nil {
Fatalf(err.Error())
}
return &ConditionalPrinter{
w: cmd.OutOrStdout(),
print: !quiet,
}
}
// NewQuietOutPrinter returns a ConditionalPrinter that
// only prints to cmd.OutOrStdout when --quiet is set
func NewQuietOutPrinter(cmd *cobra.Command) *ConditionalPrinter {
quiet, err := cmd.Flags().GetBool(FlagQuiet)
if err != nil {
Fatalf(err.Error())
}
return &ConditionalPrinter{
w: cmd.OutOrStdout(),
print: quiet,
}
}
// NewLoudErrPrinter returns a ConditionalPrinter that
// only prints to cmd.ErrOrStderr when --quiet is not set
func NewLoudErrPrinter(cmd *cobra.Command) *ConditionalPrinter {
quiet, err := cmd.Flags().GetBool(FlagQuiet)
if err != nil {
Fatalf(err.Error())
}
return &ConditionalPrinter{
w: cmd.ErrOrStderr(),
print: !quiet,
}
}
// NewQuietErrPrinter returns a ConditionalPrinter that
// only prints to cmd.ErrOrStderr when --quiet is set
func NewQuietErrPrinter(cmd *cobra.Command) *ConditionalPrinter {
quiet, err := cmd.Flags().GetBool(FlagQuiet)
if err != nil {
Fatalf(err.Error())
}
return &ConditionalPrinter{
w: cmd.ErrOrStderr(),
print: quiet,
}
}
// NewLoudPrinter returns a ConditionalPrinter that
// only prints to w when --quiet is not set
func NewLoudPrinter(cmd *cobra.Command, w io.Writer) *ConditionalPrinter {
quiet, err := cmd.Flags().GetBool(FlagQuiet)
if err != nil {
Fatalf(err.Error())
}
return &ConditionalPrinter{
w: w,
print: !quiet,
}
}
// NewQuietPrinter returns a ConditionalPrinter that
// only prints to w when --quiet is set
func NewQuietPrinter(cmd *cobra.Command, w io.Writer) *ConditionalPrinter {
quiet, err := cmd.Flags().GetBool(FlagQuiet)
if err != nil {
Fatalf(err.Error())
}
return &ConditionalPrinter{
w: w,
print: quiet,
}
}
func NewConditionalPrinter(w io.Writer, print bool) *ConditionalPrinter {
return &ConditionalPrinter{
w: w,
print: print,
}
}
func (p *ConditionalPrinter) Println(a ...interface{}) (n int, err error) {
if p.print {
return fmt.Fprintln(p.w, a...)
}
return
}
func (p *ConditionalPrinter) Print(a ...interface{}) (n int, err error) {
if p.print {
return fmt.Fprint(p.w, a...)
}
return
}
func (p *ConditionalPrinter) Printf(format string, a ...interface{}) (n int, err error) {
if p.print {
return fmt.Fprintf(p.w, format, a...)
}
return
}

84
oryx/cmdx/output.go Normal file
View File

@@ -0,0 +1,84 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package cmdx
import "strconv"
type (
// OutputIder outputs an ID
OutputIder string
// OutputIderCollection outputs a list of IDs
OutputIderCollection struct {
Items []OutputIder
}
)
func (OutputIder) Header() []string {
return []string{"ID"}
}
func (i OutputIder) Columns() []string {
return []string{string(i)}
}
func (i OutputIder) Interface() interface{} {
return i
}
func (OutputIderCollection) Header() []string {
return []string{"ID"}
}
func (c OutputIderCollection) Table() [][]string {
rows := make([][]string, len(c.Items))
for i, ident := range c.Items {
rows[i] = []string{string(ident)}
}
return rows
}
func (c OutputIderCollection) Interface() interface{} {
return c.Items
}
func (c OutputIderCollection) Len() int {
return len(c.Items)
}
type PaginatedList struct {
Collection interface {
Table
IDs() []string
} `json:"-"`
Items []interface{} `json:"items"`
NextPageToken string `json:"next_page_token"`
IsLastPage bool `json:"is_last_page"`
}
func (r *PaginatedList) Header() []string {
return r.Collection.Header()
}
func (r *PaginatedList) Table() [][]string {
return append(
r.Collection.Table(),
[]string{},
[]string{"NEXT PAGE TOKEN", r.NextPageToken},
[]string{"IS LAST PAGE", strconv.FormatBool(r.IsLastPage)},
)
}
func (r *PaginatedList) Interface() interface{} {
return r
}
func (r *PaginatedList) Len() int {
return r.Collection.Len() + 3
}
func (r *PaginatedList) IDs() []string {
return r.Collection.IDs()
}
var _ Table = (*PaginatedList)(nil)

57
oryx/cmdx/pagination.go Normal file
View File

@@ -0,0 +1,57 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package cmdx
import (
"fmt"
"strconv"
"github.com/spf13/cobra"
)
const (
FlagPageSize = "page-size"
FlagPageToken = "page-token"
)
func RegisterTokenPaginationFlags(cmd *cobra.Command) (pageSize int, pageToken string) {
cmd.Flags().StringVar(&pageToken, FlagPageToken, "", "page token acquired from a previous response")
cmd.Flags().IntVar(&pageSize, FlagPageSize, 100, "maximum number of items to return")
return
}
// ParsePaginationArgs parses pagination arguments from the command line.
func ParsePaginationArgs(cmd *cobra.Command, pageArg, perPageArg string) (page, perPage int64, err error) {
if len(pageArg+perPageArg) > 0 {
page, err = strconv.ParseInt(pageArg, 0, 64)
if err != nil {
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "Could not parse page argument\"%s\": %s", pageArg, err)
return 0, 0, FailSilently(cmd)
}
perPage, err = strconv.ParseInt(perPageArg, 0, 64)
if err != nil {
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "Could not parse per-page argument\"%s\": %s", perPageArg, err)
return 0, 0, FailSilently(cmd)
}
}
return
}
// ParseTokenPaginationArgs parses token-based pagination arguments from the command line.
func ParseTokenPaginationArgs(cmd *cobra.Command) (page string, perPage int, err error) {
pageArg, err := cmd.Flags().GetString(FlagPageToken)
if err != nil {
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "Could not parse %s argument \"%s\": %s", FlagPageToken, pageArg, err)
return "", 0, FailSilently(cmd)
}
perPageArg, err := cmd.Flags().GetInt(FlagPageSize)
if err != nil {
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "Could not parse %s argument \"%d\": %s", FlagPageSize, perPageArg, err)
return "", 0, FailSilently(cmd)
}
return pageArg, perPageArg, nil
}

355
oryx/cmdx/printing.go Normal file
View File

@@ -0,0 +1,355 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package cmdx
import (
"encoding/json"
"errors"
"fmt"
"io"
"os"
"strings"
"text/tabwriter"
"github.com/go-openapi/jsonpointer"
"github.com/goccy/go-yaml"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/tidwall/gjson"
)
type (
TableHeader interface {
Header() []string
}
TableRow interface {
TableHeader
Columns() []string
Interface() interface{}
}
Table interface {
TableHeader
Table() [][]string
Interface() interface{}
Len() int
}
Nil struct{}
Format string
)
const (
FormatQuiet Format = "quiet"
FormatTable Format = "table"
FormatJSON Format = "json"
FormatJSONPath Format = "jsonpath"
FormatJSONPointer Format = "jsonpointer"
FormatJSONPretty Format = "json-pretty"
FormatYAML Format = "yaml"
FormatDefault Format = "default"
FlagFormat = "format"
None = "<none>"
)
func (Nil) String() string {
return "null"
}
func (Nil) Interface() interface{} {
return nil
}
type printOptions struct {
format string
}
type PrintOption func(*printOptions)
func WithFormat(v string) PrintOption {
return func(o *printOptions) {
o.format = v
}
}
func PrintErrors(cmd *cobra.Command, errs map[string]error) {
for src, err := range errs {
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "%s: %s\n", src, err.Error())
}
}
func PrintRow(cmd *cobra.Command, row TableRow) {
PrintRowf(cmd.OutOrStdout(), row, WithFormat(getFormatValue(cmd)))
}
func PrintRowf(w io.Writer, row TableRow, opts ...PrintOption) {
o := &printOptions{}
for _, fn := range opts {
fn(o)
}
switch parseFormat(o.format) {
case FormatQuiet:
if idAble, ok := row.(interface{ ID() string }); ok {
_, _ = fmt.Fprintln(w, idAble.ID())
break
}
_, _ = fmt.Fprintln(w, row.Columns()[0])
case FormatJSON:
printJSON(w, row.Interface(), false, "")
case FormatYAML:
printYAML(w, row.Interface())
case FormatJSONPretty:
printJSON(w, row.Interface(), true, "")
case FormatJSONPath:
printJSON(w, row.Interface(), true, getPath(o.format))
case FormatJSONPointer:
printJSON(w, filterJSONPointer(o.format, row.Interface()), true, "")
case FormatTable, FormatDefault:
w := tabwriter.NewWriter(w, 0, 8, 1, '\t', 0)
fields := row.Columns()
for i, h := range row.Header() {
_, _ = fmt.Fprintf(w, "%s\t%s\t\n", h, fields[i])
}
_ = w.Flush()
}
}
func filterJSONPointer(f string, data any) any {
_, jsonptr, found := strings.Cut(f, "=")
if !found {
_, _ = fmt.Fprintf(os.Stderr,
"Format %s is missing a JSON pointer, e.g., --%s=%s=<jsonpointer>. The path syntax is described at https://datatracker.ietf.org/doc/html/draft-ietf-appsawg-json-pointer-07.",
f, FlagFormat, f)
os.Exit(1)
}
ptr, err := jsonpointer.New(jsonptr)
Must(err, "invalid JSON pointer: %s", err)
result, _, err := ptr.Get(data)
Must(err, "failed to apply JSON pointer: %s", err)
return result
}
func PrintTable(cmd *cobra.Command, table Table) {
PrintTablef(cmd.OutOrStdout(), table, WithFormat(getFormatValue(cmd)))
}
func PrintTablef(w io.Writer, table Table, opts ...PrintOption) {
o := &printOptions{}
for _, fn := range opts {
fn(o)
}
switch parseFormat(o.format) {
case FormatQuiet:
if table.Len() == 0 {
fmt.Fprintln(w)
}
if idAble, ok := table.(interface{ IDs() []string }); ok {
for _, row := range idAble.IDs() {
fmt.Fprintln(w, row)
}
break
}
for _, row := range table.Table() {
fmt.Fprintln(w, row[0])
}
case FormatJSON:
printJSON(w, table.Interface(), false, "")
case FormatJSONPretty:
printJSON(w, table.Interface(), true, "")
case FormatJSONPath:
printJSON(w, table.Interface(), true, getPath(o.format))
case FormatJSONPointer:
printJSON(w, filterJSONPointer(o.format, table.Interface()), true, "")
case FormatYAML:
printYAML(w, table.Interface())
default:
w := tabwriter.NewWriter(w, 0, 8, 1, '\t', 0)
for _, h := range table.Header() {
fmt.Fprintf(w, "%s\t", h)
}
fmt.Fprintln(w)
for _, row := range table.Table() {
fmt.Fprintln(w, strings.Join(row, "\t")+"\t")
}
_ = w.Flush()
}
}
type interfacer interface{ Interface() interface{} }
func PrintJSONAble(cmd *cobra.Command, d interface{ String() string }) {
PrintJSONAblef(cmd.OutOrStdout(), d, WithFormat(getFormatValue(cmd)))
}
func PrintJSONAblef(w io.Writer, d interface{ String() string }, opts ...PrintOption) {
if d == nil {
d = Nil{}
}
o := &printOptions{}
for _, fn := range opts {
fn(o)
}
var path string
switch parseFormat(o.format) {
default:
_, _ = fmt.Fprint(w, d.String())
case FormatJSON:
var v interface{} = d
if i, ok := d.(interfacer); ok {
v = i
}
printJSON(w, v, false, "")
case FormatJSONPath:
path = getPath(o.format)
fallthrough
case FormatJSONPretty:
var v interface{} = d
if i, ok := d.(interfacer); ok {
v = i
}
printJSON(w, v, true, path)
case FormatJSONPointer:
var v interface{} = d
if i, ok := d.(interfacer); ok {
v = i
}
printJSON(w, filterJSONPointer(o.format, v), true, "")
case FormatYAML:
var v interface{} = d
if i, ok := d.(interfacer); ok {
v = i
}
printYAML(w, v)
}
}
func getQuiet(cmd *cobra.Command) bool {
// ignore the error here as we use this function also when the flag might not be registered
q, _ := cmd.Flags().GetBool(FlagQuiet)
return q
}
func getFormatValue(cmd *cobra.Command) string {
if getQuiet(cmd) {
return string(FormatQuiet)
}
f, _ := cmd.Flags().GetString(FlagFormat)
return f
}
func parseFormat(f string) Format {
switch {
case f == string(FormatQuiet):
return FormatQuiet
case f == string(FormatTable):
return FormatTable
case f == string(FormatJSON):
return FormatJSON
case strings.HasPrefix(f, string(FormatJSONPath)):
return FormatJSONPath
case strings.HasPrefix(f, string(FormatJSONPointer)):
return FormatJSONPointer
case f == string(FormatJSONPretty):
return FormatJSONPretty
case f == string(FormatYAML):
return FormatYAML
default:
return FormatDefault
}
}
func getPath(f string) string {
_, path, found := strings.Cut(f, "=")
if !found {
_, _ = fmt.Fprintf(os.Stderr,
"Format %s is missing a path, e.g., --%s=%s=<path>. The path syntax is described at https://github.com/tidwall/gjson/blob/master/SYNTAX.md",
f, FlagFormat, f)
os.Exit(1)
}
return path
}
func printJSON(w io.Writer, v interface{}, pretty bool, path string) {
if path != "" {
temp, err := json.Marshal(v)
Must(err, "Error encoding JSON: %s", err)
v = gjson.GetBytes(temp, path).Value()
}
e := json.NewEncoder(w)
if pretty {
e.SetIndent("", " ")
}
err := e.Encode(v)
// unexpected error
Must(err, "Error encoding JSON: %s", err)
}
func printYAML(w io.Writer, v interface{}) {
j, err := json.Marshal(v)
Must(err, "Error encoding JSON: %s", err)
e, err := yaml.JSONToYAML(j)
Must(err, "Error encoding YAML: %s", err)
_, _ = w.Write(e)
}
func RegisterJSONFormatFlags(flags *pflag.FlagSet) {
flags.String(FlagFormat, string(FormatDefault), fmt.Sprintf("Set the output format. One of %s, %s, %s, %s, %s and %s.", FormatDefault, FormatJSON, FormatYAML, FormatJSONPretty, FormatJSONPath, FormatJSONPointer))
}
func RegisterFormatFlags(flags *pflag.FlagSet) {
RegisterNoiseFlags(flags)
flags.String(FlagFormat, string(FormatTable), fmt.Sprintf("Set the output format. One of %s, %s, %s, %s, %s and %s.", FormatTable, FormatJSON, FormatYAML, FormatJSONPretty, FormatJSONPath, FormatJSONPointer))
}
func PrintOpenAPIError(cmd *cobra.Command, err error) error {
if err == nil {
return nil
}
var be interface {
Body() []byte
}
if !errors.As(err, &be) {
return err
}
body := be.Body()
didPrettyPrint := false
if message := gjson.GetBytes(body, "error.message"); message.Exists() {
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "%s\n", message.String())
didPrettyPrint = true
}
if reason := gjson.GetBytes(body, "error.reason"); reason.Exists() {
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "%s\n", reason.String())
didPrettyPrint = true
}
if didPrettyPrint {
return FailSilently(cmd)
}
if body, err := json.MarshalIndent(json.RawMessage(body), "", " "); err == nil {
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "%s\nFailed to execute API request, see error above.\n", body)
return FailSilently(cmd)
}
return err
}

122
oryx/cmdx/usage.go Normal file
View File

@@ -0,0 +1,122 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package cmdx
import (
"bytes"
"maps"
"sync"
"text/template"
"github.com/Masterminds/sprig/v3"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
usageTemplateFuncs = sprig.TxtFuncMap()
usageTemplateFuncsMux sync.RWMutex
enableUsageTemplatingOnce sync.Once
)
// AddUsageTemplateFunc adds a template function to the usage template.
func AddUsageTemplateFunc(name string, f interface{}) {
usageTemplateFuncsMux.Lock()
usageTemplateFuncs[name] = f
usageTemplateFuncsMux.Unlock()
}
const (
helpTemplate = `{{insertTemplate . (or .Long .Short) | trimTrailingWhitespaces}}
{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
usageTemplate = `Usage:{{if .Runnable}}
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
Aliases:
{{.NameAndAliases}}{{end}}{{if .HasExample}}
Examples:
{{insertTemplate . .Example}}{{end}}{{if .HasAvailableSubCommands}}
Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
Flags:
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
Global Flags:
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
`
)
// EnableUsageTemplating enables gotemplates for usage strings, i.e. cmd.Short, cmd.Long, and cmd.Example.
// The data for the template is the command itself. Especially useful are `.Root.Name` and `.CommandPath`.
// This will be inherited by all subcommands, so enabling it on the root command is sufficient.
func EnableUsageTemplating(cmds ...*cobra.Command) {
enableUsageTemplatingOnce.Do(func() {
cobra.AddTemplateFunc("insertTemplate", TemplateCommandField)
})
for _, cmd := range cmds {
cmd.SetHelpTemplate(helpTemplate)
cmd.SetUsageTemplate(usageTemplate)
}
}
func TemplateCommandField(cmd *cobra.Command, field string) (string, error) {
t := template.New("")
usageTemplateFuncsMux.RLock()
t.Funcs(maps.Clone(usageTemplateFuncs))
usageTemplateFuncsMux.RUnlock()
t, err := t.Parse(field)
if err != nil {
return "", err
}
var out bytes.Buffer
if err := t.Execute(&out, cmd); err != nil {
return "", err
}
return out.String(), nil
}
// DisableUsageTemplating resets the commands usage template to the default.
// This can be used to undo the effects of EnableUsageTemplating, specifically for a subcommand.
func DisableUsageTemplating(cmds ...*cobra.Command) {
defaultCmd := new(cobra.Command)
for _, cmd := range cmds {
cmd.SetHelpTemplate(defaultCmd.HelpTemplate())
cmd.SetUsageTemplate(defaultCmd.UsageTemplate())
}
}
// AssertUsageTemplates asserts that the usage string of the commands are properly templated.
func AssertUsageTemplates(t require.TestingT, cmd *cobra.Command) {
var usage, help string
require.NotPanics(t, func() {
usage = cmd.UsageString()
out, err := cmd.OutOrStdout(), cmd.ErrOrStderr()
bb := new(bytes.Buffer)
cmd.SetOut(bb)
cmd.SetErr(bb)
require.NoError(t, cmd.Help())
help = bb.String()
cmd.SetOut(out)
cmd.SetErr(err)
})
assert.NotContains(t, usage, "{{")
assert.NotContains(t, help, "{{")
for _, child := range cmd.Commands() {
AssertUsageTemplates(t, child)
}
}

57
oryx/cmdx/user_input.go Normal file
View File

@@ -0,0 +1,57 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package cmdx
import (
"bufio"
"fmt"
"io"
"os"
"strings"
"github.com/pkg/errors"
)
// asks for confirmation with the question string s and reads the answer
// pass nil to use os.Stdin and os.Stdout
func AskForConfirmation(s string, stdin io.Reader, stdout io.Writer) bool {
if stdin == nil {
stdin = os.Stdin
}
if stdout == nil {
stdout = os.Stdout
}
ok, err := AskScannerForConfirmation(s, bufio.NewReader(stdin), stdout)
if err != nil {
Must(err, "Unable to confirm: %s", err)
}
return ok
}
func AskScannerForConfirmation(s string, reader *bufio.Reader, stdout io.Writer) (bool, error) {
if stdout == nil {
stdout = os.Stdout
}
for {
_, err := fmt.Fprintf(stdout, "%s [y/n]: ", s)
if err != nil {
return false, errors.Wrap(err, "unable to print to stdout")
}
response, err := reader.ReadString('\n')
if err != nil {
return false, errors.Wrap(err, "unable to read from stdin")
}
response = strings.ToLower(strings.TrimSpace(response))
if response == "y" || response == "yes" {
return true, nil
} else if response == "n" || response == "no" {
return false, nil
}
}
}

38
oryx/cmdx/version.go Normal file
View File

@@ -0,0 +1,38 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package cmdx
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
// Version returns a *cobra.Command that handles the `version` command.
func Version(gitTag, gitHash, buildTime *string) *cobra.Command {
return &cobra.Command{
Use: "version",
Short: "Show the build version, build time, and git hash",
Run: func(cmd *cobra.Command, args []string) {
if len(*gitTag) == 0 {
fmt.Fprintln(os.Stderr, "Unable to determine version because the build process did not properly configure it.")
} else {
fmt.Printf("Version: %s\n", *gitTag)
}
if len(*gitHash) == 0 {
fmt.Fprintln(os.Stderr, "Unable to determine build commit because the build process did not properly configure it.")
} else {
fmt.Printf("Build Commit: %s\n", *gitHash)
}
if len(*buildTime) == 0 {
fmt.Fprintln(os.Stderr, "Unable to determine build timestamp because the build process did not properly configure it.")
} else {
fmt.Printf("Build Timestamp: %s\n", *buildTime)
}
},
}
}