Compare commits

...

4 Commits

Author SHA1 Message Date
Jeff McCune
2c79982bd3 cue: enable @embed for loading yaml (#385)
mpvl suggests @embed is a more ideal solution than our implementation of
core.Component.Instances for the use case of unifying YAML data updated
by Kargo Stage resources.

See the issue for a link to the discussion.
2024-12-20 07:14:01 -08:00
Jeff McCune
e5e4de3073 cue: update to 0.11.1
go get cuelang.org/go/cmd/cue@latest

    go: downloading cuelang.org/go v0.11.1
    go: upgraded cuelang.org/go v0.11.0 => v0.11.1
2024-12-20 07:09:39 -08:00
Jeff McCune
ec462f5f0b docs: redirect /docs/support 2024-12-19 22:13:04 -08:00
Jeff McCune
0e95a2812e cmd: expose MakeMain() for testing
I'd like to add the kargo-demo repository to Unity to test evalv3, but
can't get a handle on the main function to wire up to testscript.

This patch fixes the problem by moving the MakeMain function to a public
package so the kargo-demo go module can import and call it using the go
mod tools technique.
2024-12-19 15:19:46 -08:00
11 changed files with 128 additions and 122 deletions

63
cmd/cmd.go Normal file
View 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)
}
}
}

View File

@@ -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()())
}

View File

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

View File

@@ -18,3 +18,5 @@
/docs/kargo/ /docs/v1alpha5/topics/kargo/ 301
/docs/comparison /docs/v1alpha5/topics/comparison/ 301
/docs/comparison/ /docs/v1alpha5/topics/comparison/ 301
/docs/support /docs/v1alpha5/tutorial/overview/#getting-help 301
/docs/support/ /docs/v1alpha5/tutorial/overview/#getting-help 301

2
go.mod
View File

@@ -10,7 +10,7 @@ require (
connectrpc.com/grpcreflect v1.2.0
connectrpc.com/otelconnect v0.7.0
connectrpc.com/validate v0.1.0
cuelang.org/go v0.11.0
cuelang.org/go v0.11.1
entgo.io/ent v0.13.1
github.com/bufbuild/buf v1.35.1
github.com/choria-io/machine-room v0.0.0-20240417064836-c604da2f005e

4
go.sum
View File

@@ -52,8 +52,8 @@ connectrpc.com/validate v0.1.0 h1:r55jirxMK7HO/xZwVHj3w2XkVFarsUM77ZDy367NtH4=
connectrpc.com/validate v0.1.0/go.mod h1:GU47c9/x/gd+u9wRSPkrQOP46gx2rMN+Wo37EHgI3Ow=
cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565 h1:R5wwEcbEZSBmeyg91MJZTxfd7WpBo2jPof3AYjRbxwY=
cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565/go.mod h1:5A4xfTzHTXfeVJBU6RAUf+QrlfTCW+017q/QiW+sMLg=
cuelang.org/go v0.11.0 h1:2af2nhipqlUHtXk2dtOP5xnMm1ObGvKqIsJUJL1sRE4=
cuelang.org/go v0.11.0/go.mod h1:PBY6XvPUswPPJ2inpvUozP9mebDVTXaeehQikhZPBz0=
cuelang.org/go v0.11.1 h1:pV+49MX1mmvDm8Qh3Za3M786cty8VKPWzQ1Ho4gZRP0=
cuelang.org/go v0.11.1/go.mod h1:PBY6XvPUswPPJ2inpvUozP9mebDVTXaeehQikhZPBz0=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=

View File

@@ -11,6 +11,7 @@ import (
"cuelang.org/go/cue"
"cuelang.org/go/cue/cuecontext"
"cuelang.org/go/cue/interpreter/embed"
"cuelang.org/go/cue/load"
"cuelang.org/go/encoding/yaml"
"github.com/holos-run/holos/internal/errors"
@@ -76,7 +77,7 @@ func LoadInstance(path string, filepaths []string, tags []string) (*Instance, er
ModuleRoot: root,
Tags: tags,
}
ctxt := cuecontext.New()
ctxt := cuecontext.New(cuecontext.Interpreter(embed.New()))
bis := load.Instances([]string{path}, cfg)
values, err := ctxt.BuildInstances(bis)

View File

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

View File

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

View File

@@ -1 +1 @@
101
102

View File

@@ -1 +1 @@
7
0