From 2d9c323888fa4eac44a6b79b71ef1e21c829f502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20G=C3=B3rski?= Date: Mon, 28 Jul 2025 11:29:24 +0200 Subject: [PATCH 1/2] feat: add --upgrade flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dawid Górski --- internal/cmd/get/get.go | 39 +++++++++++++++++++++++++++++++ internal/cmd/root/loader.go | 1 - internal/dependency/dependency.go | 9 +++++++ internal/scope/global.go | 28 +++++++++------------- 4 files changed, 59 insertions(+), 18 deletions(-) diff --git a/internal/cmd/get/get.go b/internal/cmd/get/get.go index e751c30..2d19d51 100644 --- a/internal/cmd/get/get.go +++ b/internal/cmd/get/get.go @@ -6,6 +6,7 @@ import ( "github.com/g2a-com/klio/internal/context" "github.com/g2a-com/klio/internal/dependency" + "github.com/g2a-com/klio/internal/dependency/manager" "github.com/g2a-com/klio/internal/log" "github.com/g2a-com/klio/internal/scope" "github.com/spf13/cobra" @@ -19,6 +20,7 @@ type options struct { As string Version string NoInit bool + Upgrade bool } // NewCommand creates a new getCommand command. @@ -39,6 +41,7 @@ func NewCommand(ctx context.CLIContext) *cobra.Command { cmd.Flags().StringVar(&opts.As, "as", "", "changes name under which dependency is installed") cmd.Flags().BoolVar(&opts.NoInit, "no-init", false, "prevent creating config file if not exist") cmd.Flags().StringVar(&opts.Version, "version", "*", "version range of the dependency") + cmd.Flags().BoolVar(&opts.Upgrade, "upgrade", false, fmt.Sprintf("download the latest available version instead of the one defined in %s.yaml", ctx.Config.CommandName)) return cmd } @@ -60,6 +63,9 @@ func getCommand(ctx context.CLIContext, opts *options, args []string) { switch len(args) { case 0: dependencies = getScope.GetImplicitDependencies() + if opts.Upgrade { + dependencies = getLatestVersions(ctx, dependencies) + } case 1: dependencies = []dependency.Dependency{ { @@ -69,6 +75,9 @@ func getCommand(ctx context.CLIContext, opts *options, args []string) { Alias: opts.As, }, } + if opts.Upgrade { + dependencies = getLatestVersions(ctx, dependencies) + } default: log.Fatalf("max one command can be provided for install; provided %d", len(args)) } @@ -84,3 +93,33 @@ func getCommand(ctx context.CLIContext, opts *options, args []string) { } log.Infof("All dependencies (%s) installed successfully", strings.Join(formattingArray, ",")) } + +// getLatestVersions updates the version field of dependencies to the latest available version. +func getLatestVersions(ctx context.CLIContext, deps []dependency.Dependency) []dependency.Dependency { + manager := manager.NewManager() + manager.DefaultRegistry = ctx.Config.DefaultRegistry + + for i := range deps { + dep := &deps[i] + + // Get latest version using GetUpdateFor + updates, err := manager.GetUpdateFor(*dep) + if err != nil { + log.Debugf("Failed to get updates for %s: %s", dep.Name, err) + continue + } + + // Use the latest version available + latestVersion := updates.Breaking + if latestVersion == "" { + latestVersion = updates.NonBreaking + } + + if latestVersion != "" { + dep.Version = latestVersion + log.Infof("Upgrading %s to latest version: %s", dep.Name, latestVersion) + } + } + + return deps +} diff --git a/internal/cmd/root/loader.go b/internal/cmd/root/loader.go index 443fd1c..e2d60e8 100644 --- a/internal/cmd/root/loader.go +++ b/internal/cmd/root/loader.go @@ -222,7 +222,6 @@ func getUpdateMessage(ctx context.CLIContext, dep dependency.DependenciesIndexEn } func autoDownloadCommand(ctx *context.CLIContext, dep dependency.DependenciesIndexEntry) (*dependency.DependenciesIndexEntry, error) { - args := os.Args[1:] if len(args) == 0 { return &dep, nil diff --git a/internal/dependency/dependency.go b/internal/dependency/dependency.go index 4cbff25..c7931be 100644 --- a/internal/dependency/dependency.go +++ b/internal/dependency/dependency.go @@ -41,6 +41,15 @@ type DependenciesIndexEntry struct { Path string `json:"path"` } +func (di *DependenciesIndexEntry) ToDependency() Dependency { + return Dependency{ + Name: di.Name, + Registry: di.Registry, + Version: di.Version, + Alias: di.Alias, + } +} + type IndexHandler interface { LoadDependencyIndex(filePath string) error SaveDependencyIndex() error diff --git a/internal/scope/global.go b/internal/scope/global.go index 975cce1..dec99f6 100644 --- a/internal/scope/global.go +++ b/internal/scope/global.go @@ -10,8 +10,6 @@ import ( "github.com/spf13/afero" ) -const allowedNumberOfGlobalCommands = 1 - type global struct { os afero.Fs dependencyManager *manager.Manager @@ -51,23 +49,22 @@ func (g *global) initialize(ctx *context.CLIContext) error { // initialize dependency manager g.dependencyManager = manager.NewManager() g.dependencyManager.DefaultRegistry = ctx.Config.DefaultRegistry + installedCommands := g.dependencyManager.GetInstalledCommands(ctx.Paths) + for _, command := range installedCommands { + if ctx.Paths.IsGlobal(command.Path) { + g.installedDeps = append(g.installedDeps, command.ToDependency()) + } + } return nil } func (g *global) GetImplicitDependencies() []dependency.Dependency { - return []dependency.Dependency{} + return g.installedDeps } func (g *global) InstallDependencies(listOfCommands []dependency.Dependency) ([]dependency.Dependency, []dependency.DependenciesIndexEntry, error) { - if len(listOfCommands) != allowedNumberOfGlobalCommands { - return nil, nil, fmt.Errorf("wrong number of commands provided; provided %d, expected %d", - len(listOfCommands), allowedNumberOfGlobalCommands) - } - - dep := listOfCommands - - installedDeps, installedDepsEntries, err := installDependencies(g.dependencyManager, dep, g.installDir) + installedDeps, installedDepsEntries, err := installDependencies(g.dependencyManager, listOfCommands, g.installDir) if err != nil { return nil, nil, err } @@ -81,14 +78,11 @@ func (g *global) GetInstalledDependencies() []dependency.Dependency { } func (g *global) RemoveDependencies(listOfCommands []dependency.Dependency) error { - if len(listOfCommands) != allowedNumberOfGlobalCommands { - return fmt.Errorf("wrong number of commands provided; provided %d, expected %d", - len(listOfCommands), allowedNumberOfGlobalCommands) + if len(listOfCommands) == 0 { + return fmt.Errorf("no dependencies provided for removal") } - dep := listOfCommands - - g.removedDeps = removeDependencies(g.dependencyManager, dep, g.installDir) + g.removedDeps = removeDependencies(g.dependencyManager, listOfCommands, g.installDir) return nil } From 257f4367b90943e6a05bc0d50fb0ae57ae6724b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20G=C3=B3rski?= Date: Wed, 22 Oct 2025 13:54:34 +0200 Subject: [PATCH 2/2] chore: update golangci lint config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dawid Górski --- .github/workflows/build.yaml | 2 +- .github/workflows/buildPR.yaml | 2 +- .golangci.yaml | 197 ++++++++------------------------- 3 files changed, 46 insertions(+), 155 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 356b8ff..fa2dd4e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -24,7 +24,7 @@ jobs: go-version: 1.25 - name: golangci-lint - uses: golangci/golangci-lint-action@v6 + uses: golangci/golangci-lint-action@v8 with: version: latest diff --git a/.github/workflows/buildPR.yaml b/.github/workflows/buildPR.yaml index 021d0c0..dba85cf 100644 --- a/.github/workflows/buildPR.yaml +++ b/.github/workflows/buildPR.yaml @@ -23,7 +23,7 @@ jobs: go-version: 1.25 - name: golangci-lint - uses: golangci/golangci-lint-action@v6 + uses: golangci/golangci-lint-action@v8 with: version: latest diff --git a/.golangci.yaml b/.golangci.yaml index 931b938..1576faf 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -1,169 +1,60 @@ -# This file contains all available configuration options -# with their default values. - -# options for analysis running +version: "2" run: - # exit code when at least one issue was found, default is 1 - issues-exit-code: 0 - - timeout: 10m - - # which dirs to skip: issues from them won't be reported; - # can use regexp here: generated.*, regexp is applied on full path; - # default value is empty list, but default dirs are skipped independently - # from this option's value (see skip-dirs-use-default). - # "/" will be replaced by current OS file path separator to properly work - # on Windows. -# skip-dirs: -# - clients/ - - # which files to skip: they will be analyzed, but issues from them - # won't be reported. Default value is empty list, but there is - # no need to include all autogenerated files, we confidently recognize - # autogenerated files. If it's not please let us know. - # "/" will be replaced by current OS file path separator to properly work - # on Windows. -# skip-files: -# - version.go - - # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": - # If invoked with -mod=readonly, the go command is disallowed from the implicit - # automatic updating of go.mod described above. Instead, it fails when any changes - # to go.mod are needed. This setting is most useful to check that go.mod does - # not need updates, such as in a continuous integration and testing system. - # If invoked with -mod=vendor, the go command assumes that the vendor - # directory holds the correct copies of dependencies and ignores - # the dependency descriptions in go.mod. modules-download-mode: readonly - -# output configuration options + issues-exit-code: 0 output: - # colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions - # default is "colored-line-number" - formats: - - format: colored-line-number - - # sorts results by: filepath, line and column - sort-results: true - - -# all available settings of specific linters -linters-settings: - tagliatelle: - case: - # use the struct field name to check the name of the struct tag - use-field-name: false - rules: - json: camel - yaml: camel - envconfig: lower - + formats: + text: + path: stdout + colors: true linters: - disable-all: true + default: none enable: - # unused - - ineffassign - # - deadcode - # - structcheck - #complexity - - nestif - - gocyclo - # import - - gci - #- depguard may come in handy - # bugs - bodyclose - - errcheck - - govet - - staticcheck - - typecheck - #- noctx maybe - #- exhaustive will see - #- gosec needs to be implemented - # comment - - godot - #- prealloc - # format - - gofumpt - #style - - gosimple - dogsled + - errcheck - errname - goconst + - gocyclo + - godot + - govet + - ineffassign - mnd + - nestif + - staticcheck - unconvert - fast: false - - + settings: + tagliatelle: + case: + rules: + envconfig: lower + json: camel + yaml: camel + use-field-name: false + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - third_party$ + - builtin$ + - examples$ issues: - # List of regexps of issue texts to exclude, empty list by default. - # But independently from this option we use default exclude patterns, - # it can be disabled by `exclude-use-default: false`. To list all - # excluded by default patterns execute `golangci-lint run --help` - # exclude: - # - abcdef - - # Excluding configuration per-path, per-linter, per-text and per-source - # exclude-rules: - # # Exclude some linters from running on tests files. - # - path: _test\.go - # linters: - # - gocyclo - # - errcheck - # - dupl - # - gosec - # - # # Exclude known linters from partially hard-vendored code, - # # which is impossible to exclude via "nolint" comments. - # - path: internal/hmac/ - # text: "weak cryptographic primitive" - # linters: - # - gosec - # - # # Exclude some staticcheck messages - # - linters: - # - staticcheck - # text: "SA9003:" - # - # # Exclude lll issues for long lines with go:generate - # - linters: - # - lll - # source: "^//go:generate " - - # Maximum issues count per one linter. Set to 0 to disable. Default is 50. max-issues-per-linter: 0 - - # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. max-same-issues: 0 - - # The default value is false. If set to true exclude and exclude-rules - # regular expressions become case sensitive. - exclude-case-sensitive: false - - # Fix found issues (if it's supported by the linter) fix: false - severity: - # Default value is empty string. - # Set the default severity for issues. If severity rules are defined and the issues - # do not match or no severity is provided to the rule this will be the default - # severity applied. Severities should match the supported severity names of the - # selected out format. - # - Code climate: https://docs.codeclimate.com/docs/issues#issue-severity - # - Checkstyle: https://checkstyle.sourceforge.io/property_types.html#severity - # - Github: https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message - default-severity: info - - # The default value is false. - # If set to true severity-rules regular expressions become case sensitive. - case-sensitive: false - - # Default value is empty list. - # When a list of severity rules are provided, severity information will be added to lint - # issues. Severity rules have the same filtering capability as exclude rules except you - # are allowed to specify one matcher per severity rule. - # Only affects out formats that support setting severity information. -# rules: -# - linters: -# - dupl -# severity: info \ No newline at end of file + default: info +formatters: + enable: + - gci + - gofumpt + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$