From 973016dfd62ad242c440b38039d546b4ee25663d Mon Sep 17 00:00:00 2001 From: "Ryan J. Price" Date: Tue, 23 Sep 2025 17:37:34 -0500 Subject: [PATCH 1/3] Add colored output, move some stuff around in a package --- go.mod | 2 + go.sum | 4 + internal/cli/root.go | 43 ++++++---- internal/consts/consts.go | 10 ++- internal/git/git.go | 12 +-- internal/print/colors.go | 91 ++++++++++++++++++++++ internal/print/doc.go | 2 +- internal/print/print.go | 54 ++++++++++--- internal/system/doc.go | 2 + internal/{tasks/util => system}/system.go | 23 +++--- internal/tasks/ci/run.go | 18 +---- internal/tasks/delivery/run.go | 8 +- internal/tasks/tools/containers/ci.go | 5 +- internal/tasks/tools/containers/deliver.go | 5 +- internal/tasks/tools/gittag/deliver.go | 3 +- internal/tasks/tools/go/ci.go | 23 +++--- internal/tasks/tools/go/deliver.go | 5 +- internal/tasks/tools/markdown/ci.go | 3 +- internal/tasks/tools/python/ci.go | 11 +-- internal/tasks/tools/shell/ci.go | 5 +- internal/tasks/tools/version/ci.go | 7 +- internal/tasks/tools/yaml/ci.go | 9 ++- internal/tasks/util/repo.go | 15 ++-- internal/tasks/util/run.go | 59 +++++++++++--- oscar.yaml | 2 +- 25 files changed, 303 insertions(+), 118 deletions(-) create mode 100644 internal/print/colors.go create mode 100644 internal/system/doc.go rename internal/{tasks/util => system}/system.go (90%) diff --git a/go.mod b/go.mod index 1cf2c17..e736209 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( buf.build/go/protovalidate v1.0.0 github.com/stretchr/testify v1.11.1 go.yaml.in/yaml/v4 v4.0.0-rc.2 + golang.org/x/term v0.35.0 google.golang.org/protobuf v1.36.9 ) @@ -23,6 +24,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stoewer/go-strcase v1.3.1 // indirect golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect + golang.org/x/sys v0.36.0 // indirect golang.org/x/text v0.26.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect diff --git a/go.sum b/go.sum index db49db2..e92c682 100644 --- a/go.sum +++ b/go.sum @@ -37,6 +37,10 @@ golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/y golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= +golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY= diff --git a/internal/cli/root.go b/internal/cli/root.go index 8b5d89e..9695b7a 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -16,8 +16,10 @@ import ( const ( // Command names and their flags - rootCmdName = "oscar" - debugFlagName = "debug" + rootCmdName = "oscar" + debugFlagName = "debug" + noBannerFlagName = "no-banner" + noColorFlagName = "no-color" ciCommandName = "ci" @@ -40,8 +42,27 @@ func NewRootCmd() *cli.Command { Flags: []cli.Flag{ &cli.BoolFlag{ Name: debugFlagName, - Usage: "Whether to print debug logs during oscar runs", - Sources: cli.EnvVars(consts.DebugEnvVarName), + Usage: "Whether to print debug logs during oscar runs.", + Sources: cli.EnvVars(consts.OscarEnvVarDebug), + Action: func(_ context.Context, _ *cli.Command, _ bool) error { + return os.Setenv(consts.OscarEnvVarDebug, "true") + }, + }, + &cli.BoolFlag{ + Name: noBannerFlagName, + Usage: "Pass to suppress printing oscar's stylistic banner at startup.", + Sources: cli.EnvVars(consts.OscarEnvVarNoBanner), + Action: func(_ context.Context, _ *cli.Command, _ bool) error { + return os.Setenv(consts.OscarEnvVarNoBanner, "true") + }, + }, + &cli.BoolFlag{ + Name: noColorFlagName, + Usage: "Pass to suppress printing colored terminal output. Note that oscar defaults to printing in color during interactive runs.", + Sources: cli.EnvVars(consts.OscarEnvVarNoColor), + Action: func(_ context.Context, _ *cli.Command, _ bool) error { + return os.Setenv(consts.OscarEnvVarNoColor, "true") + }, }, }, Commands: []*cli.Command{ @@ -61,13 +82,6 @@ func NewRootCmd() *cli.Command { return cmd } -// maybeSetDebug conditionally sets oscar's debug env var, so that other packages can use it. -func maybeSetDebug(cmd *cli.Command) { - if cmd.Bool(debugFlagName) || os.Getenv(consts.DebugEnvVarName) != "" { - _ = os.Setenv(consts.DebugEnvVarName, "true") - } -} - // getVersion retrieves the version of the codebase. func getVersion() (string, error) { cfg, err := oscarcfg.Get() @@ -80,15 +94,13 @@ func getVersion() (string, error) { // rootAction defines the logic for oscar's root command. func rootAction(_ context.Context, cmd *cli.Command) error { - maybeSetDebug(cmd) iprint.Debugf("oscar root command\n") _ = cli.ShowAppHelp(cmd) return errors.New("\nERROR: oscar requires a valid subcommand") } // ciAction defines the logic for oscar's ci subcommand. -func ciAction(ctx context.Context, cmd *cli.Command) error { - maybeSetDebug(cmd) +func ciAction(ctx context.Context, _ *cli.Command) error { iprint.Banner() iprint.Debugf("oscar ci subcommand\n") @@ -100,8 +112,7 @@ func ciAction(ctx context.Context, cmd *cli.Command) error { } // deliverAction defines the logic for oscar's deliver subcommand. -func deliverAction(ctx context.Context, cmd *cli.Command) error { - maybeSetDebug(cmd) +func deliverAction(ctx context.Context, _ *cli.Command) error { iprint.Banner() iprint.Debugf("oscar deliver subcommand\n") diff --git a/internal/consts/consts.go b/internal/consts/consts.go index 7934546..059289e 100644 --- a/internal/consts/consts.go +++ b/internal/consts/consts.go @@ -8,8 +8,14 @@ import ( ) const ( - // DebugEnvVarName is for enabling debug logs etc. - DebugEnvVarName = "OSC_DEBUG" + // OscarEnvVarDebug is for enabling debug logs etc. + OscarEnvVarDebug = "OSC_DEBUG" + + // OscarEnvVarNoBanner is used to suppress printing the stylistic banner at startup. + OscarEnvVarNoBanner = "OSCAR_NO_BANNER" + + // OscarEnvVarNoColor is used to suppress printing colored terminal output. + OscarEnvVarNoColor = "OSCAR_NO_COLOR" // MiseVersion is the default version of mise to install if not present. Can be overridden via // the `MISE_VERSION` env var, which is checked elsewhere. diff --git a/internal/git/git.go b/internal/git/git.go index a479265..dc854da 100644 --- a/internal/git/git.go +++ b/internal/git/git.go @@ -8,7 +8,7 @@ import ( "strings" iprint "github.com/opensourcecorp/oscar/internal/print" - taskutil "github.com/opensourcecorp/oscar/internal/tasks/util" + "github.com/opensourcecorp/oscar/internal/system" ) // Git holds metadata about the current state of the Git repository. @@ -35,25 +35,25 @@ type Status struct { // New returns a populated [Git]. func New(ctx context.Context) (*Git, error) { - root, err := taskutil.RunCommand(ctx, []string{"git", "rev-parse", "--show-toplevel"}) + root, err := system.RunCommand(ctx, []string{"git", "rev-parse", "--show-toplevel"}) if err != nil { return nil, err } iprint.Debugf("Git root on host: '%s'\n", root) - branch, err := taskutil.RunCommand(ctx, []string{"git", "rev-parse", "--abbrev-ref", "HEAD"}) + branch, err := system.RunCommand(ctx, []string{"git", "rev-parse", "--abbrev-ref", "HEAD"}) if err != nil { return nil, err } iprint.Debugf("Git branch: '%s'\n", branch) - latestTag, err := taskutil.RunCommand(ctx, []string{"bash", "-c", "git tag --list | tail -n1"}) + latestTag, err := system.RunCommand(ctx, []string{"bash", "-c", "git tag --list | tail -n1"}) if err != nil { return nil, err } iprint.Debugf("latest Git tag: '%s'\n", latestTag) - latestCommit, err := taskutil.RunCommand(ctx, []string{"git", "rev-parse", "--short=8", "HEAD"}) + latestCommit, err := system.RunCommand(ctx, []string{"git", "rev-parse", "--short=8", "HEAD"}) if err != nil { return nil, err } @@ -107,7 +107,7 @@ func (g *Git) String() string { // getRawStatus returns a slightly-modified "git status" output, so that calling tools can parse it // more easily. func getRawStatus(ctx context.Context) (Status, error) { - outputBytes, err := taskutil.RunCommand(ctx, []string{"git", "status", "--porcelain"}) + outputBytes, err := system.RunCommand(ctx, []string{"git", "status", "--porcelain"}) if err != nil { return Status{}, fmt.Errorf("getting git status output: %w", err) } diff --git a/internal/print/colors.go b/internal/print/colors.go new file mode 100644 index 0000000..97baad4 --- /dev/null +++ b/internal/print/colors.go @@ -0,0 +1,91 @@ +package iprint + +// NOTE: there's a lot of extra-feeling code in here, but that's because of how these vars/functions +// end up being used. Since oscar checks for the '--no-color' flag at execution startup (and then +// sets the env var for it), but that env var is checked *at overall program start time* (i.e. +// before any flags are parsed/handled), we can't just set top-level global vars per color that +// check the env var -- we have to only return color codes on-demand, so the var is available to be +// checked at any point after the env var is assigned. +// +// All this is fine, it just might be goofy-looking. + +import ( + "os" + + "github.com/opensourcecorp/oscar/internal/consts" + "golang.org/x/term" +) + +var ( + // ANSI color codes + reset = "\033[0m" + red = "\033[31m" + green = "\033[32m" + yellow = "\033[33m" + blue = "\033[34m" + magenta = "\033[35m" + cyan = "\033[36m" + gray = "\033[37m" + white = "\033[97m" +) + +// AllColors holds All the possible ANSI color codes that can be used. See the topmost comment in +// this file for "why". +type AllColors struct { + Reset string + Red string + Green string + Yellow string + Blue string + Magenta string + Cyan string + Gray string + White string + DebugColor string + InfoColor string + WarnColor string + ErrorColor string + GoodColor string +} + +// Colors returns a populated [AllColors]. It can be used to grab a conditional ANSI color code in +// message-printing. +// +// For example, to override a print function's default text color with, say, red, you could do +// something like this: +// +// fmt.Println(iprint.Colors().Red + "oops" + iprint.Colors().Reset) +func Colors() AllColors { + out := AllColors{ + Reset: color(reset), + Red: color(red), + Green: color(green), + Yellow: color(yellow), + Blue: color(blue), + Magenta: color(magenta), + Cyan: color(cyan), + Gray: color(gray), + White: color(white), + + DebugColor: color(blue), + InfoColor: color(cyan), + WarnColor: color(yellow), + ErrorColor: color(red), + GoodColor: color(green), + } + + return out +} + +// color is passed an ANSI color code, and conditionally returns either it or an empty string in the +// case of colors being disabled. +func color(ansiCode string) string { + if !term.IsTerminal(int(os.Stdout.Fd())) { + return "" + } + if noColor := os.Getenv(consts.OscarEnvVarNoColor); noColor != "" { + return "" + } + + return ansiCode +} diff --git a/internal/print/doc.go b/internal/print/doc.go index 909749b..fd42798 100644 --- a/internal/print/doc.go +++ b/internal/print/doc.go @@ -1,2 +1,2 @@ -// Package iprint contains helper functions for printing information for the caller. +// Package iprint contains helper functions for printing information. package iprint diff --git a/internal/print/print.go b/internal/print/print.go index 63a3e0d..686e0e3 100644 --- a/internal/print/print.go +++ b/internal/print/print.go @@ -3,11 +3,12 @@ package iprint import ( "fmt" "os" + "time" "github.com/opensourcecorp/oscar/internal/consts" ) -// Banner prints the oscar banner. +// Banner prints the oscar stylistic banner. func Banner() { var banner = ` ____________________ \ @@ -17,29 +18,64 @@ func Banner() { ~=~=~=~=| |_| _| |_ | | | \ |/|---------/ |____________________|/ / ` - fmt.Println(banner) + + if os.Getenv(consts.OscarEnvVarNoBanner) == "" { + Goodf(banner + "\n") + } } // Debugf is a helper function that prints debug logs if requested. func Debugf(format string, args ...any) { - if os.Getenv(consts.DebugEnvVarName) != "" { - format = "DEBUG: " + format - fmt.Printf(format, args...) + colors := Colors() + if os.Getenv(consts.OscarEnvVarDebug) != "" { + fmt.Printf(colors.DebugColor+"DEBUG: "+format+colors.Reset, args...) } } +// Infof is a helper function that writes info-level text. +func Infof(format string, args ...any) { + colors := Colors() + fmt.Printf(colors.InfoColor+format+colors.Reset, args...) +} + // Warnf is a helper function that writes warnings to standard error. func Warnf(format string, args ...any) { - if _, err := fmt.Fprintf(os.Stderr, "WARN: "+format, args...); err != nil { + colors := Colors() + if _, err := fmt.Fprintf( + os.Stderr, + colors.WarnColor+"WARN: "+format+colors.Reset, + args..., + ); err != nil { // NOTE: panicking is fine here, this would be catastrophic lol - panic(fmt.Sprintf("trying to write warning to stderr: %v", err)) + panic( + fmt.Sprintf(colors.ErrorColor+"trying to write warning to stderr: %v"+colors.Reset, err), + ) } } // Errorf is a helper function that writes errors to standard error. func Errorf(format string, args ...any) { - if _, err := fmt.Fprintf(os.Stderr, format, args...); err != nil { + colors := Colors() + if _, err := fmt.Fprintf( + os.Stderr, + colors.ErrorColor+format+colors.Reset, + args..., + ); err != nil { // NOTE: panicking is fine here, this would be catastrophic lol - panic(fmt.Sprintf("trying to write error to stderr: %v", err)) + panic( + fmt.Sprintf(colors.ErrorColor+"trying to write error to stderr: %v"+colors.Reset, err), + ) } } + +// Goodf is a helper function that prints green info text indicating something went well +func Goodf(format string, args ...any) { + colors := Colors() + fmt.Printf(colors.GoodColor+format+colors.Reset, args...) +} + +// RunDurationString returns a calculated duration used to indicate how long a particular Task (or +// set of Tasks) took to run. +func RunDurationString(t time.Time) string { + return fmt.Sprintf("t: %s", time.Since(t).Round(time.Second/1000).String()) +} diff --git a/internal/system/doc.go b/internal/system/doc.go new file mode 100644 index 0000000..fb00e97 --- /dev/null +++ b/internal/system/doc.go @@ -0,0 +1,2 @@ +// Package system contains functionality for host-related activities. +package system diff --git a/internal/tasks/util/system.go b/internal/system/system.go similarity index 90% rename from internal/tasks/util/system.go rename to internal/system/system.go index 0f729c9..87eebe5 100644 --- a/internal/tasks/util/system.go +++ b/internal/system/system.go @@ -1,4 +1,4 @@ -package taskutil +package system import ( "context" @@ -18,9 +18,9 @@ import ( iprint "github.com/opensourcecorp/oscar/internal/print" ) -// InitSystem runs setup & checks against the host itself, so that oscar can run. -func InitSystem(ctx context.Context) error { - fmt.Printf("Initializing the host, this might take some time... ") +// Init runs setup & checks against the host itself, so that oscar can run. +func Init(ctx context.Context) error { + iprint.Infof("Initializing the host, this might take some time... ") startTime := time.Now() requiredSystemCommands := [][]string{ @@ -77,7 +77,10 @@ func InitSystem(ctx context.Context) error { return fmt.Errorf("running mise install: %w", err) } - fmt.Printf("Done! (%s)\n\n", RunDurationString(startTime)) + iprint.Infof( + iprint.Colors().Green+"Done! (%s)\n\n"+iprint.Colors().Reset, + iprint.RunDurationString(startTime), + ) return nil } @@ -110,12 +113,6 @@ func RunCommand(ctx context.Context, cmdArgs []string) (string, error) { return strings.TrimSuffix(string(output), "\n"), nil } -// RunDurationString returns a calculated duration used to indicate how long a particular Task (or -// set of Tasks) took to run. -func RunDurationString(t time.Time) string { - return fmt.Sprintf("t: %s", time.Since(t).Round(time.Second/1000).String()) -} - // GetFileTypeListerCommand takes a [ripgrep]-known file type, and returns a string (to be used as // an arg after `bash -c`) representing a command & its args to find matching files. ripgrep is used // as the default file-finder across the codebase because not only is it fast, but it also supports @@ -214,9 +211,9 @@ func installMise(_ context.Context) (err error) { return err } -// filesExistInTree performs file discovery by allowing various tools to check if they need to run +// FilesExistInTree performs file discovery by allowing various tools to check if they need to run // based on file presence. -func filesExistInTree(ctx context.Context, findScript string) (bool, error) { +func FilesExistInTree(ctx context.Context, findScript string) (bool, error) { cmd := exec.CommandContext(ctx, "bash", "-c", fmt.Sprintf(` shopt -s globstar %s`, diff --git a/internal/tasks/ci/run.go b/internal/tasks/ci/run.go index fa66e30..e5febfb 100644 --- a/internal/tasks/ci/run.go +++ b/internal/tasks/ci/run.go @@ -58,19 +58,7 @@ func Run(ctx context.Context) (err error) { return fmt.Errorf("internal error setting up run info: %w", err) } - git, err := igit.New(ctx) - if err != nil { - return err - } - fmt.Print(git.String()) - - repo, err := taskutil.NewRepo(ctx) - if err != nil { - return fmt.Errorf("getting repo composition: %w", err) - } - fmt.Print(repo.String()) - - taskMap, err := getCITaskMap(repo) + taskMap, err := getCITaskMap(run.Repo) if err != nil { return err } @@ -104,7 +92,7 @@ func Run(ctx context.Context) (err error) { } if runErr != nil || gitStatusHasChanged { - iprint.Errorf("FAILED (%s)\n", taskutil.RunDurationString(taskStartTime)) + iprint.Errorf("FAILED (%s)\n", iprint.RunDurationString(taskStartTime)) iprint.Errorf("\n") if runErr != nil { @@ -125,7 +113,7 @@ func Run(ctx context.Context) (err error) { return fmt.Errorf("internal error: %w", err) } } else { - fmt.Printf("PASSED (%s)\n", taskutil.RunDurationString(taskStartTime)) + iprint.Goodf("PASSED (%s)\n", iprint.RunDurationString(taskStartTime)) } } } diff --git a/internal/tasks/delivery/run.go b/internal/tasks/delivery/run.go index d4fe11a..d4c5eaf 100644 --- a/internal/tasks/delivery/run.go +++ b/internal/tasks/delivery/run.go @@ -68,13 +68,13 @@ func Run(ctx context.Context) (err error) { if err != nil { return err } - fmt.Print(git.String()) + iprint.Infof(run.Colors.Gray + git.String() + run.Colors.Reset) repo, err := taskutil.NewRepo(ctx) if err != nil { return fmt.Errorf("getting repo composition: %w", err) } - fmt.Print(repo.String()) + iprint.Infof(run.Colors.Gray + repo.String() + run.Colors.Reset) taskMap, err := getDeliveryTaskMap(repo) if err != nil { @@ -97,12 +97,12 @@ func Run(ctx context.Context) (err error) { runErr = errors.Join(runErr, task.Post(ctx)) if runErr != nil { - iprint.Errorf("FAILED (%s)\n", taskutil.RunDurationString(taskStartTime)) + iprint.Errorf("FAILED (%s)\n", iprint.RunDurationString(taskStartTime)) iprint.Errorf("%v\n", runErr) run.Failures = append(run.Failures, fmt.Sprintf("%s :: %s", lang, task.InfoText())) } else { - fmt.Printf("SUCCEEDED (%s)\n", taskutil.RunDurationString(taskStartTime)) + iprint.Goodf("SUCCEEDED (%s)\n", iprint.RunDurationString(taskStartTime)) } } } diff --git a/internal/tasks/tools/containers/ci.go b/internal/tasks/tools/containers/ci.go index a0bd769..9d77232 100644 --- a/internal/tasks/tools/containers/ci.go +++ b/internal/tasks/tools/containers/ci.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" + "github.com/opensourcecorp/oscar/internal/system" "github.com/opensourcecorp/oscar/internal/tasks/tools/toolcfg" taskutil "github.com/opensourcecorp/oscar/internal/tasks/util" ) @@ -23,7 +24,7 @@ func NewTasksForCI(repo taskutil.Repo) []taskutil.Tasker { RunArgs: []string{"bash", "-c", fmt.Sprintf( `hadolint --config {{ConfigFilePath}} $(%s)`, - taskutil.GetFileTypeListerCommand("containerfile"), + system.GetFileTypeListerCommand("containerfile"), ), }, ConfigFilePath: filepath.Join(os.TempDir(), "hadolint.yaml"), @@ -44,7 +45,7 @@ func (t hadolint) Exec(ctx context.Context) error { return err } - if _, err := taskutil.RunCommand(ctx, t.RenderRunCommandArgs()); err != nil { + if _, err := system.RunCommand(ctx, t.RenderRunCommandArgs()); err != nil { return err } diff --git a/internal/tasks/tools/containers/deliver.go b/internal/tasks/tools/containers/deliver.go index fa4920f..64d4470 100644 --- a/internal/tasks/tools/containers/deliver.go +++ b/internal/tasks/tools/containers/deliver.go @@ -11,6 +11,7 @@ import ( igit "github.com/opensourcecorp/oscar/internal/git" "github.com/opensourcecorp/oscar/internal/oscarcfg" iprint "github.com/opensourcecorp/oscar/internal/print" + "github.com/opensourcecorp/oscar/internal/system" taskutil "github.com/opensourcecorp/oscar/internal/tasks/util" "go.yaml.in/yaml/v4" ) @@ -121,7 +122,7 @@ func (t imageBuildPush) Exec(ctx context.Context) error { authArgs = registryMap.GitHub.AuthCommand } - if _, err := taskutil.RunCommand(ctx, authArgs); err != nil { + if _, err := system.RunCommand(ctx, authArgs); err != nil { return err } @@ -129,7 +130,7 @@ func (t imageBuildPush) Exec(ctx context.Context) error { docker compose --file %s build --push %s `, outPath, cfg.GetName(), )} - if _, err := taskutil.RunCommand(ctx, buildPushArgs); err != nil { + if _, err := system.RunCommand(ctx, buildPushArgs); err != nil { return err } diff --git a/internal/tasks/tools/gittag/deliver.go b/internal/tasks/tools/gittag/deliver.go index 54cfb99..cb4f453 100644 --- a/internal/tasks/tools/gittag/deliver.go +++ b/internal/tasks/tools/gittag/deliver.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/opensourcecorp/oscar/internal/oscarcfg" + "github.com/opensourcecorp/oscar/internal/system" taskutil "github.com/opensourcecorp/oscar/internal/tasks/util" ) @@ -37,7 +38,7 @@ func (t createAndPushTag) Exec(ctx context.Context) error { `, cfg.GetVersion(), )} - if _, err := taskutil.RunCommand(ctx, args); err != nil { + if _, err := system.RunCommand(ctx, args); err != nil { return err } diff --git a/internal/tasks/tools/go/ci.go b/internal/tasks/tools/go/ci.go index 7968673..2312889 100644 --- a/internal/tasks/tools/go/ci.go +++ b/internal/tasks/tools/go/ci.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" + "github.com/opensourcecorp/oscar/internal/system" "github.com/opensourcecorp/oscar/internal/tasks/tools/toolcfg" taskutil "github.com/opensourcecorp/oscar/internal/tasks/util" ) @@ -100,7 +101,7 @@ func (t goModCheck) InfoText() string { return "go.mod tidy check" } // Exec implements [taskutil.Tasker.Exec]. func (t goModCheck) Exec(ctx context.Context) error { - if _, err := taskutil.RunCommand(ctx, t.RunArgs); err != nil { + if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err } @@ -115,7 +116,7 @@ func (t goFormat) InfoText() string { return "Format" } // Exec implements [taskutil.Tasker.Exec]. func (t goFormat) Exec(ctx context.Context) error { - if _, err := taskutil.RunCommand(ctx, t.RunArgs); err != nil { + if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err } @@ -130,7 +131,7 @@ func (t goImports) InfoText() string { return "Format imports" } // Exec implements [taskutil.Tasker.Exec]. func (t goImports) Exec(ctx context.Context) error { - if _, err := taskutil.RunCommand(ctx, t.RunArgs); err != nil { + if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err } @@ -145,7 +146,7 @@ func (t generateCodeCI) InfoText() string { return "Generate code" } // Exec implements [taskutil.Tasker.Exec]. func (t generateCodeCI) Exec(ctx context.Context) error { - if _, err := taskutil.RunCommand(ctx, t.RunArgs); err != nil { + if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err } @@ -176,7 +177,7 @@ func (t goBuildCI) InfoText() string { return "Build" } // Exec implements [taskutil.Tasker.Exec]. func (t goBuildCI) Exec(ctx context.Context) error { - if _, err := taskutil.RunCommand(ctx, t.RunArgs); err != nil { + if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err } @@ -191,7 +192,7 @@ func (t goVet) InfoText() string { return "Vet" } // Exec implements [taskutil.Tasker.Exec]. func (t goVet) Exec(ctx context.Context) error { - if _, err := taskutil.RunCommand(ctx, t.RunArgs); err != nil { + if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err } @@ -210,7 +211,7 @@ func (t staticcheck) Exec(ctx context.Context) error { return err } - if _, err := taskutil.RunCommand(ctx, t.RunArgs); err != nil { + if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err } @@ -235,7 +236,7 @@ func (t revive) Exec(ctx context.Context) error { return err } - if _, err := taskutil.RunCommand(ctx, t.RenderRunCommandArgs()); err != nil { + if _, err := system.RunCommand(ctx, t.RenderRunCommandArgs()); err != nil { return err } @@ -256,7 +257,7 @@ func (t errcheck) InfoText() string { return "Lint (errcheck)" } // Exec implements [taskutil.Tasker.Exec]. func (t errcheck) Exec(ctx context.Context) error { - if _, err := taskutil.RunCommand(ctx, t.RunArgs); err != nil { + if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err } @@ -271,7 +272,7 @@ func (t govulncheck) InfoText() string { return "Vulnerability scan (govulncheck // Exec implements [taskutil.Tasker.Exec]. func (t govulncheck) Exec(ctx context.Context) error { - if _, err := taskutil.RunCommand(ctx, t.RunArgs); err != nil { + if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err } @@ -286,7 +287,7 @@ func (t goTest) InfoText() string { return "Tests" } // Exec implements [taskutil.Tasker.Exec]. func (t goTest) Exec(ctx context.Context) error { - if _, err := taskutil.RunCommand(ctx, t.RunArgs); err != nil { + if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err } diff --git a/internal/tasks/tools/go/deliver.go b/internal/tasks/tools/go/deliver.go index 6aeb5e7..81fd10c 100644 --- a/internal/tasks/tools/go/deliver.go +++ b/internal/tasks/tools/go/deliver.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/opensourcecorp/oscar/internal/oscarcfg" + "github.com/opensourcecorp/oscar/internal/system" taskutil "github.com/opensourcecorp/oscar/internal/tasks/util" ) @@ -78,7 +79,7 @@ func (t ghRelease) Exec(ctx context.Context) error { `, cfg.GetVersion(), draftFlag, )} - if _, err := taskutil.RunCommand(ctx, args); err != nil { + if _, err := system.RunCommand(ctx, args); err != nil { return err } @@ -120,7 +121,7 @@ func goBuild(ctx context.Context, src string) error { binName := filepath.Base(src) target := filepath.Join(targetDir, fmt.Sprintf("%s-%s-%s", binName, goos, goarch)) - if _, err := taskutil.RunCommand(ctx, []string{"bash", "-c", fmt.Sprintf(` + if _, err := system.RunCommand(ctx, []string{"bash", "-c", fmt.Sprintf(` CGO_ENABLED=0 \ GOOS=%s GOARCH=%s \ go build -ldflags '-extldflags "-static"' -o %s %s`, diff --git a/internal/tasks/tools/markdown/ci.go b/internal/tasks/tools/markdown/ci.go index abc3938..e371345 100644 --- a/internal/tasks/tools/markdown/ci.go +++ b/internal/tasks/tools/markdown/ci.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" + "github.com/opensourcecorp/oscar/internal/system" "github.com/opensourcecorp/oscar/internal/tasks/tools/toolcfg" taskutil "github.com/opensourcecorp/oscar/internal/tasks/util" ) @@ -38,7 +39,7 @@ func (t markdownlint) Exec(ctx context.Context) error { return err } - if _, err := taskutil.RunCommand(ctx, t.RenderRunCommandArgs()); err != nil { + if _, err := system.RunCommand(ctx, t.RenderRunCommandArgs()); err != nil { return err } diff --git a/internal/tasks/tools/python/ci.go b/internal/tasks/tools/python/ci.go index a53e15f..f13175a 100644 --- a/internal/tasks/tools/python/ci.go +++ b/internal/tasks/tools/python/ci.go @@ -3,6 +3,7 @@ package pytools import ( "context" + "github.com/opensourcecorp/oscar/internal/system" taskutil "github.com/opensourcecorp/oscar/internal/tasks/util" ) @@ -54,7 +55,7 @@ func (t buildTask) InfoText() string { return "Build" } // Run implements [taskutil.Tasker.Run]. func (t buildTask) Exec(ctx context.Context) error { - if _, err := taskutil.RunCommand(ctx, t.RunArgs); err != nil { + if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err } @@ -69,7 +70,7 @@ func (t ruffLint) InfoText() string { return "Lint (ruff)" } // Run implements [taskutil.Tasker.Run]. func (t ruffLint) Exec(ctx context.Context) error { - if _, err := taskutil.RunCommand(ctx, t.RunArgs); err != nil { + if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err } @@ -84,7 +85,7 @@ func (t ruffFormat) InfoText() string { return "Format (ruff)" } // Run implements [taskutil.Tasker.Run]. func (t ruffFormat) Exec(ctx context.Context) error { - if _, err := taskutil.RunCommand(ctx, t.RunArgs); err != nil { + if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err } @@ -99,7 +100,7 @@ func (t pydoclint) InfoText() string { return "Lint (pydoclint)" } // Run implements [taskutil.Tasker.Run]. func (t pydoclint) Exec(ctx context.Context) error { - if _, err := taskutil.RunCommand(ctx, t.RunArgs); err != nil { + if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err } @@ -114,7 +115,7 @@ func (t mypy) InfoText() string { return "Type-check (mypy)" } // Run implements [taskutil.Tasker.Run]. func (t mypy) Exec(ctx context.Context) error { - if _, err := taskutil.RunCommand(ctx, t.RunArgs); err != nil { + if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err } diff --git a/internal/tasks/tools/shell/ci.go b/internal/tasks/tools/shell/ci.go index fa2d5d7..622ca06 100644 --- a/internal/tasks/tools/shell/ci.go +++ b/internal/tasks/tools/shell/ci.go @@ -3,6 +3,7 @@ package shtools import ( "context" + "github.com/opensourcecorp/oscar/internal/system" taskutil "github.com/opensourcecorp/oscar/internal/tasks/util" ) @@ -44,7 +45,7 @@ func (t shellcheck) InfoText() string { return "Lint (shellcheck)" } // Run implements [taskutil.Tasker.Run]. func (t shellcheck) Exec(ctx context.Context) error { - if _, err := taskutil.RunCommand(ctx, t.RunArgs); err != nil { + if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err } @@ -59,7 +60,7 @@ func (t shfmt) InfoText() string { return "Format (shfmt)" } // Run implements [taskutil.Tasker.Run]. func (t shfmt) Exec(ctx context.Context) error { - if _, err := taskutil.RunCommand(ctx, t.RunArgs); err != nil { + if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err } diff --git a/internal/tasks/tools/version/ci.go b/internal/tasks/tools/version/ci.go index 22765f5..223ec39 100644 --- a/internal/tasks/tools/version/ci.go +++ b/internal/tasks/tools/version/ci.go @@ -11,6 +11,7 @@ import ( "github.com/opensourcecorp/oscar/internal/consts" "github.com/opensourcecorp/oscar/internal/oscarcfg" iprint "github.com/opensourcecorp/oscar/internal/print" + "github.com/opensourcecorp/oscar/internal/system" taskutil "github.com/opensourcecorp/oscar/internal/tasks/util" ) @@ -49,14 +50,14 @@ func (t versionCI) Exec(ctx context.Context) (err error) { } }() - remote, err := taskutil.RunCommand(ctx, []string{"git", "remote", "get-url", "origin"}) + remote, err := system.RunCommand(ctx, []string{"git", "remote", "get-url", "origin"}) if err != nil { return fmt.Errorf("determining git root: %w", err) } remote = canonicalizeGitRemote(remote) - if _, err := taskutil.RunCommand(ctx, []string{"git", "clone", "--depth", "1", remote, tmpCloneDir}); err != nil { + if _, err := system.RunCommand(ctx, []string{"git", "clone", "--depth", "1", remote, tmpCloneDir}); err != nil { return fmt.Errorf("cloning repo source to temp location: %w", err) } @@ -72,7 +73,7 @@ func (t versionCI) Exec(ctx context.Context) (err error) { // // TODO: update internal git package to have a type with ALL this info so I stop copy-pasting // shell-outs around - branch, err := taskutil.RunCommand(ctx, []string{"git", "rev-parse", "--abbrev-ref", "HEAD"}) + branch, err := system.RunCommand(ctx, []string{"git", "rev-parse", "--abbrev-ref", "HEAD"}) if err != nil { return fmt.Errorf("checking current Git branch/ref: %w", err) } diff --git a/internal/tasks/tools/yaml/ci.go b/internal/tasks/tools/yaml/ci.go index 6f8b3ea..ece7027 100644 --- a/internal/tasks/tools/yaml/ci.go +++ b/internal/tasks/tools/yaml/ci.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" + "github.com/opensourcecorp/oscar/internal/system" "github.com/opensourcecorp/oscar/internal/tasks/tools/toolcfg" taskutil "github.com/opensourcecorp/oscar/internal/tasks/util" ) @@ -24,7 +25,7 @@ func NewTasksForCI(repo taskutil.Repo) []taskutil.Tasker { RunArgs: []string{"bash", "-c", fmt.Sprintf( `yamlfmt -conf {{ConfigFilePath}} $(%s)`, - taskutil.GetFileTypeListerCommand("yaml"), + system.GetFileTypeListerCommand("yaml"), ), }, ConfigFilePath: filepath.Join(os.TempDir(), ".yamlfmt"), @@ -35,7 +36,7 @@ func NewTasksForCI(repo taskutil.Repo) []taskutil.Tasker { RunArgs: []string{"bash", "-c", fmt.Sprintf( `yamllint --strict --config-file {{ConfigFilePath}} $(%s)`, - taskutil.GetFileTypeListerCommand("yaml"), + system.GetFileTypeListerCommand("yaml"), ), }, ConfigFilePath: filepath.Join(os.TempDir(), ".yamllint"), @@ -56,7 +57,7 @@ func (t yamllint) Exec(ctx context.Context) error { return err } - if _, err := taskutil.RunCommand(ctx, t.RenderRunCommandArgs()); err != nil { + if _, err := system.RunCommand(ctx, t.RenderRunCommandArgs()); err != nil { return err } @@ -75,7 +76,7 @@ func (t yamlfmt) Exec(ctx context.Context) error { return err } - if _, err := taskutil.RunCommand(ctx, t.RenderRunCommandArgs()); err != nil { + if _, err := system.RunCommand(ctx, t.RenderRunCommandArgs()); err != nil { return err } diff --git a/internal/tasks/util/repo.go b/internal/tasks/util/repo.go index fa0fcd9..d7992ae 100644 --- a/internal/tasks/util/repo.go +++ b/internal/tasks/util/repo.go @@ -5,6 +5,7 @@ import ( "errors" iprint "github.com/opensourcecorp/oscar/internal/print" + "github.com/opensourcecorp/oscar/internal/system" ) // A Repo stores information about the contents of the repository being run against. @@ -56,37 +57,37 @@ func (repo Repo) String() string { func NewRepo(ctx context.Context) (Repo, error) { var errs error - hasGo, err := filesExistInTree(ctx, GetFileTypeListerCommand("go")) + hasGo, err := system.FilesExistInTree(ctx, system.GetFileTypeListerCommand("go")) if err != nil { errs = errors.Join(errs, err) } - hasPython, err := filesExistInTree(ctx, GetFileTypeListerCommand("py")) + hasPython, err := system.FilesExistInTree(ctx, system.GetFileTypeListerCommand("py")) if err != nil { errs = errors.Join(errs, err) } - hasShell, err := filesExistInTree(ctx, GetFileTypeListerCommand("sh")) + hasShell, err := system.FilesExistInTree(ctx, system.GetFileTypeListerCommand("sh")) if err != nil { errs = errors.Join(errs, err) } - hasTerraform, err := filesExistInTree(ctx, GetFileTypeListerCommand("tf")) + hasTerraform, err := system.FilesExistInTree(ctx, system.GetFileTypeListerCommand("tf")) if err != nil { errs = errors.Join(errs, err) } - hasContainerfile, err := filesExistInTree(ctx, GetFileTypeListerCommand("containerfile")) + hasContainerfile, err := system.FilesExistInTree(ctx, system.GetFileTypeListerCommand("containerfile")) if err != nil { errs = errors.Join(errs, err) } - hasYaml, err := filesExistInTree(ctx, GetFileTypeListerCommand("yaml")) + hasYaml, err := system.FilesExistInTree(ctx, system.GetFileTypeListerCommand("yaml")) if err != nil { errs = errors.Join(errs, err) } - hasMarkdown, err := filesExistInTree(ctx, GetFileTypeListerCommand("md")) + hasMarkdown, err := system.FilesExistInTree(ctx, system.GetFileTypeListerCommand("md")) if err != nil { errs = errors.Join(errs, err) } diff --git a/internal/tasks/util/run.go b/internal/tasks/util/run.go index 8ba05f7..9ce54f3 100644 --- a/internal/tasks/util/run.go +++ b/internal/tasks/util/run.go @@ -7,7 +7,9 @@ import ( "strings" "time" + igit "github.com/opensourcecorp/oscar/internal/git" iprint "github.com/opensourcecorp/oscar/internal/print" + "github.com/opensourcecorp/oscar/internal/system" ) // A Run holds metadata about an instance of an oscar subcommand run, e.g. a list of Tasks for the @@ -16,6 +18,12 @@ type Run struct { // The "type" of Run as an informative string, i.e. "CI", "Deliver", etc. Used in // banner-printing in [Run.PrintRunTypeBanner]. Type string + // See [igit.Git]. + Git *igit.Git + // See [Repo]. + Repo Repo + // See [iprint.AllColors]. + Colors iprint.AllColors // A timestamp for storing when the overall run started. StartTime time.Time // Keeps track of all task failures. @@ -27,13 +35,30 @@ func NewRun(ctx context.Context, runType string) (Run, error) { // Kind of wonky, but print the banner first Run{Type: runType}.PrintRunTypeBanner() + colors := iprint.Colors() + // Handle system init - if err := InitSystem(ctx); err != nil { + if err := system.Init(ctx); err != nil { return Run{}, fmt.Errorf("initializing system: %w", err) } + git, err := igit.New(ctx) + if err != nil { + return Run{}, err + } + iprint.Infof(colors.Gray + git.String() + colors.Reset) + + repo, err := NewRepo(ctx) + if err != nil { + return Run{}, fmt.Errorf("getting repo composition: %w", err) + } + iprint.Infof(colors.Gray + repo.String() + colors.Reset) + return Run{ Type: runType, + Git: git, + Repo: repo, + Colors: colors, StartTime: time.Now(), Failures: make([]string, 0), }, nil @@ -41,31 +66,43 @@ func NewRun(ctx context.Context, runType string) (Run, error) { // PrintRunTypeBanner prints a banner about the type of [Run] underway. func (run Run) PrintRunTypeBanner() { + colors := iprint.Colors() + // this padding accounts for leading text length before the run.Type string padding := 9 bannerChar := "#" - fmt.Printf("%s\n", strings.Repeat(bannerChar, len(run.Type)+padding)) - fmt.Printf("%s Run: %s #\n", bannerChar, run.Type) - fmt.Printf("%s\n\n", strings.Repeat(bannerChar, len(run.Type)+padding)) + iprint.Infof( + colors.Yellow+"%s\n"+colors.Reset, + strings.Repeat(bannerChar, len(run.Type)+padding), + ) + iprint.Infof( + colors.Yellow+"%s Run: %s #\n"+colors.Reset, + bannerChar, run.Type, + ) + iprint.Infof( + colors.Yellow+"%s\n\n"+colors.Reset, + strings.Repeat(bannerChar, len(run.Type)+padding), + ) } // PrintTaskMapBanner prints a banner about the [TaskMap] being run. func (run Run) PrintTaskMapBanner(lang string) { - fmt.Printf( - "=== %s %s>\n", - lang, strings.Repeat("=", 64-len(lang)), - ) + iprint.Infof("=== %s %s>\n", lang, strings.Repeat("=", 64-len(lang))) } // PrintTaskBanner prints a banner about the Task being run. func (run Run) PrintTaskBanner(task Tasker) { // NOTE: no trailing newline on purpose - fmt.Printf("> %s %s............", task.InfoText(), strings.Repeat(".", 32-len(task.InfoText()))) + iprint.Infof( + "> %s %s............", + iprint.Colors().White+task.InfoText()+iprint.Colors().InfoColor, + strings.Repeat(".", 32-len(task.InfoText())), + ) } // ReportSuccess prints information about the success of a [Run]. func (run Run) ReportSuccess() { - fmt.Printf("\nAll tasks succeeded! (%s)\n\n", RunDurationString(run.StartTime)) + iprint.Goodf("\nAll tasks succeeded! (%s)\n\n", iprint.RunDurationString(run.StartTime)) } // ReportFailure prints information about the failure of a [Run]. It takes an `error` arg in case @@ -73,7 +110,7 @@ func (run Run) ReportSuccess() { // errors that an outer variable already holds. func (run Run) ReportFailure(err error) error { iprint.Errorf("\n%s\n", strings.Repeat("=", 65)) - iprint.Errorf("The following tasks failed: (%s)\n", RunDurationString(run.StartTime)) + iprint.Errorf("The following tasks failed: (%s)\n", iprint.RunDurationString(run.StartTime)) for _, f := range run.Failures { iprint.Errorf("- %s\n", f) } diff --git a/oscar.yaml b/oscar.yaml index 197b7cf..350266f 100644 --- a/oscar.yaml +++ b/oscar.yaml @@ -1,5 +1,5 @@ --- -version: "0.2.1" +version: "0.3.0-alpha1" deliverables: go_github_release: build_sources: From 6e83b8622eba5dd8b39b9906414617aad1dab2e3 Mon Sep 17 00:00:00 2001 From: "Ryan J. Price" Date: Sun, 28 Sep 2025 14:14:47 -0500 Subject: [PATCH 2/3] Compress builds --- Makefile | 3 ++- README.md | 16 ++++++++++++++++ internal/tasks/tools/go/deliver.go | 15 ++++++++++++--- mise.toml | 1 + 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index f80a9e6..13580c9 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,8 @@ test: ci # NOTE: oscar builds itself IRL, but having a target here makes it easier to have the Containerfile # have a stage-copiable output build: FORCE - @$(RUN) go build -o ./build/oscar ./cmd/oscar + @$(RUN) go build -ldflags '-s -w -extldflags "-static"' -o ./build/$(BINNAME) ./cmd/$(BINNAME) + @upx --best ./build/$(BINNAME) clean: FORCE @rm -rf \ diff --git a/README.md b/README.md index 604132a..819551b 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,22 @@ TODO +## Installation + +`oscar` can be installed a few different ways: + +* Downloadign a binary from a [GitHub Release](https://github.com/opensourcecorp/oscar/releases). + +* Via `mise`, using a `github` or `go` backend: + + mise use "github:opensourcecorp/oscar@" + # or + mise use "go:github.com/opensourcecorp/oscar/cmd/oscar@" + +* Via the Go toolchain: + + go install github.com/opensourcecorp/oscar/cmd/oscar@ + ## Requirements Before getting started, note that `oscar` has a few host-system runtime dependencies. Some of these diff --git a/internal/tasks/tools/go/deliver.go b/internal/tasks/tools/go/deliver.go index 81fd10c..ab41e92 100644 --- a/internal/tasks/tools/go/deliver.go +++ b/internal/tasks/tools/go/deliver.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "regexp" "strings" "github.com/opensourcecorp/oscar/internal/oscarcfg" @@ -74,9 +75,15 @@ func (t ghRelease) Exec(ctx context.Context) error { draftFlag = "--draft" } + // Don't label the Release as "latest" if the version isn't strictly MAJOR.MINOR.PATCH + latestFlag := "" + if regexp.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+$`).MatchString(cfg.GetVersion()) { + latestFlag = "--latest" + } + args := []string{"bash", "-c", fmt.Sprintf(` - gh release create v%s %s --generate-notes --verify-tag --latest ./dist/* - `, cfg.GetVersion(), draftFlag, + gh release create v%s %s --generate-notes --verify-tag %s ./dist/* + `, cfg.GetVersion(), draftFlag, latestFlag, )} if _, err := system.RunCommand(ctx, args); err != nil { @@ -124,9 +131,11 @@ func goBuild(ctx context.Context, src string) error { if _, err := system.RunCommand(ctx, []string{"bash", "-c", fmt.Sprintf(` CGO_ENABLED=0 \ GOOS=%s GOARCH=%s \ - go build -ldflags '-extldflags "-static"' -o %s %s`, + go build -ldflags '-s -w -extldflags "-static"' -o %s %s + upx --best %s`, goos, goarch, target, src, + target, )}); err != nil { return fmt.Errorf("building Go binary: %w", err) } diff --git a/mise.toml b/mise.toml index d1358f7..739336f 100644 --- a/mise.toml +++ b/mise.toml @@ -16,6 +16,7 @@ ripgrep = "14.1.1" shellcheck = "0.11.0" shfmt = "3.12.0" terraform = "1.13.3" +upx = "5.0.2" uv = "0.8.18" yamlfmt = "0.17.2" yamllint = "1.37.1" From a6b6d7fd36ffb003c69dee6ff57ffbcef552e250 Mon Sep 17 00:00:00 2001 From: "Ryan J. Price" Date: Sun, 28 Sep 2025 14:17:11 -0500 Subject: [PATCH 3/3] Fix README typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 819551b..4a55618 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ TODO `oscar` can be installed a few different ways: -* Downloadign a binary from a [GitHub Release](https://github.com/opensourcecorp/oscar/releases). +* Downloading a binary from a [GitHub Release](https://github.com/opensourcecorp/oscar/releases). * Via `mise`, using a `github` or `go` backend: