mirror of
https://github.com/holos-run/holos.git
synced 2026-03-19 08:44:58 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e95a2812e |
63
cmd/cmd.go
Normal file
63
cmd/cmd.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"runtime/pprof"
|
||||
"runtime/trace"
|
||||
|
||||
"github.com/holos-run/holos/internal/cli"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
)
|
||||
|
||||
// MakeMain makes a main function for the cli or tests.
|
||||
func MakeMain(options ...holos.Option) func() int {
|
||||
return func() (exitCode int) {
|
||||
cfg := holos.New(options...)
|
||||
slog.SetDefault(cfg.Logger())
|
||||
ctx := context.Background()
|
||||
|
||||
if format := os.Getenv("HOLOS_CPU_PROFILE"); format != "" {
|
||||
f, _ := os.Create(fmt.Sprintf(format, os.Getppid(), os.Getpid()))
|
||||
err := pprof.StartCPUProfile(f)
|
||||
defer func() {
|
||||
pprof.StopCPUProfile()
|
||||
f.Close()
|
||||
}()
|
||||
if err != nil {
|
||||
return cli.HandleError(ctx, err, cfg)
|
||||
}
|
||||
}
|
||||
defer memProfile(ctx, cfg)
|
||||
|
||||
if format := os.Getenv("HOLOS_TRACE"); format != "" {
|
||||
f, _ := os.Create(fmt.Sprintf(format, os.Getppid(), os.Getpid()))
|
||||
err := trace.Start(f)
|
||||
defer func() {
|
||||
trace.Stop()
|
||||
f.Close()
|
||||
}()
|
||||
if err != nil {
|
||||
return cli.HandleError(ctx, err, cfg)
|
||||
}
|
||||
}
|
||||
|
||||
feature := &holos.EnvFlagger{}
|
||||
if err := cli.New(cfg, feature).ExecuteContext(ctx); err != nil {
|
||||
return cli.HandleError(ctx, err, cfg)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func memProfile(ctx context.Context, cfg *holos.Config) {
|
||||
if format := os.Getenv("HOLOS_MEM_PROFILE"); format != "" {
|
||||
f, _ := os.Create(fmt.Sprintf(format, os.Getppid(), os.Getpid()))
|
||||
defer f.Close()
|
||||
if err := pprof.WriteHeapProfile(f); err != nil {
|
||||
_ = cli.HandleError(ctx, err, cfg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,9 @@ package main
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/holos-run/holos/internal/cli"
|
||||
"github.com/holos-run/holos/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
os.Exit(cli.MakeMain()())
|
||||
os.Exit(cmd.MakeMain()())
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ import (
|
||||
"testing"
|
||||
|
||||
cue "cuelang.org/go/cmd/cue/cmd"
|
||||
"github.com/holos-run/holos/internal/cli"
|
||||
"github.com/holos-run/holos/cmd"
|
||||
"github.com/rogpeppe/go-internal/testscript"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(testscript.RunMain(m, map[string]func() int{
|
||||
"holos": cli.MakeMain(),
|
||||
"holos": cmd.MakeMain(),
|
||||
"cue": cue.Main,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1,110 +1 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"runtime/pprof"
|
||||
"runtime/trace"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
cue "cuelang.org/go/cue/errors"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||
)
|
||||
|
||||
func memProfile(ctx context.Context, cfg *holos.Config) {
|
||||
if format := os.Getenv("HOLOS_MEM_PROFILE"); format != "" {
|
||||
f, _ := os.Create(fmt.Sprintf(format, os.Getppid(), os.Getpid()))
|
||||
defer f.Close()
|
||||
if err := pprof.WriteHeapProfile(f); err != nil {
|
||||
_ = HandleError(ctx, err, cfg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MakeMain makes a main function for the cli or tests.
|
||||
func MakeMain(options ...holos.Option) func() int {
|
||||
return func() (exitCode int) {
|
||||
cfg := holos.New(options...)
|
||||
slog.SetDefault(cfg.Logger())
|
||||
ctx := context.Background()
|
||||
|
||||
if format := os.Getenv("HOLOS_CPU_PROFILE"); format != "" {
|
||||
f, _ := os.Create(fmt.Sprintf(format, os.Getppid(), os.Getpid()))
|
||||
err := pprof.StartCPUProfile(f)
|
||||
defer func() {
|
||||
pprof.StopCPUProfile()
|
||||
f.Close()
|
||||
}()
|
||||
if err != nil {
|
||||
return HandleError(ctx, err, cfg)
|
||||
}
|
||||
}
|
||||
defer memProfile(ctx, cfg)
|
||||
|
||||
if format := os.Getenv("HOLOS_TRACE"); format != "" {
|
||||
f, _ := os.Create(fmt.Sprintf(format, os.Getppid(), os.Getpid()))
|
||||
err := trace.Start(f)
|
||||
defer func() {
|
||||
trace.Stop()
|
||||
f.Close()
|
||||
}()
|
||||
if err != nil {
|
||||
return HandleError(ctx, err, cfg)
|
||||
}
|
||||
}
|
||||
|
||||
feature := &holos.EnvFlagger{}
|
||||
if err := New(cfg, feature).ExecuteContext(ctx); err != nil {
|
||||
return HandleError(ctx, err, cfg)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// HandleError is the top level error handler that unwraps and logs errors.
|
||||
func HandleError(ctx context.Context, err error, hc *holos.Config) (exitCode int) {
|
||||
// Connect errors have codes, log them.
|
||||
log := hc.NewTopLevelLogger().With("code", connect.CodeOf(err))
|
||||
var cueErr cue.Error
|
||||
var errAt *errors.ErrorAt
|
||||
|
||||
if errors.As(err, &errAt) {
|
||||
loc := errAt.Source.Loc()
|
||||
err2 := errAt.Unwrap()
|
||||
log.ErrorContext(ctx, fmt.Sprintf("could not run: %s at %s", err2, loc), "err", err2, "loc", loc)
|
||||
} else {
|
||||
log.ErrorContext(ctx, fmt.Sprintf("could not run: %s", err), "err", err)
|
||||
}
|
||||
|
||||
// cue errors are bundled up as a list and refer to multiple files / lines.
|
||||
if errors.As(err, &cueErr) {
|
||||
msg := cue.Details(cueErr, nil)
|
||||
if _, err := fmt.Fprint(hc.Stderr(), msg); err != nil {
|
||||
log.ErrorContext(ctx, "could not write CUE error details: "+err.Error(), "err", err)
|
||||
}
|
||||
}
|
||||
// connect errors have details and codes.
|
||||
// Refer to https://connectrpc.com/docs/go/errors
|
||||
if connectErr := new(connect.Error); errors.As(err, &connectErr) {
|
||||
for _, detail := range connectErr.Details() {
|
||||
msg, valueErr := detail.Value()
|
||||
if valueErr != nil {
|
||||
log.WarnContext(ctx, "could not decode error detail", "err", err, "type", detail.Type(), "note", "this usually means we don't have the schema for the protobuf message type")
|
||||
continue
|
||||
}
|
||||
if info, ok := msg.(*errdetails.ErrorInfo); ok {
|
||||
logDetail := log.With("reason", info.GetReason(), "domain", info.GetDomain())
|
||||
for k, v := range info.GetMetadata() {
|
||||
logDetail = logDetail.With(k, v)
|
||||
}
|
||||
logDetail.ErrorContext(ctx, info.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||
|
||||
"github.com/holos-run/holos/version"
|
||||
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/logger"
|
||||
"github.com/holos-run/holos/internal/server"
|
||||
@@ -28,7 +32,8 @@ import (
|
||||
"github.com/holos-run/holos/internal/cli/token"
|
||||
"github.com/holos-run/holos/internal/cli/txtar"
|
||||
|
||||
cue "cuelang.org/go/cmd/cue/cmd"
|
||||
cueCmd "cuelang.org/go/cmd/cue/cmd"
|
||||
cue_errors "cuelang.org/go/cue/errors"
|
||||
)
|
||||
|
||||
//go:embed help.txt
|
||||
@@ -119,7 +124,7 @@ func newOrgCmd(feature holos.Flagger) (cmd *cobra.Command) {
|
||||
|
||||
func newCueCmd() (cmd *cobra.Command) {
|
||||
// Get a handle on the cue root command fields.
|
||||
root, _ := cue.New([]string{})
|
||||
root, _ := cueCmd.New([]string{})
|
||||
// Copy the fields to our embedded command.
|
||||
cmd = command.New("cue")
|
||||
cmd.Short = root.Short
|
||||
@@ -130,8 +135,52 @@ func newCueCmd() (cmd *cobra.Command) {
|
||||
|
||||
// We do it this way so we handle errors correctly.
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
cueRootCommand, _ := cue.New(args)
|
||||
cueRootCommand, _ := cueCmd.New(args)
|
||||
return cueRootCommand.Run(cmd.Root().Context())
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// HandleError is the top level error handler that unwraps and logs errors.
|
||||
func HandleError(ctx context.Context, err error, hc *holos.Config) (exitCode int) {
|
||||
// Connect errors have codes, log them.
|
||||
log := hc.NewTopLevelLogger().With("code", connect.CodeOf(err))
|
||||
var cueErr cue_errors.Error
|
||||
var errAt *errors.ErrorAt
|
||||
|
||||
if errors.As(err, &errAt) {
|
||||
loc := errAt.Source.Loc()
|
||||
err2 := errAt.Unwrap()
|
||||
log.ErrorContext(ctx, fmt.Sprintf("could not run: %s at %s", err2, loc), "err", err2, "loc", loc)
|
||||
} else {
|
||||
log.ErrorContext(ctx, fmt.Sprintf("could not run: %s", err), "err", err)
|
||||
}
|
||||
|
||||
// cue errors are bundled up as a list and refer to multiple files / lines.
|
||||
if errors.As(err, &cueErr) {
|
||||
msg := cue_errors.Details(cueErr, nil)
|
||||
if _, err := fmt.Fprint(hc.Stderr(), msg); err != nil {
|
||||
log.ErrorContext(ctx, "could not write CUE error details: "+err.Error(), "err", err)
|
||||
}
|
||||
}
|
||||
// connect errors have details and codes.
|
||||
// Refer to https://connectrpc.com/docs/go/errors
|
||||
if connectErr := new(connect.Error); errors.As(err, &connectErr) {
|
||||
for _, detail := range connectErr.Details() {
|
||||
msg, valueErr := detail.Value()
|
||||
if valueErr != nil {
|
||||
log.WarnContext(ctx, "could not decode error detail", "err", err, "type", detail.Type(), "note", "this usually means we don't have the schema for the protobuf message type")
|
||||
continue
|
||||
}
|
||||
if info, ok := msg.(*errdetails.ErrorInfo); ok {
|
||||
logDetail := log.With("reason", info.GetReason(), "domain", info.GetDomain())
|
||||
for k, v := range info.GetMetadata() {
|
||||
logDetail = logDetail.With(k, v)
|
||||
}
|
||||
logDetail.ErrorContext(ctx, info.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
7
|
||||
8
|
||||
|
||||
Reference in New Issue
Block a user