From 205b02ff2f5f8312a74b3ddb327317c86dfa963b Mon Sep 17 00:00:00 2001 From: "Ryan J. Price" Date: Sun, 21 Dec 2025 14:42:53 -0600 Subject: [PATCH 01/10] WIP reset to fix undesired formatter changes --- docker-compose.yaml | 2 +- go.mod | 2 +- internal/cli/root.go | 4 +- internal/consts/consts.go | 4 +- internal/oscarcfg/config.go | 2 +- internal/print/colors.go | 3 +- internal/print/print.go | 11 +- internal/system/system.go | 32 ++-- internal/tasks/ci/run.go | 7 +- internal/tasks/delivery/run.go | 5 +- internal/tasks/tools/containers/deliver.go | 7 +- internal/tasks/tools/go/ci.go | 145 ++------------- internal/tasks/tools/go/deliver.go | 6 +- internal/tasks/tools/markdown/ci.go | 6 +- internal/tasks/tools/toolcfg/.golangci.yaml | 168 ++++++++++++++++++ internal/tasks/tools/toolcfg/revive.toml | 36 ---- internal/tasks/tools/toolcfg/staticcheck.conf | 7 - internal/tasks/tools/version/ci.go | 7 +- internal/tasks/util/repo.go | 6 + internal/tasks/util/run.go | 5 + mise.toml | 7 +- 21 files changed, 267 insertions(+), 205 deletions(-) create mode 100644 internal/tasks/tools/toolcfg/.golangci.yaml delete mode 100644 internal/tasks/tools/toolcfg/revive.toml delete mode 100644 internal/tasks/tools/toolcfg/staticcheck.conf diff --git a/docker-compose.yaml b/docker-compose.yaml index 1ada3ee..8bb28be 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -7,7 +7,7 @@ services: dockerfile: "./Containerfile" args: GO_VERSION: "${GO_VERSION:-1.25.0}" - MISE_VERSION: "${MISE_VERSION:-v2025.9.10}" + MISE_VERSION: "${MISE_VERSION:-v2025.11.11}" http_proxy: "${http_proxy:-}" https_proxy: "${https_proxy:-}" image: "${IMAGE_URI:-}" diff --git a/go.mod b/go.mod index e736209..795041e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/opensourcecorp/oscar -go 1.24.4 +go 1.25.3 require ( github.com/urfave/cli/v3 v3.4.1 diff --git a/internal/cli/root.go b/internal/cli/root.go index 9695b7a..147aebb 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -15,7 +15,7 @@ import ( ) const ( - // Command names and their flags + // Command names and their flags. rootCmdName = "oscar" debugFlagName = "debug" noBannerFlagName = "no-banner" @@ -89,7 +89,7 @@ func getVersion() (string, error) { return "", fmt.Errorf("reading oscar config file: %w", err) } - return cfg.Version, nil + return cfg.GetVersion(), nil } // rootAction defines the logic for oscar's root command. diff --git a/internal/consts/consts.go b/internal/consts/consts.go index 059289e..1bf7ff9 100644 --- a/internal/consts/consts.go +++ b/internal/consts/consts.go @@ -19,7 +19,7 @@ const ( // 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. - MiseVersion = "v2025.9.10" + MiseVersion = "v2025.11.11" // DefaultOscarCfgFileName is the default basename of oscar's config file. DefaultOscarCfgFileName = "oscar.yaml" @@ -34,7 +34,7 @@ var ( // MiseBinPath is the absolute path to the mise binary, if oscar is the one installing it. MiseBinPath = filepath.Join(OscarHomeBin, "mise") - // MiseConfigFileName is the basename of the mise configuration file that oscar uses + // MiseConfigFileName is the basename of the mise configuration file that oscar uses. MiseConfigFileName = "mise.oscar.toml" // MiseEnvVars maps mise's env var keys to their desired values. diff --git a/internal/oscarcfg/config.go b/internal/oscarcfg/config.go index 646a6c4..1b9e5aa 100644 --- a/internal/oscarcfg/config.go +++ b/internal/oscarcfg/config.go @@ -43,7 +43,7 @@ func Get(pathOverride ...string) (*oscarcfgpbv1.Config, error) { } iprint.Debugf("map data as JSON string: %s\n", string(jsonData)) - var cfg = &oscarcfgpbv1.Config{} + cfg := &oscarcfgpbv1.Config{} if err := protojson.Unmarshal(jsonData, cfg); err != nil { return nil, fmt.Errorf("unmarshalling oscar config file '%s': %w", path, err) } diff --git a/internal/print/colors.go b/internal/print/colors.go index 97baad4..3548fcf 100644 --- a/internal/print/colors.go +++ b/internal/print/colors.go @@ -17,7 +17,7 @@ import ( ) var ( - // ANSI color codes + // ANSI color codes. reset = "\033[0m" red = "\033[31m" green = "\033[32m" @@ -83,6 +83,7 @@ func color(ansiCode string) string { if !term.IsTerminal(int(os.Stdout.Fd())) { return "" } + if noColor := os.Getenv(consts.OscarEnvVarNoColor); noColor != "" { return "" } diff --git a/internal/print/print.go b/internal/print/print.go index 686e0e3..f2989eb 100644 --- a/internal/print/print.go +++ b/internal/print/print.go @@ -10,7 +10,7 @@ import ( // Banner prints the oscar stylistic banner. func Banner() { - var banner = ` + banner := ` ____________________ \ =~=~=~=~=/____________________/|---------\ ~=~=~=~=| _ _ _ _ _ |/|----------\ @@ -48,7 +48,10 @@ func Warnf(format string, args ...any) { ); err != nil { // NOTE: panicking is fine here, this would be catastrophic lol panic( - fmt.Sprintf(colors.ErrorColor+"trying to write warning to stderr: %v"+colors.Reset, err), + fmt.Sprintf( + colors.ErrorColor+"trying to write warning to stderr: %v"+colors.Reset, + err, + ), ) } } @@ -68,7 +71,7 @@ func Errorf(format string, args ...any) { } } -// Goodf is a helper function that prints green info text indicating something went well +// 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...) @@ -77,5 +80,5 @@ func Goodf(format string, args ...any) { // 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()) + return "t: " + time.Since(t).Round(time.Second/1000).String() } diff --git a/internal/system/system.go b/internal/system/system.go index 87eebe5..3a8252a 100644 --- a/internal/system/system.go +++ b/internal/system/system.go @@ -21,6 +21,7 @@ import ( // 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{ @@ -30,18 +31,21 @@ func Init(ctx context.Context) error { for _, cmd := range requiredSystemCommands { iprint.Debugf("Running '%v'\n", cmd) + if output, err := exec.CommandContext(ctx, cmd[0], cmd[1:]...).CombinedOutput(); err != nil { return fmt.Errorf( "command '%s' possibly not found on PATH, cannot continue (error: %w -- output: %s)", - cmd[0], err, string(output), + cmd[0], + err, + string(output), ) } } for _, d := range []string{consts.OscarHome, consts.OscarHomeBin} { - if err := os.MkdirAll(d, 0755); err != nil { + if err := os.MkdirAll(d, 0o755); err != nil { return fmt.Errorf( - "internal error when creating oscar directory '%s': %v", + "internal error when creating oscar directory '%s': %w", d, err, ) } @@ -50,7 +54,7 @@ func Init(ctx context.Context) error { for name, value := range consts.MiseEnvVars { if err := os.Setenv(name, value); err != nil { return fmt.Errorf( - "internal error when setting mise env var '%s': %v", + "internal error when setting mise env var '%s': %w", name, err, ) } @@ -73,6 +77,7 @@ func Init(ctx context.Context) error { if _, err := RunCommand(ctx, []string{consts.MiseBinPath, "trust", consts.MiseConfigFileName}); err != nil { return fmt.Errorf("running mise trust: %w", err) } + if _, err := RunCommand(ctx, []string{consts.MiseBinPath, "install"}); err != nil { return fmt.Errorf("running mise install: %w", err) } @@ -90,7 +95,10 @@ func Init(ctx context.Context) error { // parse it on their own. func RunCommand(ctx context.Context, cmdArgs []string) (string, error) { if len(cmdArgs) <= 1 { - return "", fmt.Errorf("internal error: not enough arguments passed to RunCommand() -- received: %v", cmdArgs) + return "", fmt.Errorf( + "internal error: not enough arguments passed to RunCommand() -- received: %v", + cmdArgs, + ) } var args []string @@ -102,6 +110,7 @@ func RunCommand(ctx context.Context, cmdArgs []string) (string, error) { cmd := exec.CommandContext(ctx, consts.MiseBinPath, args...) iprint.Debugf("Running '%v'\n", cmd.Args) + output, err := cmd.CombinedOutput() if err != nil { return "", fmt.Errorf( @@ -136,10 +145,10 @@ func GetFileTypeListerCommand(fileType string) string { // [mise]: https://mise.jdx.dev func installMise(_ context.Context) (err error) { miseFound := true - _, err = os.Stat(consts.MiseBinPath) - if err != nil { + + if _, statErr := os.Stat(consts.MiseBinPath); statErr != nil { iprint.Debugf("error when running os.Stat(consts.MiseBinPath): %w\n", err) - if os.IsNotExist(err) { + if os.IsNotExist(statErr) { miseFound = false iprint.Debugf("mise not found, will install\n") } else { @@ -172,13 +181,14 @@ func installMise(_ context.Context) (err error) { miseReleaseURL := fmt.Sprintf( "https://github.com/jdx/mise/releases/download/%s/mise-%s-%s-%s", - consts.MiseVersion, consts.MiseVersion, host.Kernel, host.Arch, + miseVersion, miseVersion, host.Kernel, host.Arch, ) out, err := os.Create(consts.MiseBinPath) if err != nil { return fmt.Errorf("creating mise target file: %w", err) } + defer func() { if closeErr := out.Close(); closeErr != nil { err = errors.Join(err, fmt.Errorf("closing mise target file: %w", closeErr)) @@ -190,6 +200,7 @@ func installMise(_ context.Context) (err error) { if err != nil { return fmt.Errorf("making GET request for mise GitHub Release: %w", err) } + defer func() { if closeErr := resp.Body.Close(); closeErr != nil { err = errors.Join(err, fmt.Errorf("closing response body: %w", closeErr)) @@ -204,7 +215,7 @@ func installMise(_ context.Context) (err error) { return fmt.Errorf("writing mise data to target: %w", err) } - if err := os.Chmod(consts.MiseBinPath, 0755); err != nil { + if err := os.Chmod(consts.MiseBinPath, 0o755); err != nil { return fmt.Errorf("changing mise binary to be executable: %w", err) } @@ -225,6 +236,7 @@ func FilesExistInTree(ctx context.Context, findScript string) (bool, error) { if strings.Contains(string(output), "No such file or directory") { return false, nil } + return false, fmt.Errorf("finding files: %w -- output:\n%s", err, string(output)) } diff --git a/internal/tasks/ci/run.go b/internal/tasks/ci/run.go index e5febfb..8d679c2 100644 --- a/internal/tasks/ci/run.go +++ b/internal/tasks/ci/run.go @@ -20,9 +20,10 @@ import ( taskutil "github.com/opensourcecorp/oscar/internal/tasks/util" ) -// getCITaskMap assembles the overall list of CI tasks, keyed by their language/tooling name +// getCITaskMap assembles the overall list of CI tasks, keyed by their language/tooling name. func getCITaskMap(repo taskutil.Repo) (taskutil.TaskMap, error) { out := make(taskutil.TaskMap) + for langName, getTasksFunc := range map[string]func(taskutil.Repo) []taskutil.Tasker{ "Versioning": versiontools.NewTasksForCI, "Go": gotools.NewTasksForCI, @@ -73,19 +74,23 @@ func Run(ctx context.Context) (err error) { tasks := taskMap[lang] run.PrintTaskMapBanner(lang) + for _, task := range tasks { taskStartTime := time.Now() + run.PrintTaskBanner(task) // NOTE: this error is checked later, when we can check the Run, Post, and git-diff // potential errors together var runErr error + runErr = errors.Join(runErr, task.Exec(ctx)) runErr = errors.Join(runErr, task.Post(ctx)) if err := gitCI.Update(ctx); err != nil { return fmt.Errorf("internal error: %w", err) } + gitStatusHasChanged, err := gitCI.StatusHasChanged(ctx) if err != nil { return fmt.Errorf("internal error: %w", err) diff --git a/internal/tasks/delivery/run.go b/internal/tasks/delivery/run.go index 422f768..410f944 100644 --- a/internal/tasks/delivery/run.go +++ b/internal/tasks/delivery/run.go @@ -20,6 +20,7 @@ import ( // name. func getDeliveryTaskMap(repo taskutil.Repo) (taskutil.TaskMap, error) { out := make(taskutil.TaskMap) + for langName, getTasksFunc := range map[string]func(taskutil.Repo) ([]taskutil.Tasker, error){ // Independent of Delivery tasks, always push a Git Tag first "0 - Create Git Tag": gittagtools.NewTasksForDelivery, @@ -75,14 +76,16 @@ func Run(ctx context.Context) (err error) { for _, task := range tasks { taskStartTime := time.Now() + run.PrintTaskBanner(task) // NOTE: this error is checked later, when we can check the Run, Post, and git-diff // potential errors together var runErr error + runErr = errors.Join(runErr, task.Exec(ctx)) - runErr = errors.Join(runErr, task.Post(ctx)) + runErr = errors.Join(runErr, task.Post(ctx)) if runErr != nil { iprint.Errorf("FAILED (%s)\n", iprint.RunDurationString(taskStartTime)) iprint.Errorf("%v\n", runErr) diff --git a/internal/tasks/tools/containers/deliver.go b/internal/tasks/tools/containers/deliver.go index 64d4470..bbabc3e 100644 --- a/internal/tasks/tools/containers/deliver.go +++ b/internal/tasks/tools/containers/deliver.go @@ -72,6 +72,7 @@ func (t imageBuildPush) Exec(ctx context.Context) error { if err != nil { return err } + cfg := rootCfg.GetDeliverables().GetContainerImage() uri, err := constructImageURI(ctx, rootCfg) @@ -88,6 +89,7 @@ func (t imageBuildPush) Exec(ctx context.Context) error { if err := yaml.Unmarshal(composeFileContents, composeFile); err != nil { return err } + iprint.Debugf("composeFile unmarshalled: %#v\n", composeFile) curDir, err := os.Getwd() @@ -103,6 +105,7 @@ func (t imageBuildPush) Exec(ctx context.Context) error { if err != nil { return err } + iprint.Debugf("edited Compose file YAML: %s\n", string(composeOut)) workDir := filepath.Join(os.TempDir(), "oscar-oci") @@ -118,7 +121,7 @@ func (t imageBuildPush) Exec(ctx context.Context) error { registryMap := newRegistryMap(cfg.GetName()) var authArgs []string - if strings.Contains(cfg.Registry, "ghcr") { + if strings.Contains(cfg.GetRegistry(), "ghcr") { authArgs = registryMap.GitHub.AuthCommand } @@ -143,6 +146,7 @@ func (t imageBuildPush) Post(_ context.Context) error { return nil } // constructImageURI constructs an image URI based on data from oscar's config & Git. func constructImageURI(ctx context.Context, rootCfg *oscarcfgpbv1.Config) (string, error) { cfg := rootCfg.GetDeliverables().GetContainerImage() + git, err := igit.New(ctx) if err != nil { return "", fmt.Errorf("getting Git info: %w", err) @@ -158,6 +162,7 @@ func constructImageURI(ctx context.Context, rootCfg *oscarcfgpbv1.Config) (strin } else { tag = fmt.Sprintf("%s-%s", git.SanitizedBranch(), git.LatestCommit) } + if git.IsDirty { tag = fmt.Sprintf("%s-%s-dirty", git.SanitizedBranch(), git.LatestCommit) } diff --git a/internal/tasks/tools/go/ci.go b/internal/tasks/tools/go/ci.go index 2312889..052499a 100644 --- a/internal/tasks/tools/go/ci.go +++ b/internal/tasks/tools/go/ci.go @@ -13,14 +13,9 @@ import ( type ( goModCheck struct{ taskutil.Tool } - goFormat struct{ taskutil.Tool } generateCodeCI struct{ taskutil.Tool } goBuildCI struct{ taskutil.Tool } - goVet struct{ taskutil.Tool } - staticcheck struct{ taskutil.Tool } - revive struct{ taskutil.Tool } - errcheck struct{ taskutil.Tool } - goImports struct{ taskutil.Tool } + golangciLint struct{ taskutil.Tool } govulncheck struct{ taskutil.Tool } goTest struct{ taskutil.Tool } ) @@ -34,16 +29,6 @@ func NewTasksForCI(repo taskutil.Repo) []taskutil.Tasker { RunArgs: []string{"go", "mod", "tidy"}, }, }, - goFormat{ - Tool: taskutil.Tool{ - RunArgs: []string{"go", "fmt", "./..."}, - }, - }, - goImports{ - Tool: taskutil.Tool{ - RunArgs: []string{"goimports", "-l", "-w", "."}, - }, - }, generateCodeCI{ Tool: taskutil.Tool{ RunArgs: []string{"go", "generate", "./..."}, @@ -54,30 +39,12 @@ func NewTasksForCI(repo taskutil.Repo) []taskutil.Tasker { RunArgs: []string{"go", "build", "./..."}, }, }, - goVet{ - Tool: taskutil.Tool{ - RunArgs: []string{"go", "vet", "./..."}, - }, - }, - staticcheck{ - Tool: taskutil.Tool{ - RunArgs: []string{"staticcheck", "./..."}, - // NOTE: staticcheck does not have a flag to point to a config file, so we need - // to put it at the repo root - ConfigFilePath: filepath.Join("staticcheck.conf"), - }, - }, - revive{ + golangciLint{ Tool: taskutil.Tool{ RunArgs: []string{ - "revive", "--config", "{{ConfigFilePath}}", "--set_exit_status", "./...", + "golangci-lint", "run", "--config", "{{ConfigFilePath}}", }, - ConfigFilePath: filepath.Join(os.TempDir(), "revive.toml"), - }, - }, - errcheck{ - Tool: taskutil.Tool{ - RunArgs: []string{"errcheck", "./..."}, + ConfigFilePath: filepath.Join(os.TempDir(), ".golangci.yaml"), }, }, govulncheck{ @@ -111,36 +78,6 @@ func (t goModCheck) Exec(ctx context.Context) error { // Post implements [taskutil.Tasker.Post]. func (t goModCheck) Post(_ context.Context) error { return nil } -// InfoText implements [taskutil.Tasker.InfoText]. -func (t goFormat) InfoText() string { return "Format" } - -// Exec implements [taskutil.Tasker.Exec]. -func (t goFormat) Exec(ctx context.Context) error { - if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { - return err - } - - return nil -} - -// Post implements [taskutil.Tasker.Post]. -func (t goFormat) Post(_ context.Context) error { return nil } - -// InfoText implements [taskutil.Tasker.InfoText]. -func (t goImports) InfoText() string { return "Format imports" } - -// Exec implements [taskutil.Tasker.Exec]. -func (t goImports) Exec(ctx context.Context) error { - if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { - return err - } - - return nil -} - -// Post implements [taskutil.Tasker.Post]. -func (t goImports) Post(_ context.Context) error { return nil } - // InfoText implements [taskutil.Tasker.InfoText]. func (t generateCodeCI) InfoText() string { return "Generate code" } @@ -155,12 +92,15 @@ func (t generateCodeCI) Exec(ctx context.Context) error { tasks := NewTasksForCI(taskutil.Repo{HasGo: true}) for _, task := range tasks { if wantToRun, isOfType := task.(goFormat); isOfType { - if err := wantToRun.Exec(ctx); err != nil { + err := wantToRun.Exec(ctx) + if err != nil { return fmt.Errorf("running Go formatter after code generation: %w", err) } } + if wantToRun, isOfType := task.(goImports); isOfType { - if err := wantToRun.Exec(ctx); err != nil { + err := wantToRun.Exec(ctx) + if err != nil { return fmt.Errorf("running Go formatter after code generation: %w", err) } } @@ -188,51 +128,12 @@ func (t goBuildCI) Exec(ctx context.Context) error { func (t goBuildCI) Post(_ context.Context) error { return nil } // InfoText implements [taskutil.Tasker.InfoText]. -func (t goVet) InfoText() string { return "Vet" } - -// Exec implements [taskutil.Tasker.Exec]. -func (t goVet) Exec(ctx context.Context) error { - if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { - return err - } - - return nil -} - -// Post implements [taskutil.Tasker.Post]. -func (t goVet) Post(_ context.Context) error { return nil } - -// InfoText implements [taskutil.Tasker.InfoText]. -func (t staticcheck) InfoText() string { return "Lint (staticcheck)" } - -// Exec implements [taskutil.Tasker.Exec]. -func (t staticcheck) Exec(ctx context.Context) error { - if err := toolcfg.SetupConfigFile(t.Tool); err != nil { - return err - } - - if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { - return err - } - - return nil -} - -// Post implements [taskutil.Tasker.Post]. -func (t staticcheck) Post(_ context.Context) error { - if err := os.RemoveAll(t.ConfigFilePath); err != nil { - return fmt.Errorf("removing config file: %w", err) - } - - return nil -} - -// InfoText implements [taskutil.Tasker.InfoText]. -func (t revive) InfoText() string { return "Lint (revive)" } +func (t golangciLint) InfoText() string { return "Lint (golangci-lint)" } // Exec implements [taskutil.Tasker.Exec]. -func (t revive) Exec(ctx context.Context) error { - if err := toolcfg.SetupConfigFile(t.Tool); err != nil { +func (t golangciLint) Exec(ctx context.Context) error { + err := toolcfg.SetupConfigFile(t.Tool) + if err != nil { return err } @@ -244,29 +145,15 @@ func (t revive) Exec(ctx context.Context) error { } // Post implements [taskutil.Tasker.Post]. -func (t revive) Post(_ context.Context) error { - if err := os.RemoveAll(t.ConfigFilePath); err != nil { +func (t golangciLint) Post(_ context.Context) error { + err := os.RemoveAll(t.ConfigFilePath) + if err != nil { return fmt.Errorf("removing config file: %w", err) } return nil } -// InfoText implements [taskutil.Tasker.InfoText]. -func (t errcheck) InfoText() string { return "Lint (errcheck)" } - -// Exec implements [taskutil.Tasker.Exec]. -func (t errcheck) Exec(ctx context.Context) error { - if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { - return err - } - - return nil -} - -// Post implements [taskutil.Tasker.Post]. -func (t errcheck) Post(_ context.Context) error { return nil } - // InfoText implements [taskutil.Tasker.InfoText]. func (t govulncheck) InfoText() string { return "Vulnerability scan (govulncheck)" } diff --git a/internal/tasks/tools/go/deliver.go b/internal/tasks/tools/go/deliver.go index 6eccf55..177ace7 100644 --- a/internal/tasks/tools/go/deliver.go +++ b/internal/tasks/tools/go/deliver.go @@ -53,6 +53,7 @@ func (t ghRelease) Exec(ctx context.Context) error { for _, src := range cfg.GetDeliverables().GetGoGithubRelease().GetBuildSources() { buildErrs = errors.Join(buildErrs, goBuild(ctx, src)) } + if buildErrs != nil { return buildErrs } @@ -102,7 +103,10 @@ func (t ghRelease) Post(_ context.Context) error { return nil } // root-level "build/" subdirectory. func goBuild(ctx context.Context, src string) error { if strings.HasSuffix(src, ".go") { - return fmt.Errorf("provided Go build source '%s' was a file, but must be a path to a package", src) + return fmt.Errorf( + "provided Go build source '%s' was a file, but must be a path to a package", + src, + ) } targetDir := "build" diff --git a/internal/tasks/tools/markdown/ci.go b/internal/tasks/tools/markdown/ci.go index e371345..2a70c98 100644 --- a/internal/tasks/tools/markdown/ci.go +++ b/internal/tasks/tools/markdown/ci.go @@ -20,7 +20,11 @@ func NewTasksForCI(repo taskutil.Repo) []taskutil.Tasker { return []taskutil.Tasker{ markdownlint{ Tool: taskutil.Tool{ - RunArgs: []string{"markdownlint-cli2", "--config", "{{ConfigFilePath}}", "**/*.md"}, + RunArgs: []string{ + "markdownlint-cli2", + "--config", "{{ConfigFilePath}}", + "**/*.md", + }, ConfigFilePath: filepath.Join(os.TempDir(), ".markdownlint-cli2.yaml"), }, }, diff --git a/internal/tasks/tools/toolcfg/.golangci.yaml b/internal/tasks/tools/toolcfg/.golangci.yaml new file mode 100644 index 0000000..7012343 --- /dev/null +++ b/internal/tasks/tools/toolcfg/.golangci.yaml @@ -0,0 +1,168 @@ +# NOTE: modified based on defaults found at: +# https://golangci-lint.run/docs/configuration/file +--- +version: 2 +linters: + # Disable all so we can then enable just the ones we want + default: "none" + enable: + # - "arangolint" + # - "asasalint" + # - "asciicheck" + # - "bidichk" + # - "bodyclose" + # - "canonicalheader" + # - "containedctx" + # - "contextcheck" + # - "copyloopvar" + # - "cyclop" + # - "decorder" + # - "depguard" + # - "dogsled" + # - "dupl" + # - "dupword" + # - "durationcheck" + # - "embeddedstructfieldcheck" + # - "err113" + - "errcheck" + # - "errchkjson" + # - "errname" + # - "errorlint" + # - "exhaustive" + # - "exhaustruct" + # - "exptostd" + # - "fatcontext" + # - "forbidigo" + # - "forcetypeassert" + # - "funcorder" + # - "funlen" + # - "ginkgolinter" + # - "gocheckcompilerdirectives" + # - "gochecknoglobals" + # - "gochecknoinits" + # - "gochecksumtype" + # - "gocognit" + # - "goconst" + # - "gocritic" + # - "gocyclo" + - "godoclint" + # - "godot" + # - "godox" + # - "goheader" + # - "gomoddirectives" + # - "gomodguard" + # - "goprintffuncname" + # - "gosec" + # - "gosmopolitan" + - "govet" + # - "grouper" + # - "iface" + # - "importas" + # - "inamedparam" + # - "ineffassign" + # - "interfacebloat" + # - "intrange" + # - "iotamixing" + # - "ireturn" + # - "lll" + # - "loggercheck" + # - "maintidx" + # - "makezero" + # - "mirror" + # - "misspell" + # - "mnd" + # - "modernize" + # - "musttag" + # - "nakedret" + # - "nestif" + # - "nilerr" + # - "nilnesserr" + # - "nilnil" + # - "nlreturn" + # - "noctx" + # - "noinlineerr" + # - "nolintlint" + # - "nonamedreturns" + # - "nosprintfhostport" + # - "paralleltest" + # - "perfsprint" + # - "prealloc" + # - "predeclared" + # - "promlinter" + - "protogetter" + # - "reassign" + # - "recvcheck" + - "revive" + # - "rowserrcheck" + # - "sloglint" + # - "spancheck" + # - "sqlclosecheck" + - "staticcheck" + # - "tagalign" + # - "tagliatelle" + # - "testableexamples" + # - "testifylint" + # - "testpackage" + # - "thelper" + # - "tparallel" + # - "unconvert" + # - "unparam" + # - "unqueryvet" + # - "unused" + # - "usestdlibvars" + # - "usetesting" + # - "varnamelen" + # - "wastedassign" + # - "whitespace" + # - "wrapcheck" + # - "wsl_v5" + # - "zerologlint" + disable: [] + # See: https://golangci-lint.run/docs/linters/configuration + settings: + revive: + confidence: 0.8 + errorCode: 1 + warningCode: 1 + ignoreGeneratedHeader: false + severity: "warning" + rules: + - name: "increment-decrement" + disabled: true + staticcheck: + checks: + - "all" + # staticcheck won't ignore generated code for this one, so revive handles this check instead + - "-ST1000" + exclusions: + # See: https://go.dev/s/generatedcode + generated: "strict" + +formatters: + enable: + # - "gci" + - "gofmt" + # - "gofumpt" + - "goimports" + # - "golines" + # - "swaggo" + # See: https://golangci-lint.run/docs/formatters/configuration + settings: {} + exclusions: + generated: "strict" + +issues: + new: false + # new-from-merge-base: main + fix: true + +output: {} + +run: + timeout: 0 + relative-path-mode: "gomod" + allow-parallel-runners: true + allow-serial-runners: true + +severity: + default: "warning" diff --git a/internal/tasks/tools/toolcfg/revive.toml b/internal/tasks/tools/toolcfg/revive.toml deleted file mode 100644 index 9e34f49..0000000 --- a/internal/tasks/tools/toolcfg/revive.toml +++ /dev/null @@ -1,36 +0,0 @@ -# Config file for revive Go linter -# This was initially pulled from defaults.toml found here: -# https://github.com/mgechev/revive/blob/251470be6ab08b06e86f9a2d0178cef5f8bbca97/defaults.toml -confidence = 0.8 -errorCode = 1 -warningCode = 1 - -ignoreGeneratedHeader = false -severity = "warning" - -exclude = [] - -[rule.blank-imports] -[rule.context-as-argument] -[rule.context-keys-type] -[rule.dot-imports] -[rule.empty-block] -[rule.error-naming] -[rule.error-return] -[rule.error-strings] -[rule.errorf] -[rule.exported] -[rule.increment-decrement] -Disabled = true -[rule.indent-error-flow] -[rule.package-comments] -[rule.range] -[rule.receiver-naming] -[rule.redefines-builtin-id] -[rule.superfluous-else] -[rule.time-naming] -[rule.unexported-return] -[rule.unreachable-code] -[rule.unused-parameter] -[rule.var-declaration] -[rule.var-naming] diff --git a/internal/tasks/tools/toolcfg/staticcheck.conf b/internal/tasks/tools/toolcfg/staticcheck.conf deleted file mode 100644 index 2edb311..0000000 --- a/internal/tasks/tools/toolcfg/staticcheck.conf +++ /dev/null @@ -1,7 +0,0 @@ -# All checks available here: -# https://staticcheck.dev/docs/checks/ -checks = [ - "all", - # staticcheck won't ignore generated code for this one, so revive handles this check instead - "-ST1000" -] diff --git a/internal/tasks/tools/version/ci.go b/internal/tasks/tools/version/ci.go index 223ec39..963a61d 100644 --- a/internal/tasks/tools/version/ci.go +++ b/internal/tasks/tools/version/ci.go @@ -33,6 +33,7 @@ func (t versionCI) Exec(ctx context.Context) (err error) { if err != nil { return fmt.Errorf("getting oscar config: %w", err) } + version := cfg.GetVersion() iprint.Debugf("provided version: %s\n", version) @@ -41,9 +42,10 @@ func (t versionCI) Exec(ctx context.Context) (err error) { // least of which being that alternatives would be unreliable in e.g. GitHub Actions CI based on // how it treats PR checkouts et al. A small price to pay for reliability. tmpCloneDir := filepath.Join(os.TempDir(), "oscar-ci", "this-repo") - if err := os.MkdirAll(filepath.Dir(tmpCloneDir), 0755); err != nil { + if err := os.MkdirAll(filepath.Dir(tmpCloneDir), 0o755); err != nil { return fmt.Errorf("creating temp clone parent directory: %w", err) } + defer func() { if rmErr := os.RemoveAll(tmpCloneDir); rmErr != nil { err = errors.Join(err, fmt.Errorf("removing temp clone directory: %w", rmErr)) @@ -65,7 +67,9 @@ func (t versionCI) Exec(ctx context.Context) (err error) { if err != nil { return fmt.Errorf("getting oscar config: %w", err) } + mainVersion := mainCfg.GetVersion() + iprint.Debugf("main version: %s\n", version) // Need to check if we're already on the main branch, since checking its version against itself @@ -77,6 +81,7 @@ func (t versionCI) Exec(ctx context.Context) (err error) { if err != nil { return fmt.Errorf("checking current Git branch/ref: %w", err) } + iprint.Debugf("current Git branch/ref: %s\n", branch) if branch != "main" { diff --git a/internal/tasks/util/repo.go b/internal/tasks/util/repo.go index d7992ae..0cb920d 100644 --- a/internal/tasks/util/repo.go +++ b/internal/tasks/util/repo.go @@ -28,21 +28,27 @@ func (repo Repo) String() string { if repo.HasGo { out += "- Go\n" } + if repo.HasPython { out += "- Python\n" } + if repo.HasShell { out += "- Shell (sh, bash, etc.)\n" } + if repo.HasTerraform { out += "- Terraform\n" } + if repo.HasContainerfile { out += "- Containerfile\n" } + if repo.HasYaml { out += "- YAML\n" } + if repo.HasMarkdown { out += "- Markdown\n" } diff --git a/internal/tasks/util/run.go b/internal/tasks/util/run.go index 9ce54f3..481ede4 100644 --- a/internal/tasks/util/run.go +++ b/internal/tasks/util/run.go @@ -46,12 +46,14 @@ func NewRun(ctx context.Context, runType string) (Run, error) { 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{ @@ -111,11 +113,14 @@ func (run Run) ReportSuccess() { func (run Run) ReportFailure(err error) error { iprint.Errorf("\n%s\n", strings.Repeat("=", 65)) iprint.Errorf("The following tasks failed: (%s)\n", iprint.RunDurationString(run.StartTime)) + for _, f := range run.Failures { iprint.Errorf("- %s\n", f) } + iprint.Errorf("%s\n\n", strings.Repeat("=", 65)) err = errors.Join(err, errors.New("one or more tasks failed")) + return err } diff --git a/mise.toml b/mise.toml index 739336f..b0cde2e 100644 --- a/mise.toml +++ b/mise.toml @@ -5,7 +5,7 @@ jobs = 4 [tools] buf = "1.57.2" github-cli = "2.79.0" -go = "1.25.1" +go = "1.25.3" hadolint = "2.13.1" markdownlint-cli2 = "0.18.1" # required for markdownlint-cli2 @@ -22,12 +22,9 @@ yamlfmt = "0.17.2" yamllint = "1.37.1" ### Go-specific tools, for oscar or its development -"go:github.com/kisielk/errcheck" = { version = "1.9.0" } -"go:github.com/mgechev/revive" = { version = "1.12.0" } -"go:golang.org/x/tools/cmd/goimports" = { version = "0.37.0" } +"github:golangci/golangci-lint" = { version = "2.6.2" } "go:golang.org/x/vuln/cmd/govulncheck" = { version = "1.1.4" } "go:google.golang.org/protobuf/cmd/protoc-gen-go" = { version = "1.36.8" } -"go:honnef.co/go/tools/cmd/staticcheck" = { version = "2025.1.1" } ### Python-specific tools. Note that others that may also be CLI tools, may be run internally via ### `uvx` instead. From 9b3a54f1b1d39b3e705e108f761bd5ebb2bb6090 Mon Sep 17 00:00:00 2001 From: "Ryan J. Price" Date: Sun, 21 Dec 2025 15:10:06 -0600 Subject: [PATCH 02/10] get golangci-lint replacements working --- go.mod | 2 +- .../oscar/config/v1/config.pb.go | 7 ++- internal/tasks/tools/containers/ci.go | 2 +- internal/tasks/tools/containers/deliver.go | 2 +- internal/tasks/tools/go/ci.go | 49 +++++++++++++++---- internal/tasks/tools/python/ci.go | 10 ++-- internal/tasks/tools/shell/ci.go | 4 +- internal/tasks/tools/yaml/ci.go | 4 +- mise.toml | 2 +- oscar.yaml | 2 +- 10 files changed, 56 insertions(+), 28 deletions(-) diff --git a/go.mod b/go.mod index 795041e..5091281 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/opensourcecorp/oscar -go 1.25.3 +go 1.25.5 require ( github.com/urfave/cli/v3 v3.4.1 diff --git a/internal/generated/opensourcecorp/oscar/config/v1/config.pb.go b/internal/generated/opensourcecorp/oscar/config/v1/config.pb.go index 252d1c4..0561a17 100644 --- a/internal/generated/opensourcecorp/oscar/config/v1/config.pb.go +++ b/internal/generated/opensourcecorp/oscar/config/v1/config.pb.go @@ -7,13 +7,12 @@ package oscarcfgpbv1 import ( - reflect "reflect" - sync "sync" - unsafe "unsafe" - _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" ) const ( diff --git a/internal/tasks/tools/containers/ci.go b/internal/tasks/tools/containers/ci.go index 9d77232..9240767 100644 --- a/internal/tasks/tools/containers/ci.go +++ b/internal/tasks/tools/containers/ci.go @@ -39,7 +39,7 @@ func NewTasksForCI(repo taskutil.Repo) []taskutil.Tasker { // InfoText implements [taskutil.Tasker.InfoText]. func (t hadolint) InfoText() string { return "Lint (hadolint)" } -// Run implements [taskutil.Tasker.Run]. +// Exec implements [taskutil.Tasker.Exec]. func (t hadolint) Exec(ctx context.Context) error { if err := toolcfg.SetupConfigFile(t.Tool); err != nil { return err diff --git a/internal/tasks/tools/containers/deliver.go b/internal/tasks/tools/containers/deliver.go index bbabc3e..bc2541f 100644 --- a/internal/tasks/tools/containers/deliver.go +++ b/internal/tasks/tools/containers/deliver.go @@ -66,7 +66,7 @@ func NewTasksForDelivery(repo taskutil.Repo) ([]taskutil.Tasker, error) { // InfoText implements [taskutil.Tasker.InfoText]. func (t imageBuildPush) InfoText() string { return "Image Build & Push" } -// Run implements [taskutil.Tasker.Run]. +// Exec implements [taskutil.Tasker.Exec]. func (t imageBuildPush) Exec(ctx context.Context) error { rootCfg, err := oscarcfg.Get() if err != nil { diff --git a/internal/tasks/tools/go/ci.go b/internal/tasks/tools/go/ci.go index 052499a..67a5b22 100644 --- a/internal/tasks/tools/go/ci.go +++ b/internal/tasks/tools/go/ci.go @@ -15,6 +15,7 @@ type ( goModCheck struct{ taskutil.Tool } generateCodeCI struct{ taskutil.Tool } goBuildCI struct{ taskutil.Tool } + golangciFmt struct{ taskutil.Tool } golangciLint struct{ taskutil.Tool } govulncheck struct{ taskutil.Tool } goTest struct{ taskutil.Tool } @@ -39,6 +40,14 @@ func NewTasksForCI(repo taskutil.Repo) []taskutil.Tasker { RunArgs: []string{"go", "build", "./..."}, }, }, + golangciFmt{ + Tool: taskutil.Tool{ + RunArgs: []string{ + "golangci-lint", "fmt", "--config", "{{ConfigFilePath}}", + }, + ConfigFilePath: filepath.Join(os.TempDir(), ".golangci.yaml"), + }, + }, golangciLint{ Tool: taskutil.Tool{ RunArgs: []string{ @@ -88,20 +97,13 @@ func (t generateCodeCI) Exec(ctx context.Context) error { } // Generating code will likely throw diffs if not also addressing other formatting CI checks, so - // run those here again as well. + // run those here as well. tasks := NewTasksForCI(taskutil.Repo{HasGo: true}) for _, task := range tasks { - if wantToRun, isOfType := task.(goFormat); isOfType { - err := wantToRun.Exec(ctx) - if err != nil { - return fmt.Errorf("running Go formatter after code generation: %w", err) - } - } - - if wantToRun, isOfType := task.(goImports); isOfType { + if wantToRun, isOfType := task.(golangciFmt); isOfType { err := wantToRun.Exec(ctx) if err != nil { - return fmt.Errorf("running Go formatter after code generation: %w", err) + return fmt.Errorf("running Go formatters after code generation: %w", err) } } } @@ -127,6 +129,33 @@ func (t goBuildCI) Exec(ctx context.Context) error { // Post implements [taskutil.Tasker.Post]. func (t goBuildCI) Post(_ context.Context) error { return nil } +// InfoText implements [taskutil.Tasker.InfoText]. +func (t golangciFmt) InfoText() string { return "Format (golangci-lint)" } + +// Exec implements [taskutil.Tasker.Exec]. +func (t golangciFmt) Exec(ctx context.Context) error { + err := toolcfg.SetupConfigFile(t.Tool) + if err != nil { + return err + } + + if _, err := system.RunCommand(ctx, t.RenderRunCommandArgs()); err != nil { + return err + } + + return nil +} + +// Post implements [taskutil.Tasker.Post]. +func (t golangciFmt) Post(_ context.Context) error { + err := os.RemoveAll(t.ConfigFilePath) + if err != nil { + return fmt.Errorf("removing config file: %w", err) + } + + return nil +} + // InfoText implements [taskutil.Tasker.InfoText]. func (t golangciLint) InfoText() string { return "Lint (golangci-lint)" } diff --git a/internal/tasks/tools/python/ci.go b/internal/tasks/tools/python/ci.go index f13175a..b590c99 100644 --- a/internal/tasks/tools/python/ci.go +++ b/internal/tasks/tools/python/ci.go @@ -53,7 +53,7 @@ func NewTasksForCI(repo taskutil.Repo) []taskutil.Tasker { // InfoText implements [taskutil.Tasker.InfoText]. func (t buildTask) InfoText() string { return "Build" } -// Run implements [taskutil.Tasker.Run]. +// Exec implements [taskutil.Tasker.Exec]. func (t buildTask) Exec(ctx context.Context) error { if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err @@ -68,7 +68,7 @@ func (t buildTask) Post(_ context.Context) error { return nil } // InfoText implements [taskutil.Tasker.InfoText]. func (t ruffLint) InfoText() string { return "Lint (ruff)" } -// Run implements [taskutil.Tasker.Run]. +// Exec implements [taskutil.Tasker.Exec]. func (t ruffLint) Exec(ctx context.Context) error { if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err @@ -83,7 +83,7 @@ func (t ruffLint) Post(_ context.Context) error { return nil } // InfoText implements [taskutil.Tasker.InfoText]. func (t ruffFormat) InfoText() string { return "Format (ruff)" } -// Run implements [taskutil.Tasker.Run]. +// Exec implements [taskutil.Tasker.Exec]. func (t ruffFormat) Exec(ctx context.Context) error { if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err @@ -98,7 +98,7 @@ func (t ruffFormat) Post(_ context.Context) error { return nil } // InfoText implements [taskutil.Tasker.InfoText]. func (t pydoclint) InfoText() string { return "Lint (pydoclint)" } -// Run implements [taskutil.Tasker.Run]. +// Exec implements [taskutil.Tasker.Exec]. func (t pydoclint) Exec(ctx context.Context) error { if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err @@ -113,7 +113,7 @@ func (t pydoclint) Post(_ context.Context) error { return nil } // InfoText implements [taskutil.Tasker.InfoText]. func (t mypy) InfoText() string { return "Type-check (mypy)" } -// Run implements [taskutil.Tasker.Run]. +// Exec implements [taskutil.Tasker.Exec]. func (t mypy) Exec(ctx context.Context) error { 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 622ca06..181758e 100644 --- a/internal/tasks/tools/shell/ci.go +++ b/internal/tasks/tools/shell/ci.go @@ -43,7 +43,7 @@ func NewTasksForCI(repo taskutil.Repo) []taskutil.Tasker { // InfoText implements [taskutil.Tasker.InfoText]. func (t shellcheck) InfoText() string { return "Lint (shellcheck)" } -// Run implements [taskutil.Tasker.Run]. +// Exec implements [taskutil.Tasker.Exec]. func (t shellcheck) Exec(ctx context.Context) error { if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err @@ -58,7 +58,7 @@ func (t shellcheck) Post(_ context.Context) error { return nil } // InfoText implements [taskutil.Tasker.InfoText]. func (t shfmt) InfoText() string { return "Format (shfmt)" } -// Run implements [taskutil.Tasker.Run]. +// Exec implements [taskutil.Tasker.Exec]. func (t shfmt) Exec(ctx context.Context) error { if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { return err diff --git a/internal/tasks/tools/yaml/ci.go b/internal/tasks/tools/yaml/ci.go index ece7027..15579a6 100644 --- a/internal/tasks/tools/yaml/ci.go +++ b/internal/tasks/tools/yaml/ci.go @@ -51,7 +51,7 @@ func NewTasksForCI(repo taskutil.Repo) []taskutil.Tasker { // InfoText implements [taskutil.Tasker.InfoText]. func (t yamllint) InfoText() string { return "Lint (yamllint)" } -// Run implements [taskutil.Tasker.Run]. +// Exec implements [taskutil.Tasker.Exec]. func (t yamllint) Exec(ctx context.Context) error { if err := toolcfg.SetupConfigFile(t.Tool); err != nil { return err @@ -70,7 +70,7 @@ func (t yamllint) Post(_ context.Context) error { return nil } // InfoText implements [taskutil.Tasker.InfoText]. func (t yamlfmt) InfoText() string { return "Format (yamlfmt)" } -// Run implements [taskutil.Tasker.Run]. +// Exec implements [taskutil.Tasker.Exec]. func (t yamlfmt) Exec(ctx context.Context) error { if err := toolcfg.SetupConfigFile(t.Tool); err != nil { return err diff --git a/mise.toml b/mise.toml index b0cde2e..a6c267b 100644 --- a/mise.toml +++ b/mise.toml @@ -5,7 +5,7 @@ jobs = 4 [tools] buf = "1.57.2" github-cli = "2.79.0" -go = "1.25.3" +go = "1.25.5" hadolint = "2.13.1" markdownlint-cli2 = "0.18.1" # required for markdownlint-cli2 diff --git a/oscar.yaml b/oscar.yaml index ff5bc64..6c73d42 100644 --- a/oscar.yaml +++ b/oscar.yaml @@ -1,5 +1,5 @@ --- -version: "0.3.0-alpha2" +version: "0.3.0-alpha3" deliverables: go_github_release: build_sources: From 42528774f991d6be718ab81123056a7aa440e2ca Mon Sep 17 00:00:00 2001 From: "Ryan J. Price" Date: Mon, 22 Dec 2025 15:58:35 -0600 Subject: [PATCH 03/10] Finish configuring linters and get CI to pass locally --- Containerfile | 1 + go.mod | 2 +- go.sum | 4 +- internal/cli/root.go | 11 +- internal/git/git.go | 68 ++-- internal/hostinfo/hostinfo.go | 59 ++-- internal/oscarcfg/config.go | 16 +- internal/system/system.go | 64 ++-- internal/tasks/ci/run.go | 38 +- internal/tasks/delivery/run.go | 8 +- internal/tasks/tools/containers/ci.go | 4 +- internal/tasks/tools/containers/deliver.go | 71 ++-- internal/tasks/tools/gittag/deliver.go | 8 +- internal/tasks/tools/go/ci.go | 27 +- internal/tasks/tools/go/deliver.go | 16 +- internal/tasks/tools/markdown/ci.go | 9 +- internal/tasks/tools/python/ci.go | 27 +- internal/tasks/tools/shell/ci.go | 5 +- internal/tasks/tools/toolcfg/.golangci.yaml | 331 ++++++++++++------ .../tools/toolcfg/.markdownlint-cli2.yaml | 3 + internal/tasks/tools/toolcfg/pyproject.toml | 13 +- internal/tasks/tools/toolcfg/util.go | 8 +- internal/tasks/tools/version/ci.go | 36 +- internal/tasks/tools/yaml/ci.go | 8 +- internal/tasks/util/core_types.go | 48 +-- internal/tasks/util/repo.go | 80 ++--- internal/tasks/util/run.go | 8 +- mise.toml | 3 +- proto/generate.go | 2 +- 29 files changed, 578 insertions(+), 400 deletions(-) diff --git a/Containerfile b/Containerfile index 7eb599d..a32ad03 100644 --- a/Containerfile +++ b/Containerfile @@ -14,6 +14,7 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ bash \ ca-certificates \ make \ + upx \ && \ rm -rf /var/lib/apt* diff --git a/go.mod b/go.mod index 5091281..b381d04 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.9-20250912141014-52f32327d4b0.1 buf.build/go/protovalidate v1.0.0 github.com/stretchr/testify v1.11.1 - go.yaml.in/yaml/v4 v4.0.0-rc.2 + go.yaml.in/yaml/v4 v4.0.0-rc.3 golang.org/x/term v0.35.0 google.golang.org/protobuf v1.36.9 ) diff --git a/go.sum b/go.sum index e92c682..2779089 100644 --- a/go.sum +++ b/go.sum @@ -31,8 +31,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/urfave/cli/v3 v3.4.1 h1:1M9UOCy5bLmGnuu1yn3t3CB4rG79Rtoxuv1sPhnm6qM= github.com/urfave/cli/v3 v3.4.1/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo= -go.yaml.in/yaml/v4 v4.0.0-rc.2 h1:/FrI8D64VSr4HtGIlUtlFMGsm7H7pWTbj6vOLVZcA6s= -go.yaml.in/yaml/v4 v4.0.0-rc.2/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= +go.yaml.in/yaml/v4 v4.0.0-rc.3 h1:3h1fjsh1CTAPjW7q/EMe+C8shx5d8ctzZTrLcs/j8Go= +go.yaml.in/yaml/v4 v4.0.0-rc.3/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= 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= diff --git a/internal/cli/root.go b/internal/cli/root.go index 147aebb..f32cc06 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -57,8 +57,9 @@ func NewRootCmd() *cli.Command { }, }, &cli.BoolFlag{ - Name: noColorFlagName, - Usage: "Pass to suppress printing colored terminal output. Note that oscar defaults to printing in color during interactive runs.", + 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") @@ -95,7 +96,11 @@ func getVersion() (string, error) { // rootAction defines the logic for oscar's root command. func rootAction(_ context.Context, cmd *cli.Command) error { iprint.Debugf("oscar root command\n") - _ = cli.ShowAppHelp(cmd) + + if err := cli.ShowAppHelp(cmd); err != nil { + return fmt.Errorf("internal error trying to show command help: %w", err) + } + return errors.New("\nERROR: oscar requires a valid subcommand") } diff --git a/internal/git/git.go b/internal/git/git.go index dc854da..4f90ae4 100644 --- a/internal/git/git.go +++ b/internal/git/git.go @@ -11,52 +11,58 @@ import ( "github.com/opensourcecorp/oscar/internal/system" ) -// Git holds metadata about the current state of the Git repository. -type Git struct { - // The root directory of the repository on the host. - Root string - // The current branch name. - Branch string - // The latest tag available in the repo. - LatestTag string - // The latest commit on the current branch. - LatestCommit string - // Whether or not the working directory has uncommitted changes. - IsDirty bool -} +type ( + // Git holds metadata about the current state of the Git repository. + Git struct { + // The root directory of the repository on the host. + Root string + // The current branch name. + Branch string + // The latest tag available in the repo. + LatestTag string + // The latest commit on the current branch. + LatestCommit string + // Whether or not the working directory has uncommitted changes. + IsDirty bool + } -// Status holds various pieces of information about Git status. -type Status struct { - // The list of modified files. - Diff []string - // The list of untracked files. - UntrackedFiles []string -} + // Status holds various pieces of information about Git status. + Status struct { + // The list of modified files. + Diff []string + // The list of untracked files. + UntrackedFiles []string + } +) // New returns a populated [Git]. func New(ctx context.Context) (*Git, error) { root, err := system.RunCommand(ctx, []string{"git", "rev-parse", "--show-toplevel"}) if err != nil { - return nil, err + return nil, fmt.Errorf("getting Git root: %w", err) } + iprint.Debugf("Git root on host: '%s'\n", root) branch, err := system.RunCommand(ctx, []string{"git", "rev-parse", "--abbrev-ref", "HEAD"}) if err != nil { - return nil, err + return nil, fmt.Errorf("getting Git branch name: %w", err) } + iprint.Debugf("Git branch: '%s'\n", branch) latestTag, err := system.RunCommand(ctx, []string{"bash", "-c", "git tag --list | tail -n1"}) if err != nil { - return nil, err + return nil, fmt.Errorf("getting latest Git tag: %w", err) } + iprint.Debugf("latest Git tag: '%s'\n", latestTag) latestCommit, err := system.RunCommand(ctx, []string{"git", "rev-parse", "--short=8", "HEAD"}) if err != nil { - return nil, err + return nil, fmt.Errorf("getting latest Git commit: %w", err) } + iprint.Debugf("latest Git commit: '%s'\n", latestCommit) gitStatus, err := getRawStatus(ctx) @@ -89,19 +95,21 @@ func (g *Git) SanitizedBranch() string { // String implements [fmt.Stringer]. func (g *Git) String() string { - out := "Current Git information:\n" + var out strings.Builder + out.WriteString("Current Git information:\n") + + t := reflect.TypeFor[Git]() - t := reflect.TypeOf(*g) v := reflect.ValueOf(*g) for i := range v.NumField() { field := t.Field(i) value := v.Field(i) - out += fmt.Sprintf("- %s: %v\n", field.Name, value) + out.WriteString(fmt.Sprintf("- %s: %v\n", field.Name, value)) } - out += "\n" + out.WriteString("\n") - return out + return out.String() } // getRawStatus returns a slightly-modified "git status" output, so that calling tools can parse it @@ -117,10 +125,12 @@ func getRawStatus(ctx context.Context) (Status, error) { untrackedFiles := make([]string, 0) diff := make([]string, 0) + for _, line := range outputSplit { if line == "" { continue } + if strings.HasPrefix(line, "??") { filename := strings.ReplaceAll(line, "?? ", "") untrackedFiles = append(untrackedFiles, filename) diff --git a/internal/hostinfo/hostinfo.go b/internal/hostinfo/hostinfo.go index 56c9d8d..33e8311 100644 --- a/internal/hostinfo/hostinfo.go +++ b/internal/hostinfo/hostinfo.go @@ -5,36 +5,37 @@ import ( "runtime" ) -/* -Input allows different tools to specify the different possible values for their host-related -information. This is primarily used when downloading a release artifact. This is necessary to -provide because different tools use different values for their host info. For example: - - - Some tools use "x86_64" for CPU architecture, while some use "amd64", and others even use "x64" - - In addition to a kernel ID, some tools additionally specify an OS value (e.g. both "darwin" and - "macos") -*/ -type Input struct { - // The name a tool uses for Linux OSes (e.g. "linux", "unknown", etc.) - OSLinux string - // The name a tool uses for the Linux kernel (e.g. "linux", "linux-gnu", etc.) - KernelLinux string - // The name a tool uses for macOS (e.g. "apple") - OSMacOS string - // The name a tool uses for the macOS kernel (e.g. "darwin") - KernelMacOS string - // The name a tool uses for AMD64 architectures (e.g. "x86_64", "amd64", etc.) - ArchAMD64 string - // The name a tool uses for ARM64 architectures (e.g. "aarch64", "arm64", etc.) - ArchARM64 string -} +type ( + // Input allows different tools to specify the different possible values for their host-related + // information. This is primarily used when downloading a release artifact. This is necessary to + // provide because different tools use different values for their host info. For example: + // + // - Some tools use "x86_64" for CPU architecture, while some use "amd64", and others even use + // "x64" + // - In addition to a kernel ID, some tools additionally specify an OS value (e.g. both + // "darwin" and "macos") + Input struct { + // The name a tool uses for Linux OSes (e.g. "linux", "unknown", etc.) + OSLinux string + // The name a tool uses for the Linux kernel (e.g. "linux", "linux-gnu", etc.) + KernelLinux string + // The name a tool uses for macOS (e.g. "apple") + OSMacOS string + // The name a tool uses for the macOS kernel (e.g. "darwin") + KernelMacOS string + // The name a tool uses for AMD64 architectures (e.g. "x86_64", "amd64", etc.) + ArchAMD64 string + // The name a tool uses for ARM64 architectures (e.g. "aarch64", "arm64", etc.) + ArchARM64 string + } -// HostInfo holds the determined host information values. -type HostInfo struct { - OS string - Arch string - Kernel string -} + // HostInfo holds the determined host information values. + HostInfo struct { + OS string + Arch string + Kernel string + } +) // Get returns a populated [HostInfo], based on the provided [Input] mappings. func Get(i Input) (HostInfo, error) { diff --git a/internal/oscarcfg/config.go b/internal/oscarcfg/config.go index 1b9e5aa..fc7946d 100644 --- a/internal/oscarcfg/config.go +++ b/internal/oscarcfg/config.go @@ -25,28 +25,32 @@ func Get(pathOverride ...string) (*oscarcfgpbv1.Config, error) { path = pathOverride[0] } - yamlData, err := os.ReadFile(path) - if err != nil { - return nil, fmt.Errorf("reading oscar config file: %w", err) + yamlData, readErr := os.ReadFile(path) + if readErr != nil { + return nil, fmt.Errorf("reading oscar config file: %w", readErr) } + iprint.Debugf("data read from oscar config file:\n%s\n", string(yamlData)) jsonSweepMap := make(map[string]any) if err := yaml.Unmarshal(yamlData, jsonSweepMap); err != nil { panic(err) } + iprint.Debugf("YAML data unmarshalled to map: %+v\n", jsonSweepMap) - jsonData, err := json.Marshal(jsonSweepMap) - if err != nil { - panic(err) + jsonData, mErr := json.Marshal(jsonSweepMap) + if mErr != nil { + panic(mErr) } + iprint.Debugf("map data as JSON string: %s\n", string(jsonData)) cfg := &oscarcfgpbv1.Config{} if err := protojson.Unmarshal(jsonData, cfg); err != nil { return nil, fmt.Errorf("unmarshalling oscar config file '%s': %w", path, err) } + iprint.Debugf("proto message: %+v\n", cfg) if err := protovalidate.Validate(cfg); err != nil { diff --git a/internal/system/system.go b/internal/system/system.go index 3a8252a..df73fe4 100644 --- a/internal/system/system.go +++ b/internal/system/system.go @@ -43,7 +43,7 @@ func Init(ctx context.Context) error { } for _, d := range []string{consts.OscarHome, consts.OscarHomeBin} { - if err := os.MkdirAll(d, 0o755); err != nil { + if err := os.MkdirAll(d, 0700); err != nil { return fmt.Errorf( "internal error when creating oscar directory '%s': %w", d, err, @@ -64,12 +64,12 @@ func Init(ctx context.Context) error { return fmt.Errorf("installing mise: %w", err) } - cfgFileContents, err := oscar.Files.ReadFile("mise.toml") - if err != nil { - return fmt.Errorf("reading embedded file contents: %w", err) + cfgFileContents, readErr := oscar.Files.ReadFile("mise.toml") + if readErr != nil { + return fmt.Errorf("reading embedded file contents: %w", readErr) } - if err := os.WriteFile(consts.MiseConfigFileName, cfgFileContents, 0644); err != nil { + if err := os.WriteFile(consts.MiseConfigFileName, cfgFileContents, 0600); err != nil { return fmt.Errorf("writing config file: %w", err) } @@ -123,7 +123,7 @@ func RunCommand(ctx context.Context, cmdArgs []string) (string, error) { } // 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 +// 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 // files like `.gitignore` without any extra configuration. // @@ -143,13 +143,15 @@ func GetFileTypeListerCommand(fileType string) string { // installMise installs [mise] into [consts.OscarHomeBin], if not found there. // // [mise]: https://mise.jdx.dev -func installMise(_ context.Context) (err error) { +func installMise(ctx context.Context) (outErr error) { miseFound := true - if _, statErr := os.Stat(consts.MiseBinPath); statErr != nil { + if _, err := os.Stat(consts.MiseBinPath); err != nil { iprint.Debugf("error when running os.Stat(consts.MiseBinPath): %w\n", err) - if os.IsNotExist(statErr) { + + if os.IsNotExist(err) { miseFound = false + iprint.Debugf("mise not found, will install\n") } else { return fmt.Errorf("internal error checking if mise is installed: %w", err) @@ -159,7 +161,8 @@ func installMise(_ context.Context) (err error) { if miseFound { // TODO: mise version check iprint.Debugf("mise found, nothing to do\n") - return + + return outErr } miseVersion := os.Getenv("MISE_VERSION") @@ -174,9 +177,9 @@ func installMise(_ context.Context) (err error) { ArchARM64: "arm64", } - host, err := hostinfo.Get(hostInput) - if err != nil { - return fmt.Errorf("getting host info during mise install: %w", err) + host, hostErr := hostinfo.Get(hostInput) + if hostErr != nil { + return fmt.Errorf("getting host info during mise install: %w", hostErr) } miseReleaseURL := fmt.Sprintf( @@ -184,26 +187,32 @@ func installMise(_ context.Context) (err error) { miseVersion, miseVersion, host.Kernel, host.Arch, ) - out, err := os.Create(consts.MiseBinPath) - if err != nil { - return fmt.Errorf("creating mise target file: %w", err) + out, createErr := os.Create(consts.MiseBinPath) + if createErr != nil { + return fmt.Errorf("creating mise target file: %w", createErr) } defer func() { - if closeErr := out.Close(); closeErr != nil { - err = errors.Join(err, fmt.Errorf("closing mise target file: %w", closeErr)) + if err := out.Close(); err != nil { + outErr = errors.Join(outErr, fmt.Errorf("closing mise target file: %w", err)) } }() - // TODO: use a context func instead - resp, err := http.Get(miseReleaseURL) - if err != nil { - return fmt.Errorf("making GET request for mise GitHub Release: %w", err) + req, reqErr := http.NewRequestWithContext(ctx, http.MethodGet, miseReleaseURL, nil) + if reqErr != nil { + return fmt.Errorf("setting up GET request for mise GitHub Release: %w", reqErr) + } + + client := http.DefaultClient + + resp, getErr := client.Do(req) + if getErr != nil { + return fmt.Errorf("making GET request for mise GitHub Release: %w", getErr) } defer func() { - if closeErr := resp.Body.Close(); closeErr != nil { - err = errors.Join(err, fmt.Errorf("closing response body: %w", closeErr)) + if err := resp.Body.Close(); err != nil { + outErr = errors.Join(outErr, fmt.Errorf("closing response body: %w", err)) } }() @@ -211,15 +220,15 @@ func installMise(_ context.Context) (err error) { return fmt.Errorf("bad HTTP status code when getting mise: %s", resp.Status) } - if _, err = io.Copy(out, resp.Body); err != nil { + if _, err := io.Copy(out, resp.Body); err != nil { return fmt.Errorf("writing mise data to target: %w", err) } - if err := os.Chmod(consts.MiseBinPath, 0o755); err != nil { + if err := os.Chmod(consts.MiseBinPath, 0700); err != nil { return fmt.Errorf("changing mise binary to be executable: %w", err) } - return err + return outErr } // FilesExistInTree performs file discovery by allowing various tools to check if they need to run @@ -230,6 +239,7 @@ func FilesExistInTree(ctx context.Context, findScript string) (bool, error) { %s`, findScript, )) + output, err := cmd.CombinedOutput() if err != nil { // If no files found, that's fine, just report it diff --git a/internal/tasks/ci/run.go b/internal/tasks/ci/run.go index 8d679c2..1e20027 100644 --- a/internal/tasks/ci/run.go +++ b/internal/tasks/ci/run.go @@ -46,28 +46,28 @@ func getCITaskMap(repo taskutil.Repo) (taskutil.TaskMap, error) { } // Run defines the behavior for running all CI tasks for the repository. -func Run(ctx context.Context) (err error) { +func Run(ctx context.Context) (outErr error) { // The mise config that oscar uses is written during init, so be sure to defer its removal here defer func() { if rmErr := os.RemoveAll(consts.MiseConfigFileName); rmErr != nil { - err = errors.Join(err, fmt.Errorf("removing mise config file: %w", rmErr)) + outErr = errors.Join(outErr, fmt.Errorf("removing mise config file: %w", rmErr)) } }() - run, err := taskutil.NewRun(ctx, "CI") - if err != nil { - return fmt.Errorf("internal error setting up run info: %w", err) + run, newRunErr := taskutil.NewRun(ctx, "CI") + if newRunErr != nil { + return fmt.Errorf("internal error setting up run info: %w", newRunErr) } - taskMap, err := getCITaskMap(run.Repo) - if err != nil { - return err + taskMap, getTMErr := getCITaskMap(run.Repo) + if getTMErr != nil { + return getTMErr } // For tracking any changes to Git status etc. after each CI Task runs - gitCI, err := igit.NewForCI(ctx) - if err != nil { - return fmt.Errorf("internal error: %w", err) + gitCI, gitErr := igit.NewForCI(ctx) + if gitErr != nil { + return fmt.Errorf("internal error: %w", gitErr) } for _, lang := range taskMap.SortedKeys() { @@ -91,9 +91,9 @@ func Run(ctx context.Context) (err error) { return fmt.Errorf("internal error: %w", err) } - gitStatusHasChanged, err := gitCI.StatusHasChanged(ctx) - if err != nil { - return fmt.Errorf("internal error: %w", err) + gitStatusHasChanged, statusErr := gitCI.StatusHasChanged(ctx) + if statusErr != nil { + return fmt.Errorf("internal error: %w", statusErr) } if runErr != nil || gitStatusHasChanged { @@ -113,9 +113,9 @@ func Run(ctx context.Context) (err error) { run.Failures = append(run.Failures, fmt.Sprintf("%s :: %s", lang, task.InfoText())) // Also need to reset the baseline status - gitCI, err = igit.NewForCI(ctx) - if err != nil { - return fmt.Errorf("internal error: %w", err) + gitCI, gitErr = igit.NewForCI(ctx) + if gitErr != nil { + return fmt.Errorf("internal error: %w", gitErr) } } else { iprint.Goodf("PASSED (%s)\n", iprint.RunDurationString(taskStartTime)) @@ -124,10 +124,10 @@ func Run(ctx context.Context) (err error) { } if len(run.Failures) > 0 { - return run.ReportFailure(err) + return fmt.Errorf("running CI: %w", run.ReportFailure(outErr)) } run.ReportSuccess() - return err + return outErr } diff --git a/internal/tasks/delivery/run.go b/internal/tasks/delivery/run.go index 410f944..2fd1b15 100644 --- a/internal/tasks/delivery/run.go +++ b/internal/tasks/delivery/run.go @@ -46,7 +46,7 @@ func getDeliveryTaskMap(repo taskutil.Repo) (taskutil.TaskMap, error) { } // Run defines the behavior for running all Delivery tasks for the repository. -func Run(ctx context.Context) (err error) { +func Run(ctx context.Context) (outErr error) { // We intentionally run CI tasks before allowing any Delivery tasks to begin if err := ci.Run(ctx); err != nil { return fmt.Errorf("running CI tasks before Delivery tasks: %w", err) @@ -55,7 +55,7 @@ func Run(ctx context.Context) (err error) { // The mise config that oscar uses is written during init, so be sure to defer its removal here defer func() { if rmErr := os.RemoveAll(consts.MiseConfigFileName); rmErr != nil { - err = errors.Join(err, fmt.Errorf("removing mise config file: %w", rmErr)) + outErr = errors.Join(outErr, fmt.Errorf("removing mise config file: %w", rmErr)) } }() @@ -98,10 +98,10 @@ func Run(ctx context.Context) (err error) { } if len(run.Failures) > 0 { - return run.ReportFailure(err) + return fmt.Errorf("running delivery tasks: %w", run.ReportFailure(err)) } run.ReportSuccess() - return err + return outErr } diff --git a/internal/tasks/tools/containers/ci.go b/internal/tasks/tools/containers/ci.go index 9240767..cd2155c 100644 --- a/internal/tasks/tools/containers/ci.go +++ b/internal/tasks/tools/containers/ci.go @@ -42,11 +42,11 @@ func (t hadolint) InfoText() string { return "Lint (hadolint)" } // Exec implements [taskutil.Tasker.Exec]. func (t hadolint) Exec(ctx context.Context) error { if err := toolcfg.SetupConfigFile(t.Tool); err != nil { - return err + return fmt.Errorf("setting up hadolint config file: %w", err) } if _, err := system.RunCommand(ctx, t.RenderRunCommandArgs()); err != nil { - return err + return fmt.Errorf("running hadolint: %w", err) } return nil diff --git a/internal/tasks/tools/containers/deliver.go b/internal/tasks/tools/containers/deliver.go index bc2541f..9acedc5 100644 --- a/internal/tasks/tools/containers/deliver.go +++ b/internal/tasks/tools/containers/deliver.go @@ -18,18 +18,18 @@ import ( type ( imageBuildPush struct{ taskutil.Tool } -) -// registryMapping contains substructs to be used based on the target OCI registry. -type registryMapping struct { - GitHub gitHubRegistry -} + // RegistryMapping contains substructs to be used based on the target OCI registry. + registryMapping struct { + GitHub gitHubRegistry + } -// gitHubRegistry provides fields for use in targeting "ghcr.io". -type gitHubRegistry struct { - // The command to run to authenticate to the registry. - AuthCommand []string -} + // GitHubRegistry provides fields for use in targeting "ghcr.io". + gitHubRegistry struct { + // The command to run to authenticate to the registry. + AuthCommand []string + } +) // newRegistryMap returns a populated [registryMapping]. func newRegistryMap(username string) registryMapping { @@ -47,7 +47,7 @@ func newRegistryMap(username string) registryMapping { func NewTasksForDelivery(repo taskutil.Repo) ([]taskutil.Tasker, error) { cfg, err := oscarcfg.Get() if err != nil { - return nil, err + return nil, fmt.Errorf("getting oscarcfg: %w", err) } if repo.HasContainerfile { @@ -68,54 +68,57 @@ func (t imageBuildPush) InfoText() string { return "Image Build & Push" } // Exec implements [taskutil.Tasker.Exec]. func (t imageBuildPush) Exec(ctx context.Context) error { - rootCfg, err := oscarcfg.Get() - if err != nil { - return err + rootCfg, cfgErr := oscarcfg.Get() + if cfgErr != nil { + return fmt.Errorf("getting oscarcfg: %w", cfgErr) } cfg := rootCfg.GetDeliverables().GetContainerImage() - uri, err := constructImageURI(ctx, rootCfg) - if err != nil { - return fmt.Errorf("constructing image URI: %w", err) + uri, uriErr := constructImageURI(ctx, rootCfg) + if uriErr != nil { + return fmt.Errorf("constructing image URI: %w", uriErr) } - composeFileContents, err := os.ReadFile("docker-compose.yaml") - if err != nil { - return err + // TODO: replace all of the below with the actual Compose API usage, e.g. as found in the README + // example here: https://github.com/compose-spec/compose-go + + composeFileContents, readErr := os.ReadFile("docker-compose.yaml") + if readErr != nil { + return fmt.Errorf("reading docker-compose.yaml file: %w", readErr) } composeFile := make(map[string]any) if err := yaml.Unmarshal(composeFileContents, composeFile); err != nil { - return err + return fmt.Errorf("unmarshalling YAML: %w", err) } iprint.Debugf("composeFile unmarshalled: %#v\n", composeFile) - curDir, err := os.Getwd() - if err != nil { - return err + curDir, wdErr := os.Getwd() + if wdErr != nil { + return fmt.Errorf("getting workdir: %w", wdErr) } - // GROSS, DUDE + // TODO: GROSS, DUDE. See comment above about using the actual Compose API. composeFile["services"].(map[string]any)[cfg.GetName()].(map[string]any)["image"] = uri composeFile["services"].(map[string]any)[cfg.GetName()].(map[string]any)["build"].(map[string]any)["context"] = curDir - composeOut, err := yaml.Marshal(composeFile) - if err != nil { - return err + composeOut, mErr := yaml.Marshal(composeFile) + if mErr != nil { + return fmt.Errorf("marshalling YAML: %w", mErr) } iprint.Debugf("edited Compose file YAML: %s\n", string(composeOut)) workDir := filepath.Join(os.TempDir(), "oscar-oci") - if err := os.MkdirAll(workDir, 0755); err != nil { - return err + if err := os.MkdirAll(workDir, 0700); err != nil { + return fmt.Errorf("making Compose directory: %w", err) } outPath := filepath.Join(workDir, "docker-compose.yaml") - if err := os.WriteFile(outPath, composeOut, 0644); err != nil { - return err + if err := os.WriteFile(outPath, composeOut, 0600); err != nil { + return fmt.Errorf("writing Compose file contents: %w", err) } registryMap := newRegistryMap(cfg.GetName()) @@ -126,7 +129,7 @@ func (t imageBuildPush) Exec(ctx context.Context) error { } if _, err := system.RunCommand(ctx, authArgs); err != nil { - return err + return fmt.Errorf("running registry auth command: %w", err) } buildPushArgs := []string{"bash", "-c", fmt.Sprintf(` @@ -134,7 +137,7 @@ func (t imageBuildPush) Exec(ctx context.Context) error { `, outPath, cfg.GetName(), )} if _, err := system.RunCommand(ctx, buildPushArgs); err != nil { - return err + return fmt.Errorf("running image build & push command: %w", err) } return nil diff --git a/internal/tasks/tools/gittag/deliver.go b/internal/tasks/tools/gittag/deliver.go index cb4f453..22278f3 100644 --- a/internal/tasks/tools/gittag/deliver.go +++ b/internal/tasks/tools/gittag/deliver.go @@ -27,9 +27,9 @@ func (t createAndPushTag) InfoText() string { return "Create & Push Git Tag" } // Exec implements [taskutil.Tasker.Exec]. func (t createAndPushTag) Exec(ctx context.Context) error { - cfg, err := oscarcfg.Get() - if err != nil { - return err + cfg, cfgErr := oscarcfg.Get() + if cfgErr != nil { + return fmt.Errorf("getting oscarcfg: %w", cfgErr) } args := []string{"bash", "-c", fmt.Sprintf(` @@ -39,7 +39,7 @@ func (t createAndPushTag) Exec(ctx context.Context) error { )} if _, err := system.RunCommand(ctx, args); err != nil { - return err + return fmt.Errorf("creating/pushing Git tag: %w", err) } return nil diff --git a/internal/tasks/tools/go/ci.go b/internal/tasks/tools/go/ci.go index 67a5b22..df14683 100644 --- a/internal/tasks/tools/go/ci.go +++ b/internal/tasks/tools/go/ci.go @@ -78,7 +78,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 := system.RunCommand(ctx, t.RunArgs); err != nil { - return err + return fmt.Errorf("running go mod check: %w", err) } return nil @@ -93,7 +93,7 @@ func (t generateCodeCI) InfoText() string { return "Generate code" } // Exec implements [taskutil.Tasker.Exec]. func (t generateCodeCI) Exec(ctx context.Context) error { if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { - return err + return fmt.Errorf("running codegen: %w", err) } // Generating code will likely throw diffs if not also addressing other formatting CI checks, so @@ -120,7 +120,7 @@ func (t goBuildCI) InfoText() string { return "Build" } // Exec implements [taskutil.Tasker.Exec]. func (t goBuildCI) Exec(ctx context.Context) error { if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { - return err + return fmt.Errorf("running go build: %w", err) } return nil @@ -134,13 +134,12 @@ func (t golangciFmt) InfoText() string { return "Format (golangci-lint)" } // Exec implements [taskutil.Tasker.Exec]. func (t golangciFmt) Exec(ctx context.Context) error { - err := toolcfg.SetupConfigFile(t.Tool) - if err != nil { - return err + if err := toolcfg.SetupConfigFile(t.Tool); err != nil { + return fmt.Errorf("setting up golangci-lint config file: %w", err) } if _, err := system.RunCommand(ctx, t.RenderRunCommandArgs()); err != nil { - return err + return fmt.Errorf("running golangci-lint fmt: %w", err) } return nil @@ -148,8 +147,7 @@ func (t golangciFmt) Exec(ctx context.Context) error { // Post implements [taskutil.Tasker.Post]. func (t golangciFmt) Post(_ context.Context) error { - err := os.RemoveAll(t.ConfigFilePath) - if err != nil { + if err := os.RemoveAll(t.ConfigFilePath); err != nil { return fmt.Errorf("removing config file: %w", err) } @@ -161,13 +159,12 @@ func (t golangciLint) InfoText() string { return "Lint (golangci-lint)" } // Exec implements [taskutil.Tasker.Exec]. func (t golangciLint) Exec(ctx context.Context) error { - err := toolcfg.SetupConfigFile(t.Tool) - if err != nil { - return err + if err := toolcfg.SetupConfigFile(t.Tool); err != nil { + return fmt.Errorf("setting up golangci-lint config file: %w", err) } if _, err := system.RunCommand(ctx, t.RenderRunCommandArgs()); err != nil { - return err + return fmt.Errorf("running golangci-lint run: %w", err) } return nil @@ -189,7 +186,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 := system.RunCommand(ctx, t.RunArgs); err != nil { - return err + return fmt.Errorf("running govulncheck: %w", err) } return nil @@ -204,7 +201,7 @@ func (t goTest) InfoText() string { return "Tests" } // Exec implements [taskutil.Tasker.Exec]. func (t goTest) Exec(ctx context.Context) error { if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { - return err + return fmt.Errorf("running go test: %w", err) } return nil diff --git a/internal/tasks/tools/go/deliver.go b/internal/tasks/tools/go/deliver.go index 177ace7..cd87742 100644 --- a/internal/tasks/tools/go/deliver.go +++ b/internal/tasks/tools/go/deliver.go @@ -23,7 +23,7 @@ type ( func NewTasksForDelivery(repo taskutil.Repo) ([]taskutil.Tasker, error) { cfg, err := oscarcfg.Get() if err != nil { - return nil, err + return nil, fmt.Errorf("getting oscarcfg: %w", err) } if repo.HasGo { @@ -44,9 +44,9 @@ func (t ghRelease) InfoText() string { return "GitHub Release" } // Exec implements [taskutil.Tasker.Exec]. func (t ghRelease) Exec(ctx context.Context) error { - cfg, err := oscarcfg.Get() - if err != nil { - return err + cfg, cfgErr := oscarcfg.Get() + if cfgErr != nil { + return fmt.Errorf("getting oscarcfg: %w", cfgErr) } var buildErrs error @@ -65,7 +65,7 @@ func (t ghRelease) Exec(ctx context.Context) error { return fmt.Errorf("removing dist directory: %w", err) } - if err := os.MkdirAll(distDir, 0755); err != nil { + if err := os.MkdirAll(distDir, 0700); err != nil { return fmt.Errorf("creating dist directory: %w", err) } @@ -90,7 +90,7 @@ func (t ghRelease) Exec(ctx context.Context) error { )} if _, err := system.RunCommand(ctx, args); err != nil { - return err + return fmt.Errorf("running GitHub Release command: %w", err) } return nil @@ -115,7 +115,7 @@ func goBuild(ctx context.Context, src string) error { return fmt.Errorf("removing build directory: %w", err) } - if err := os.MkdirAll(targetDir, 0755); err != nil { + if err := os.MkdirAll(targetDir, 0700); err != nil { return fmt.Errorf("creating build directory: %w", err) } @@ -154,7 +154,7 @@ func goBuild(ctx context.Context, src string) error { return fmt.Errorf("building Go binary: %w", err) } - if err := os.Chmod(target, 0755); err != nil { + if err := os.Chmod(target, 0700); err != nil { return fmt.Errorf("marking target as executable: %w", err) } } diff --git a/internal/tasks/tools/markdown/ci.go b/internal/tasks/tools/markdown/ci.go index 2a70c98..83582d9 100644 --- a/internal/tasks/tools/markdown/ci.go +++ b/internal/tasks/tools/markdown/ci.go @@ -2,6 +2,7 @@ package mdtools import ( "context" + "fmt" "os" "path/filepath" @@ -24,6 +25,10 @@ func NewTasksForCI(repo taskutil.Repo) []taskutil.Tasker { "markdownlint-cli2", "--config", "{{ConfigFilePath}}", "**/*.md", + // NOTE: unfortunately, getting markdownlint-cli2 to work with _its own + // config file_ is apparently more difficult than it needs to be, so any + // ignored paths need to be set directly in the CLI call here + "#**/*venv*/**/*.md", }, ConfigFilePath: filepath.Join(os.TempDir(), ".markdownlint-cli2.yaml"), }, @@ -40,11 +45,11 @@ func (t markdownlint) InfoText() string { return "Lint (markdownlint)" } // Exec implements [taskutil.Tasker.Exec]. func (t markdownlint) Exec(ctx context.Context) error { if err := toolcfg.SetupConfigFile(t.Tool); err != nil { - return err + return fmt.Errorf("setting up markdownlint config file: %w", err) } if _, err := system.RunCommand(ctx, t.RenderRunCommandArgs()); err != nil { - return err + return fmt.Errorf("running markdownlint: %w", err) } return nil diff --git a/internal/tasks/tools/python/ci.go b/internal/tasks/tools/python/ci.go index b590c99..1178ab1 100644 --- a/internal/tasks/tools/python/ci.go +++ b/internal/tasks/tools/python/ci.go @@ -2,6 +2,7 @@ package pytools import ( "context" + "fmt" "github.com/opensourcecorp/oscar/internal/system" taskutil "github.com/opensourcecorp/oscar/internal/tasks/util" @@ -12,7 +13,7 @@ type ( ruffLint struct{ taskutil.Tool } ruffFormat struct{ taskutil.Tool } pydoclint struct{ taskutil.Tool } - mypy struct{ taskutil.Tool } + ty struct{ taskutil.Tool } ) // NewTasksForCI returns the list of CI tasks. @@ -26,12 +27,12 @@ func NewTasksForCI(repo taskutil.Repo) []taskutil.Tasker { }, ruffLint{ Tool: taskutil.Tool{ - RunArgs: []string{"ruff", "check", "--fix", "./src"}, + RunArgs: []string{"uvx", "ruff", "check", "--fix", "./src"}, }, }, ruffFormat{ Tool: taskutil.Tool{ - RunArgs: []string{"ruff", "format", "./src"}, + RunArgs: []string{"uvx", "ruff", "format", "./src"}, }, }, pydoclint{ @@ -39,9 +40,9 @@ func NewTasksForCI(repo taskutil.Repo) []taskutil.Tasker { RunArgs: []string{"uvx", "pydoclint", "./src"}, }, }, - mypy{ + ty{ Tool: taskutil.Tool{ - RunArgs: []string{"uvx", "mypy", "./src"}, + RunArgs: []string{"uvx", "ty", "check"}, }, }, } @@ -56,7 +57,7 @@ func (t buildTask) InfoText() string { return "Build" } // Exec implements [taskutil.Tasker.Exec]. func (t buildTask) Exec(ctx context.Context) error { if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { - return err + return fmt.Errorf("running build: %w", err) } return nil @@ -71,7 +72,7 @@ func (t ruffLint) InfoText() string { return "Lint (ruff)" } // Exec implements [taskutil.Tasker.Exec]. func (t ruffLint) Exec(ctx context.Context) error { if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { - return err + return fmt.Errorf("running ruff linter: %w", err) } return nil @@ -86,7 +87,7 @@ func (t ruffFormat) InfoText() string { return "Format (ruff)" } // Exec implements [taskutil.Tasker.Exec]. func (t ruffFormat) Exec(ctx context.Context) error { if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { - return err + return fmt.Errorf("running ruff formatter: %w", err) } return nil @@ -101,7 +102,7 @@ func (t pydoclint) InfoText() string { return "Lint (pydoclint)" } // Exec implements [taskutil.Tasker.Exec]. func (t pydoclint) Exec(ctx context.Context) error { if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { - return err + return fmt.Errorf("running pydoclint: %w", err) } return nil @@ -111,16 +112,16 @@ func (t pydoclint) Exec(ctx context.Context) error { func (t pydoclint) Post(_ context.Context) error { return nil } // InfoText implements [taskutil.Tasker.InfoText]. -func (t mypy) InfoText() string { return "Type-check (mypy)" } +func (t ty) InfoText() string { return "Type-check (ty)" } // Exec implements [taskutil.Tasker.Exec]. -func (t mypy) Exec(ctx context.Context) error { +func (t ty) Exec(ctx context.Context) error { if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { - return err + return fmt.Errorf("running ty type checker: %w", err) } return nil } // Post implements [taskutil.Tasker.Post]. -func (t mypy) Post(_ context.Context) error { return nil } +func (t ty) Post(_ context.Context) error { return nil } diff --git a/internal/tasks/tools/shell/ci.go b/internal/tasks/tools/shell/ci.go index 181758e..636e4dc 100644 --- a/internal/tasks/tools/shell/ci.go +++ b/internal/tasks/tools/shell/ci.go @@ -2,6 +2,7 @@ package shtools import ( "context" + "fmt" "github.com/opensourcecorp/oscar/internal/system" taskutil "github.com/opensourcecorp/oscar/internal/tasks/util" @@ -46,7 +47,7 @@ func (t shellcheck) InfoText() string { return "Lint (shellcheck)" } // Exec implements [taskutil.Tasker.Exec]. func (t shellcheck) Exec(ctx context.Context) error { if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { - return err + return fmt.Errorf("running shellcheck: %w", err) } return nil @@ -61,7 +62,7 @@ func (t shfmt) InfoText() string { return "Format (shfmt)" } // Exec implements [taskutil.Tasker.Exec]. func (t shfmt) Exec(ctx context.Context) error { if _, err := system.RunCommand(ctx, t.RunArgs); err != nil { - return err + return fmt.Errorf("running shfmt: %w", err) } return nil diff --git a/internal/tasks/tools/toolcfg/.golangci.yaml b/internal/tasks/tools/toolcfg/.golangci.yaml index 7012343..be72659 100644 --- a/internal/tasks/tools/toolcfg/.golangci.yaml +++ b/internal/tasks/tools/toolcfg/.golangci.yaml @@ -6,120 +6,207 @@ linters: # Disable all so we can then enable just the ones we want default: "none" enable: - # - "arangolint" - # - "asasalint" - # - "asciicheck" - # - "bidichk" - # - "bodyclose" - # - "canonicalheader" - # - "containedctx" - # - "contextcheck" - # - "copyloopvar" - # - "cyclop" - # - "decorder" + - "asasalint" + - "asciicheck" + - "bidichk" + - "bodyclose" + - "canonicalheader" + - "containedctx" + - "contextcheck" + - "copyloopvar" + - "decorder" + # TODO: This would be good if we want to e.g. enforce nonusage of a deprecated package # - "depguard" - # - "dogsled" - # - "dupl" - # - "dupword" - # - "durationcheck" - # - "embeddedstructfieldcheck" + - "dogsled" + - "dupl" + - "durationcheck" + - "embeddedstructfieldcheck" + # TODO: eventually. # - "err113" - "errcheck" - # - "errchkjson" - # - "errname" - # - "errorlint" - # - "exhaustive" + - "errchkjson" + - "errname" + - "errorlint" + - "exhaustive" + # TODO: enable? # - "exhaustruct" - # - "exptostd" - # - "fatcontext" - # - "forbidigo" + - "exptostd" + - "fatcontext" + # TODO: enable # - "forcetypeassert" - # - "funcorder" - # - "funlen" - # - "ginkgolinter" - # - "gocheckcompilerdirectives" - # - "gochecknoglobals" - # - "gochecknoinits" - # - "gochecksumtype" + - "funcorder" + - "gocheckcompilerdirectives" + - "gochecknoinits" + # TODO: enable? # - "gocognit" + # TODO: enable # - "goconst" - # - "gocritic" - # - "gocyclo" - "godoclint" - # - "godot" - # - "godox" - # - "goheader" - # - "gomoddirectives" - # - "gomodguard" - # - "goprintffuncname" - # - "gosec" - # - "gosmopolitan" + - "godot" + - "goprintffuncname" + - "gosec" - "govet" - # - "grouper" - # - "iface" - # - "importas" - # - "inamedparam" - # - "ineffassign" - # - "interfacebloat" - # - "intrange" - # - "iotamixing" - # - "ireturn" - # - "lll" - # - "loggercheck" - # - "maintidx" - # - "makezero" - # - "mirror" - # - "misspell" - # - "mnd" - # - "modernize" - # - "musttag" - # - "nakedret" - # - "nestif" - # - "nilerr" - # - "nilnesserr" - # - "nilnil" - # - "nlreturn" - # - "noctx" - # - "noinlineerr" - # - "nolintlint" - # - "nonamedreturns" - # - "nosprintfhostport" - # - "paralleltest" - # - "perfsprint" - # - "prealloc" - # - "predeclared" - # - "promlinter" + - "grouper" + - "iface" + - "inamedparam" + - "ineffassign" + - "interfacebloat" + - "intrange" + - "iotamixing" + - "ireturn" + - "lll" + - "makezero" + - "mirror" + - "misspell" + - "modernize" + - "nakedret" + - "nestif" + - "nilnesserr" + - "nilnil" + - "nlreturn" + - "noctx" + - "nolintlint" + - "nonamedreturns" + - "nosprintfhostport" + - "perfsprint" + - "predeclared" - "protogetter" - # - "reassign" - # - "recvcheck" + - "reassign" + - "recvcheck" - "revive" - # - "rowserrcheck" - # - "sloglint" - # - "spancheck" - # - "sqlclosecheck" + - "rowserrcheck" + - "sloglint" + - "sqlclosecheck" - "staticcheck" - # - "tagalign" - # - "tagliatelle" - # - "testableexamples" - # - "testifylint" - # - "testpackage" - # - "thelper" - # - "tparallel" - # - "unconvert" - # - "unparam" - # - "unqueryvet" - # - "unused" - # - "usestdlibvars" - # - "usetesting" - # - "varnamelen" - # - "wastedassign" - # - "whitespace" - # - "wrapcheck" - # - "wsl_v5" - # - "zerologlint" + - "tagalign" + - "tagliatelle" + - "testifylint" + - "thelper" + - "unqueryvet" + - "usestdlibvars" + - "wastedassign" + - "whitespace" + - "wrapcheck" + - "wsl_v5" disable: [] # See: https://golangci-lint.run/docs/linters/configuration settings: + decorder: + disable-dec-order-check: false # defaults to "true", for some reason + dec-order: + - "const" + - "var" + - "type" + - "func" + dogsled: + max-blank-identifiers: 1 + dupl: + threshold: 100 + errcheck: + # TODO: see associated Docker Compose-related comment in the following method for when to set + # this to "true": + # internal/tasks/tools/containertools.imageBuildPush.Exec + check-type-assertions: false + check-blank: true + verbose: true + exhaustive: + check: + - "switch" + - "map" + default-case-required: true + fatcontext: + # NOTE: may generate false positives. + check-struct-pointers: true + funcorder: + constructor: true + struct-method: true + alphabetical: false + goconst: + min-len: 2 + min-occurrences: 2 + numbers: true + ignore-calls: false + find-duplicates: true + eval-const-expressions: true + godoclint: + default: "none" + enable: + - "pkg-doc" + - "single-pkg-doc" + - "require-pkg-doc" + - "start-with-name" + # - "require-doc" + - "deprecated" + - "max-len" + - "no-unused-link" + options: + max-len: + length: 120 + godot: + scope: "toplevel" + capital: true + gosec: + excludes: + - "G204" + - "G304" + config: + G302: "0700" # so we can mark e.g. built files as executable + govet: + enable-all: true + disable: + - "fieldalignment" + settings: + shadow: + strict: true + grouper: + const-require-single-const: true + import-require-single-import: true + type-require-single-type: true + var-require-single-var: true + iface: + enable: + - "identical" + - "unused" + - "opaque" + - "unexported" + interfacebloat: + max: 10 + ireturn: + allow: + - "anon" + - "empty" + - "error" + - "stdlib" + - "(or|er)$" + lll: + line-length: 120 + misspell: + locale: "US" + mode: "restricted" + nakedret: + max-func-lines: 0 + nestif: + min-complexity: 5 + nilnil: + only-two: true + detect-opposite: true + nlreturn: + block-size: 2 + nolintlint: + # Prevents the usage of "//nolint:" directives + allow-unused: false + require-explanation: true + nonamedreturns: + report-error-in-defer: false + perfsprint: + strconcat: false + reassign: + patterns: + - ".*" + rowserrcheck: + # NOTE: database/sql is always checked. + packages: + - "github.com/jmoiron/sqlx" revive: confidence: 0.8 errorCode: 1 @@ -129,11 +216,57 @@ linters: rules: - name: "increment-decrement" disabled: true + sloglint: + kv-only: true + no-global: "all" + context: "all" + static-msg: false + msg-style: "capitalized" + no-raw-keys: false + key-naming-case: "snake" + forbidden-keys: + - "time" + - "level" + - "msg" + - "source" + - "foo" + args-on-sep-lines: true staticcheck: checks: - "all" # staticcheck won't ignore generated code for this one, so revive handles this check instead - "-ST1000" + tagalign: + align: true + sort: true + strict: true + tagliatelle: + case: + rules: + avro: "snake" + bson: "snake" + db: "snake" + env: "snake" + envconfig: "snake" + json: "snake" + mapstructure: "snake" + toml: "snake" + xml: "snake" + yaml: "snake" + testifylint: + enable-all: true + usestdlibvars: + constant-kind: true + crypto-hash: true + default-rpc-path: true + http-method: true + http-status-code: true + sql-isolation-level: true + time-date-month: true + time-layout: true + time-month: true + time-weekday: true + tls-signature-scheme: true exclusions: # See: https://go.dev/s/generatedcode generated: "strict" diff --git a/internal/tasks/tools/toolcfg/.markdownlint-cli2.yaml b/internal/tasks/tools/toolcfg/.markdownlint-cli2.yaml index 7259a38..7b7c2e3 100644 --- a/internal/tasks/tools/toolcfg/.markdownlint-cli2.yaml +++ b/internal/tasks/tools/toolcfg/.markdownlint-cli2.yaml @@ -1,6 +1,9 @@ --- config: default: true + # NOTE: see comment in markdownlint-cli2's Exec() Go method for why this can't be here + # globs: + # - "#**/*venv*/**/*.md" MD013: line_length: 100 code_blocks: false diff --git a/internal/tasks/tools/toolcfg/pyproject.toml b/internal/tasks/tools/toolcfg/pyproject.toml index d32020f..bd7e1be 100644 --- a/internal/tasks/tools/toolcfg/pyproject.toml +++ b/internal/tasks/tools/toolcfg/pyproject.toml @@ -7,7 +7,11 @@ requires-python = ">=3.12.0" dependencies = [] [dependency-groups] -dev = [] +dev = [ + "pydoclint>=0.8.3", + "ruff>=0.14.10", + "ty>=0.0.5", +] [build-system] requires = ["hatchling"] @@ -22,11 +26,8 @@ packages = ["src/foo"] # Allows for `pip install`s of the package, in case it has a GitHub dependency allow-direct-references = true -# mypy does type-checking -[tool.mypy] -# Don't fail on imported packages that themselves don't have type stubs -ignore_missing_imports = true -exclude = [] +# ty does type-checking +[tool.ty] # pydoclint lints docstrings. # Configuration options: https://jsh9.github.io/pydoclint/config_options.html diff --git a/internal/tasks/tools/toolcfg/util.go b/internal/tasks/tools/toolcfg/util.go index a04217f..b1ff040 100644 --- a/internal/tasks/tools/toolcfg/util.go +++ b/internal/tasks/tools/toolcfg/util.go @@ -11,12 +11,12 @@ import ( // SetupConfigFile handles reading a Tool's config file from the embedded filesystem, and writing it // to its target location. func SetupConfigFile(t taskutil.Tool) error { - cfgFileContents, err := Files.ReadFile(filepath.Base(t.ConfigFilePath)) - if err != nil { - return fmt.Errorf("reading embedded file contents: %w", err) + cfgFileContents, readErr := Files.ReadFile(filepath.Base(t.ConfigFilePath)) + if readErr != nil { + return fmt.Errorf("reading embedded file contents: %w", readErr) } - if err := os.WriteFile(t.ConfigFilePath, cfgFileContents, 0644); err != nil { + if err := os.WriteFile(t.ConfigFilePath, cfgFileContents, 0600); err != nil { return fmt.Errorf("writing config file: %w", err) } diff --git a/internal/tasks/tools/version/ci.go b/internal/tasks/tools/version/ci.go index 963a61d..2727bf4 100644 --- a/internal/tasks/tools/version/ci.go +++ b/internal/tasks/tools/version/ci.go @@ -28,10 +28,10 @@ func NewTasksForCI(_ taskutil.Repo) []taskutil.Tasker { func (t versionCI) InfoText() string { return "Versioning checks" } // Exec implements [taskutil.Tasker.Exec]. -func (t versionCI) Exec(ctx context.Context) (err error) { - cfg, err := oscarcfg.Get() - if err != nil { - return fmt.Errorf("getting oscar config: %w", err) +func (t versionCI) Exec(ctx context.Context) (outErr error) { + cfg, cfgErr := oscarcfg.Get() + if cfgErr != nil { + return fmt.Errorf("getting oscar config: %w", cfgErr) } version := cfg.GetVersion() @@ -42,19 +42,19 @@ func (t versionCI) Exec(ctx context.Context) (err error) { // least of which being that alternatives would be unreliable in e.g. GitHub Actions CI based on // how it treats PR checkouts et al. A small price to pay for reliability. tmpCloneDir := filepath.Join(os.TempDir(), "oscar-ci", "this-repo") - if err := os.MkdirAll(filepath.Dir(tmpCloneDir), 0o755); err != nil { + if err := os.MkdirAll(filepath.Dir(tmpCloneDir), 0700); err != nil { return fmt.Errorf("creating temp clone parent directory: %w", err) } defer func() { - if rmErr := os.RemoveAll(tmpCloneDir); rmErr != nil { - err = errors.Join(err, fmt.Errorf("removing temp clone directory: %w", rmErr)) + if err := os.RemoveAll(tmpCloneDir); err != nil { + outErr = errors.Join(outErr, fmt.Errorf("removing temp clone directory: %w", err)) } }() - remote, err := system.RunCommand(ctx, []string{"git", "remote", "get-url", "origin"}) - if err != nil { - return fmt.Errorf("determining git root: %w", err) + remote, runCmdErr := system.RunCommand(ctx, []string{"git", "remote", "get-url", "origin"}) + if runCmdErr != nil { + return fmt.Errorf("determining git root: %w", runCmdErr) } remote = canonicalizeGitRemote(remote) @@ -63,12 +63,12 @@ func (t versionCI) Exec(ctx context.Context) (err error) { return fmt.Errorf("cloning repo source to temp location: %w", err) } - mainCfg, err := oscarcfg.Get(filepath.Join(tmpCloneDir, consts.DefaultOscarCfgFileName)) - if err != nil { - return fmt.Errorf("getting oscar config: %w", err) + mainBranchCfg, mainBranchCfgErr := oscarcfg.Get(filepath.Join(tmpCloneDir, consts.DefaultOscarCfgFileName)) + if mainBranchCfgErr != nil { + return fmt.Errorf("getting oscar config from main branch: %w", mainBranchCfgErr) } - mainVersion := mainCfg.GetVersion() + mainVersion := mainBranchCfg.GetVersion() iprint.Debugf("main version: %s\n", version) @@ -77,9 +77,9 @@ 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 := system.RunCommand(ctx, []string{"git", "rev-parse", "--abbrev-ref", "HEAD"}) - if err != nil { - return fmt.Errorf("checking current Git branch/ref: %w", err) + branch, getBranchErr := system.RunCommand(ctx, []string{"git", "rev-parse", "--abbrev-ref", "HEAD"}) + if getBranchErr != nil { + return fmt.Errorf("checking current Git branch/ref: %w", getBranchErr) } iprint.Debugf("current Git branch/ref: %s\n", branch) @@ -93,7 +93,7 @@ func (t versionCI) Exec(ctx context.Context) (err error) { } } - return nil + return outErr } // Post implements [taskutil.Tasker.Post]. diff --git a/internal/tasks/tools/yaml/ci.go b/internal/tasks/tools/yaml/ci.go index 15579a6..e68dc8e 100644 --- a/internal/tasks/tools/yaml/ci.go +++ b/internal/tasks/tools/yaml/ci.go @@ -54,11 +54,11 @@ func (t yamllint) InfoText() string { return "Lint (yamllint)" } // Exec implements [taskutil.Tasker.Exec]. func (t yamllint) Exec(ctx context.Context) error { if err := toolcfg.SetupConfigFile(t.Tool); err != nil { - return err + return fmt.Errorf("setting up yamllint config file: %w", err) } if _, err := system.RunCommand(ctx, t.RenderRunCommandArgs()); err != nil { - return err + return fmt.Errorf("running yamllint: %w", err) } return nil @@ -73,11 +73,11 @@ func (t yamlfmt) InfoText() string { return "Format (yamlfmt)" } // Exec implements [taskutil.Tasker.Exec]. func (t yamlfmt) Exec(ctx context.Context) error { if err := toolcfg.SetupConfigFile(t.Tool); err != nil { - return err + return fmt.Errorf("setting up yamlfmt config file: %w", err) } if _, err := system.RunCommand(ctx, t.RenderRunCommandArgs()); err != nil { - return err + return fmt.Errorf("running yamlfmt: %w", err) } return nil diff --git a/internal/tasks/util/core_types.go b/internal/tasks/util/core_types.go index 5b9415b..5de185b 100644 --- a/internal/tasks/util/core_types.go +++ b/internal/tasks/util/core_types.go @@ -8,27 +8,32 @@ import ( iprint "github.com/opensourcecorp/oscar/internal/print" ) -// Tasker defines the method set for working with metadata for a given CI Task. -type Tasker interface { - // InfoText should return a human-readable display string that describes the task, e.g. "Run - // tests". - InfoText() string - // Exec should perform the actual task's actions. - Exec(ctx context.Context) error - // Post should perform any post-run actions for the task, if necessary. - Post(ctx context.Context) error -} +type ( + // Tasker defines the method set for working with metadata for a given CI Task. + Tasker interface { + // InfoText should return a human-readable display string that describes the task, e.g. "Run + // tests". + InfoText() string + // Exec should perform the actual task's actions. + Exec(ctx context.Context) error + // Post should perform any post-run actions for the task, if necessary. + Post(ctx context.Context) error + } -// A Tool defines information about a tool used for running oscar's tasks. A Tool should be defined -// if a language etc. cannot perform the task itself. For example, you would not need a Tool to -// represent a task that runs "go test", but you *would* need a tool to represent a task that runs -// the external "staticcheck" linter for Go. -type Tool struct { - // The list of command & arguments to run during [Tasker.Exec]. - RunArgs []string - // The path to the tool's config file, if it has one to use. - ConfigFilePath string -} + // A Tool defines information about a tool used for running oscar's tasks. A Tool should be + // defined if a language etc. cannot perform the task itself. For example, you would not need a + // Tool to represent a task that runs "go test", but you *would* need a tool to represent a task + // that runs the external "staticcheck" linter for Go. + Tool struct { + // The list of command & arguments to run during [Tasker.Exec]. + RunArgs []string + // The path to the tool's config file, if it has one to use. + ConfigFilePath string + } + + // TaskMap aliases a map of a Task's language/tooling name to its list of Tasks. + TaskMap map[string][]Tasker +) // RenderRunCommandArgs uses [Tool.RunArgs] and does naive templating to replace certain values // before being used. @@ -49,9 +54,6 @@ func (t Tool) RenderRunCommandArgs() []string { return out } -// TaskMap aliases a map of a Task's language/tooling name to its list of Tasks. -type TaskMap map[string][]Tasker - // SortedKeys sorts the keys of the [TaskMap]. Useful for iterating through Tasks in a predictable // order during runs. func (tm TaskMap) SortedKeys() []string { diff --git a/internal/tasks/util/repo.go b/internal/tasks/util/repo.go index 0cb920d..75aa2fe 100644 --- a/internal/tasks/util/repo.go +++ b/internal/tasks/util/repo.go @@ -19,46 +19,6 @@ type Repo struct { HasMarkdown bool } -// String implements the [fmt.Stringer] interface. -func (repo Repo) String() string { - var out string - - out += "The following file types were found in this repo, and tasks will be run against them:\n" - - if repo.HasGo { - out += "- Go\n" - } - - if repo.HasPython { - out += "- Python\n" - } - - if repo.HasShell { - out += "- Shell (sh, bash, etc.)\n" - } - - if repo.HasTerraform { - out += "- Terraform\n" - } - - if repo.HasContainerfile { - out += "- Containerfile\n" - } - - if repo.HasYaml { - out += "- YAML\n" - } - - if repo.HasMarkdown { - out += "- Markdown\n" - } - - // One more newline for padding - out += "\n" - - return out -} - // NewRepo returns a populated [Repo]. func NewRepo(ctx context.Context) (Repo, error) { var errs error @@ -115,3 +75,43 @@ func NewRepo(ctx context.Context) (Repo, error) { return repo, nil } + +// String implements the [fmt.Stringer] interface. +func (repo Repo) String() string { + var out string + + out += "The following file types were found in this repo, and tasks will be run against them:\n" + + if repo.HasGo { + out += "- Go\n" + } + + if repo.HasPython { + out += "- Python\n" + } + + if repo.HasShell { + out += "- Shell (sh, bash, etc.)\n" + } + + if repo.HasTerraform { + out += "- Terraform\n" + } + + if repo.HasContainerfile { + out += "- Containerfile\n" + } + + if repo.HasYaml { + out += "- YAML\n" + } + + if repo.HasMarkdown { + out += "- Markdown\n" + } + + // One more newline for padding. + out += "\n" + + return out +} diff --git a/internal/tasks/util/run.go b/internal/tasks/util/run.go index 481ede4..1e73372 100644 --- a/internal/tasks/util/run.go +++ b/internal/tasks/util/run.go @@ -44,7 +44,7 @@ func NewRun(ctx context.Context, runType string) (Run, error) { git, err := igit.New(ctx) if err != nil { - return Run{}, err + return Run{}, fmt.Errorf("creating Git handler: %w", err) } iprint.Infof(colors.Gray + git.String() + colors.Reset) @@ -110,7 +110,7 @@ func (run Run) ReportSuccess() { // ReportFailure prints information about the failure of a [Run]. It takes an `error` arg in case // the caller is expecting to return a joined error because of e.g. deferred calls or later-checked // errors that an outer variable already holds. -func (run Run) ReportFailure(err error) error { +func (run Run) ReportFailure(outErr error) error { iprint.Errorf("\n%s\n", strings.Repeat("=", 65)) iprint.Errorf("The following tasks failed: (%s)\n", iprint.RunDurationString(run.StartTime)) @@ -120,7 +120,7 @@ func (run Run) ReportFailure(err error) error { iprint.Errorf("%s\n\n", strings.Repeat("=", 65)) - err = errors.Join(err, errors.New("one or more tasks failed")) + outErr = errors.Join(outErr, errors.New("one or more tasks failed")) - return err + return outErr } diff --git a/mise.toml b/mise.toml index a6c267b..ff0d1a2 100644 --- a/mise.toml +++ b/mise.toml @@ -7,7 +7,7 @@ buf = "1.57.2" github-cli = "2.79.0" go = "1.25.5" hadolint = "2.13.1" -markdownlint-cli2 = "0.18.1" +markdownlint-cli2 = "0.20.0" # required for markdownlint-cli2 node = "24.8.0" protobuf = "32.1" @@ -29,3 +29,4 @@ yamllint = "1.37.1" ### Python-specific tools. Note that others that may also be CLI tools, may be run internally via ### `uvx` instead. ruff = "0.13.1" +# ty = "0.0.5" # NOTE: not in registry at the time of this writing, will need to run with uvx diff --git a/proto/generate.go b/proto/generate.go index 71d677f..e6c083a 100644 --- a/proto/generate.go +++ b/proto/generate.go @@ -1,4 +1,4 @@ -// Package main +// Package main is // TODO: . package main import "fmt" From 18881b44e449c34d72769e90733387a20be694e6 Mon Sep 17 00:00:00 2001 From: "Ryan J. Price" Date: Mon, 22 Dec 2025 15:59:55 -0600 Subject: [PATCH 04/10] Bump MISE_VERSION in GHA --- .github/workflows/main.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index ca7e3e3..85972b7 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -15,6 +15,7 @@ env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}" MISE_GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + MISE_VERSION: "2025.11.11" jobs: ci: @@ -43,7 +44,7 @@ jobs: - name: "Set up mise" uses: "jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566" # v3.2.0 with: - version: "2025.9.10" + version: "{{ env.MISE_VERSION }}" install: true # runs `mise install` cache: true - name: "Run CI Tasks" From 5cb9ac62a386dfea55f4d23a29906a91f6669f76 Mon Sep 17 00:00:00 2001 From: "Ryan J. Price" Date: Mon, 22 Dec 2025 16:01:11 -0600 Subject: [PATCH 05/10] ugh GHA --- .github/workflows/main.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 85972b7..97715c1 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -44,7 +44,7 @@ jobs: - name: "Set up mise" uses: "jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566" # v3.2.0 with: - version: "{{ env.MISE_VERSION }}" + version: "${{ env.MISE_VERSION }}" install: true # runs `mise install` cache: true - name: "Run CI Tasks" From aa85c785e4930a8654cab8db9908e25647521e8e Mon Sep 17 00:00:00 2001 From: "Ryan J. Price" Date: Tue, 23 Dec 2025 09:24:14 -0600 Subject: [PATCH 06/10] jfc --- .github/workflows/main.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 97715c1..4646ea9 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -47,6 +47,7 @@ jobs: version: "${{ env.MISE_VERSION }}" install: true # runs `mise install` cache: true + github_token: "${{ secrets.GITHUB_TOKEN }}" - name: "Run CI Tasks" run: "make ci" deliver: @@ -78,6 +79,7 @@ jobs: version: "2025.9.10" install: true # runs `mise install` cache: true + github_token: "${{ secrets.GITHUB_TOKEN }}" - name: "Set up Docker" uses: "docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435" # v3.11.1 - name: "Run Delivery Tasks" From 21488e3fa0b6e831047c136bc1f57b2f06c56a44 Mon Sep 17 00:00:00 2001 From: "Ryan J. Price" Date: Tue, 23 Dec 2025 09:39:18 -0600 Subject: [PATCH 07/10] See if the GHA takes a v prefix so the rest of our code still works --- .github/workflows/main.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 4646ea9..5bbd925 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -15,7 +15,7 @@ env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}" MISE_GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - MISE_VERSION: "2025.11.11" + MISE_VERSION: "v2025.11.11" jobs: ci: From c25834ffd48bd6327768b4a4afe602d67d94939d Mon Sep 17 00:00:00 2001 From: "Ryan J. Price" Date: Tue, 23 Dec 2025 09:46:56 -0600 Subject: [PATCH 08/10] Fix UPX host target, and fix consistency in GH file --- .github/workflows/main.yaml | 8 ++------ Makefile | 4 +++- mise.toml | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 5bbd925..68b50cd 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -43,7 +43,7 @@ jobs: make - name: "Set up mise" uses: "jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566" # v3.2.0 - with: + with: &mise_with version: "${{ env.MISE_VERSION }}" install: true # runs `mise install` cache: true @@ -75,11 +75,7 @@ jobs: make - name: "Set up mise" uses: "jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566" # v3.2.0 - with: - version: "2025.9.10" - install: true # runs `mise install` - cache: true - github_token: "${{ secrets.GITHUB_TOKEN }}" + with: *mise_with - name: "Set up Docker" uses: "docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435" # v3.11.1 - name: "Run Delivery Tasks" diff --git a/Makefile b/Makefile index 13580c9..f7d2687 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,9 @@ test: ci # have a stage-copiable output build: FORCE @$(RUN) go build -ldflags '-s -w -extldflags "-static"' -o ./build/$(BINNAME) ./cmd/$(BINNAME) - @upx --best ./build/$(BINNAME) + @if [[ "$$(uname -s)" == "Linux" ]] ; then \ + upx --best ./build/$(BINNAME) ; \ + fi clean: FORCE @rm -rf \ diff --git a/mise.toml b/mise.toml index ff0d1a2..c191377 100644 --- a/mise.toml +++ b/mise.toml @@ -16,7 +16,7 @@ ripgrep = "14.1.1" shellcheck = "0.11.0" shfmt = "3.12.0" terraform = "1.13.3" -upx = "5.0.2" +upx = { version = "5.0.2", os = ["linux"] } uv = "0.8.18" yamlfmt = "0.17.2" yamllint = "1.37.1" From 1c346aa2d76bbff53269ea66a57c016abdfddbc2 Mon Sep 17 00:00:00 2001 From: "Ryan J. Price" Date: Tue, 23 Dec 2025 10:32:42 -0600 Subject: [PATCH 09/10] Try adding cache mounts to Containerfile, and add mise version-setter --- .github/workflows/main.yaml | 2 +- Containerfile | 10 ++++++++-- Makefile | 14 ++++++++++++-- docker-compose.yaml | 10 ++++++++-- internal/consts/consts.go | 4 +++- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 68b50cd..d0785ec 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -15,7 +15,7 @@ env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}" MISE_GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - MISE_VERSION: "v2025.11.11" + MISE_VERSION: "v2025.12.0" # NOTE: set via 'make set-mise-version' jobs: ci: diff --git a/Containerfile b/Containerfile index a32ad03..885b778 100644 --- a/Containerfile +++ b/Containerfile @@ -1,4 +1,7 @@ +ARG GITHUB_TOKEN ARG GO_VERSION +ARG GOPRIVATE +ARG MISE_GITHUB_TOKEN ARG MISE_VERSION # These two proxy args are helpful if you're trying to build on a corporate network -- they do not @@ -21,7 +24,8 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ COPY . /go/app WORKDIR /go/app -RUN make build +RUN --mount=type=cache,target=/go/pkg/mod \ + make build #################################################################################################### @@ -45,7 +49,9 @@ WORKDIR /go/app # NOTE: when creating some shims, mise refers to itself assuming it is on the $PATH, so we need to # symlink it out so it can do that -RUN ln -fs "${HOME}/.oscar/bin/mise" /usr/local/bin/mise && \ +RUN --mount=type=cache,target=/go/pkg/mod \ + --mount=type=cache,target=/root/.oscar \ + ln -fs "${HOME}/.oscar/bin/mise" /usr/local/bin/mise && \ bash ./scripts/test-bootstrap.sh setup && \ /oscar ci diff --git a/Makefile b/Makefile index f7d2687..2d75996 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,13 @@ export IMAGE_NAME ?= $(BINNAME) export IMAGE_TAG ?= latest export IMAGE_URI ?= $(IMAGE_REGISTRY)/$(IMAGE_REGISTRY_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG) -SHELL = /usr/bin/env bash -euo pipefail +# use the intended GNU tools on macOS +gawk = $(shell command -v gawk) +gsed = $(shell command -v sed) +ifeq ($(shell uname -s),Darwin) +gawk = $(shell command -v gawk) +gsed = $(shell command -v gsed) +endif all: ci @@ -53,7 +59,7 @@ clean: FORCE image: clean @export BUILDKIT_PROGRESS=plain && \ - export GO_VERSION="$$(awk '/^go/ { print $$2 }' go.mod)" && \ + export GO_VERSION="$$($(gawk) '/^go/ { print $$2 }' go.mod)" && \ $(RUN) $(DOCKER) compose build run-image: FORCE @@ -61,3 +67,7 @@ run-image: FORCE generate: FORCE @cd ./proto && $(RUN) buf generate + +set-mise-version: + @find . -type f \ + | xargs -I{} $(gsed) -i -E 's/(MISE_VERSION.*|MiseVersion.*)([0-9]{4}\.[0-9]+\.[0-9]+)/\1$(mise_version)/g' {} diff --git a/docker-compose.yaml b/docker-compose.yaml index 8bb28be..de653cd 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -6,10 +6,13 @@ services: context: "." dockerfile: "./Containerfile" args: - GO_VERSION: "${GO_VERSION:-1.25.0}" - MISE_VERSION: "${MISE_VERSION:-v2025.11.11}" + GITHUB_TOKEN: "${GITHUB_TOKEN:-}" + GO_VERSION: "${GO_VERSION:-}" + GOPRIVATE: "${GOPRIVATE:-}" http_proxy: "${http_proxy:-}" https_proxy: "${https_proxy:-}" + MISE_GITHUB_TOKEN: "${MISE_GITHUB_TOKEN:-}" + MISE_VERSION: "${MISE_VERSION:-v2025.12.0}" # NOTE: set via 'make set-mise-version' image: "${IMAGE_URI:-}" pull_policy: "build" command: @@ -17,6 +20,9 @@ services: environment: http_proxy: "${http_proxy}" https_proxy: "${https_proxy}" + GITHUB_TOKEN: "${GITHUB_TOKEN}" + MISE_GITHUB_TOKEN: "${MISE_GITHUB_TOKEN}" + GOPRIVATE: "${GOPRIVATE}" volumes: - type: "bind" source: "${PWD}" diff --git a/internal/consts/consts.go b/internal/consts/consts.go index 1bf7ff9..af39787 100644 --- a/internal/consts/consts.go +++ b/internal/consts/consts.go @@ -19,7 +19,9 @@ const ( // 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. - MiseVersion = "v2025.11.11" + // + // NOTE: set via 'make set-mise-version'. + MiseVersion = "v2025.12.0" // DefaultOscarCfgFileName is the default basename of oscar's config file. DefaultOscarCfgFileName = "oscar.yaml" From 4f3863b7be23f2ac9244559fbb3a481dccd13de5 Mon Sep 17 00:00:00 2001 From: "Ryan J. Price" Date: Tue, 23 Dec 2025 10:33:30 -0600 Subject: [PATCH 10/10] Add doc comment to new Make target --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 2d75996..a6a4e8b 100644 --- a/Makefile +++ b/Makefile @@ -68,6 +68,8 @@ run-image: FORCE generate: FORCE @cd ./proto && $(RUN) buf generate +# Since some variant of "MISE_VERSION" shows up in a lot of disjointed places in the codebase, this +# lets us update all of them at once set-mise-version: @find . -type f \ | xargs -I{} $(gsed) -i -E 's/(MISE_VERSION.*|MiseVersion.*)([0-9]{4}\.[0-9]+\.[0-9]+)/\1$(mise_version)/g' {}