From c024956172a36e95f26ec031f3709ac18df16cb3 Mon Sep 17 00:00:00 2001 From: Sem Van Broekhoven <144097969+dotsem@users.noreply.github.com> Date: Sun, 24 May 2026 20:36:49 +0200 Subject: [PATCH 1/8] add ci pipeline --- .github/workflows/ci.yml | 95 +++++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 33 ++++++++++++ .goreleaser.yaml | 35 +++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 .goreleaser.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..863bad2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,95 @@ +name: CI + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +on: + push: + branches: ["*"] + pull_request: + branches: ["*"] + +jobs: + lint: + name: Static Code Analysis + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.23" + cache: true + + - name: Check Formatting (gofmt) + run: | + unformatted=$(gofmt -l .) + if [ -n "$unformatted" ]; then + echo "The following files are not formatted correctly:" + echo "$unformatted" + echo "Please run 'gofmt -w .' locally to fix them." + exit 1 + fi + + - name: Run golangci-lint + uses: golangci/golangci-lint-action@v6 + with: + version: latest + args: --config=.golangci.yml + + vulncheck: + name: Security Vulnerability Scan + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.23" + cache: true + + - name: Run govulncheck + run: | + go install golang.org/x/vuln/cmd/govulncheck@latest + govulncheck ./... + + test: + name: Build & Test + runs-on: ubuntu-latest + needs: [lint, vulncheck] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.23" + cache: true + + - name: Download Go dependencies + run: go mod download + + - name: Build Project + run: go build -v ./... + + - name: Run Unit Tests + run: go test -v -race -coverprofile=coverage.out -covermode=atomic ./... + + - name: Generate Coverage Summary + if: always() + run: | + if [ -f coverage.out ]; then + echo "### Test Coverage Summary" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + go tool cover -func=coverage.out >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + else + echo "### Test Coverage Summary" >> $GITHUB_STEP_SUMMARY + echo "No coverage file found." >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..e171287 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,33 @@ +name: Release + +on: + push: + tags: + - "v*" + +permissions: + contents: write + +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.23" + cache: true + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..5099b8d --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,35 @@ +version: 2 +project_name: tusshi + +before: + hooks: + - go mod tidy + +builds: + - env: + - CGO_ENABLED=0 + goos: + - linux + - darwin + - windows + goarch: + - amd64 + - arm64 + main: ./cmd/main.go + +archives: + - format: tar.gz + # use zip for windows archives + format_overrides: + - goos: windows + format: zip + +checksum: + name_template: "checksums.txt" + +changelog: + sort: asc + filters: + exclude: + - "^docs:" + - "^test:" From 7c8b5ffce909450aba623add9c76df9929248c1f Mon Sep 17 00:00:00 2001 From: Sem Van Broekhoven <144097969+dotsem@users.noreply.github.com> Date: Sun, 24 May 2026 20:39:07 +0200 Subject: [PATCH 2/8] update go version --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 863bad2..8891648 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.23" + go-version: "1.26" cache: true - name: Check Formatting (gofmt) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e171287..0a08a16 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.23" + go-version: "1.26" cache: true - name: Run GoReleaser From 1e94941e7fa15ad45daca9d33d2dd8d7f9958701 Mon Sep 17 00:00:00 2001 From: Sem Van Broekhoven <144097969+dotsem@users.noreply.github.com> Date: Sun, 24 May 2026 20:41:16 +0200 Subject: [PATCH 3/8] fix go version --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8891648..c7551c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.23" + go-version: "1.26" cache: true - name: Run govulncheck @@ -69,7 +69,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.23" + go-version: "1.26" cache: true - name: Download Go dependencies From 4d9c3a395974336cab5c2567d484f0601d42fd34 Mon Sep 17 00:00:00 2001 From: Sem Van Broekhoven <144097969+dotsem@users.noreply.github.com> Date: Sun, 24 May 2026 20:45:36 +0200 Subject: [PATCH 4/8] update version for golangci --- .github/workflows/ci.yml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c7551c5..39281dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Go + id: setup-go uses: actions/setup-go@v5 with: go-version: "1.26" @@ -34,11 +35,19 @@ jobs: exit 1 fi - - name: Run golangci-lint - uses: golangci/golangci-lint-action@v6 + - name: Cache golangci-lint binary + id: cache-golangci-lint + uses: actions/cache@v4 with: - version: latest - args: --config=.golangci.yml + path: ~/go/bin/golangci-lint + key: ${{ runner.os }}-golangci-lint-${{ steps.setup-go.outputs.go-version }} + + - name: Install golangci-lint + if: steps.cache-golangci-lint.outputs.cache-hit != 'true' + run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest + + - name: Run golangci-lint + run: $(go env GOPATH)/bin/golangci-lint run --config=.golangci.yml vulncheck: name: Security Vulnerability Scan From 0677d63a77d649357fd3886a0f3c41b2f04d9376 Mon Sep 17 00:00:00 2001 From: Sem Van Broekhoven <144097969+dotsem@users.noreply.github.com> Date: Sun, 24 May 2026 20:51:57 +0200 Subject: [PATCH 5/8] fix golangci version being v1 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 39281dd..44a5df2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: - name: Install golangci-lint if: steps.cache-golangci-lint.outputs.cache-hit != 'true' - run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest + run: go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest - name: Run golangci-lint run: $(go env GOPATH)/bin/golangci-lint run --config=.golangci.yml From 82bca5421b7f69e310e47436a6bacd5678c7d048 Mon Sep 17 00:00:00 2001 From: Sem Van Broekhoven <144097969+dotsem@users.noreply.github.com> Date: Sun, 24 May 2026 21:17:27 +0200 Subject: [PATCH 6/8] fix goconst --- pkg/config/config_file.go | 2 +- pkg/config/editor.go | 12 ++++++------ pkg/config/keys.go | 10 ++++++++++ pkg/config/manager.go | 20 ++++++++++---------- pkg/config/manager_test.go | 14 +++++++------- pkg/tui/constants.go | 9 +++++++++ pkg/tui/model.go | 8 -------- 7 files changed, 43 insertions(+), 32 deletions(-) create mode 100644 pkg/config/keys.go create mode 100644 pkg/tui/constants.go diff --git a/pkg/config/config_file.go b/pkg/config/config_file.go index 7dcd747..04fa954 100644 --- a/pkg/config/config_file.go +++ b/pkg/config/config_file.go @@ -132,7 +132,7 @@ func (m *Manager) DeleteConfigFile(targetPath string) error { for _, astHost := range cfg.Hosts { val := reflect.ValueOf(astHost) isImplicit := false - if val.Kind() == reflect.Ptr && !val.IsNil() { + if val.Kind() == reflect.Pointer && !val.IsNil() { elem := val.Elem() implicitField := elem.FieldByName("implicit") if implicitField.IsValid() && implicitField.Kind() == reflect.Bool && implicitField.Bool() { diff --git a/pkg/config/editor.go b/pkg/config/editor.go index 22bd5c8..f48e771 100644 --- a/pkg/config/editor.go +++ b/pkg/config/editor.go @@ -99,7 +99,7 @@ func (m *Manager) UpdateHost(originalAlias string, h *Host) error { for _, astHost := range decoded.Hosts { val := reflect.ValueOf(astHost) isImplicit := false - if val.Kind() == reflect.Ptr && !val.IsNil() { + if val.Kind() == reflect.Pointer && !val.IsNil() { elem := val.Elem() implicitField := elem.FieldByName("implicit") if implicitField.IsValid() && implicitField.Kind() == reflect.Bool && implicitField.Bool() { @@ -239,20 +239,20 @@ func buildHostString(h *Host) string { var sb strings.Builder fmt.Fprintf(&sb, "Host %s\n", h.Alias) if h.Name != "" { - fmt.Fprintf(&sb, " HostName %s\n", h.Name) + fmt.Fprintf(&sb, " %s %s\n", keyHostName, h.Name) } if h.User != "" { - fmt.Fprintf(&sb, " User %s\n", h.User) + fmt.Fprintf(&sb, " %s %s\n", keyUser, h.User) } if h.Port != "" { - fmt.Fprintf(&sb, " Port %s\n", h.Port) + fmt.Fprintf(&sb, " %s %s\n", keyPort, h.Port) } if h.IdentityFile != "" { - fmt.Fprintf(&sb, " IdentityFile %s\n", h.IdentityFile) + fmt.Fprintf(&sb, " %s %s\n", keyIdentityFile, h.IdentityFile) } for k, v := range h.Properties { - if k != "HostName" && k != "User" && k != "Port" && k != "IdentityFile" && v != "" { + if k != keyHostName && k != keyUser && k != keyPort && k != keyIdentityFile && v != "" { fmt.Fprintf(&sb, " %s %s\n", k, v) } } diff --git a/pkg/config/keys.go b/pkg/config/keys.go new file mode 100644 index 0000000..ce50198 --- /dev/null +++ b/pkg/config/keys.go @@ -0,0 +1,10 @@ +package config + +const ( + keyHostName = "HostName" + keyUser = "User" + keyPort = "Port" + keyIdentityFile = "IdentityFile" + keyForwardAgent = "ForwardAgent" + keyProxyJump = "ProxyJump" +) diff --git a/pkg/config/manager.go b/pkg/config/manager.go index 6722b20..9cf8384 100644 --- a/pkg/config/manager.go +++ b/pkg/config/manager.go @@ -62,7 +62,7 @@ func (m *Manager) GetHosts() []*Host { for _, astHost := range cfg.Hosts { // Skip the implicit default "Host *" block added by the parser val := reflect.ValueOf(astHost) - if val.Kind() == reflect.Ptr && !val.IsNil() { + if val.Kind() == reflect.Pointer && !val.IsNil() { elem := val.Elem() implicitField := elem.FieldByName("implicit") if implicitField.IsValid() && implicitField.Kind() == reflect.Bool && implicitField.Bool() { @@ -106,7 +106,7 @@ func (m *Manager) GetHosts() []*Host { // Inject inherited properties from matching wildcard blocks. if !isWildcard && globalConfig != nil { - for _, key := range []string{"HostName", "User", "Port", "IdentityFile", "ForwardAgent", "ProxyJump"} { + for _, key := range []string{keyHostName, keyUser, keyPort, keyIdentityFile, keyForwardAgent, keyProxyJump} { if _, explicit := h.Properties[key]; !explicit { if resolvedVal, err := globalConfig.Get(alias, key); err == nil && resolvedVal != "" { h.ResolvedProperties[key] = resolvedVal @@ -116,17 +116,17 @@ func (m *Manager) GetHosts() []*Host { } // Update resolved shortcuts. - if h.Name == "" && h.ResolvedProperties["HostName"] != "" { - h.Name = h.ResolvedProperties["HostName"] + if h.Name == "" && h.ResolvedProperties[keyHostName] != "" { + h.Name = h.ResolvedProperties[keyHostName] } - if h.User == "" && h.ResolvedProperties["User"] != "" { - h.User = h.ResolvedProperties["User"] + if h.User == "" && h.ResolvedProperties[keyUser] != "" { + h.User = h.ResolvedProperties[keyUser] } - if h.Port == "" && h.ResolvedProperties["Port"] != "" { - h.Port = h.ResolvedProperties["Port"] + if h.Port == "" && h.ResolvedProperties[keyPort] != "" { + h.Port = h.ResolvedProperties[keyPort] } - if h.IdentityFile == "" && h.ResolvedProperties["IdentityFile"] != "" { - h.IdentityFile = h.ResolvedProperties["IdentityFile"] + if h.IdentityFile == "" && h.ResolvedProperties[keyIdentityFile] != "" { + h.IdentityFile = h.ResolvedProperties[keyIdentityFile] } hosts = append(hosts, h) diff --git a/pkg/config/manager_test.go b/pkg/config/manager_test.go index 16ed4e9..5eef109 100644 --- a/pkg/config/manager_test.go +++ b/pkg/config/manager_test.go @@ -64,7 +64,7 @@ Host 10.200.1.46 assert.Equal(t, "~/.ssh/keys/work_rsa", prodHost.IdentityFile) // Verify prodHost inherited port 2222 from wildcard Host * assert.Equal(t, "2222", prodHost.ResolvedProperties["Port"]) - assert.Equal(t, "yes", prodHost.ResolvedProperties["ForwardAgent"]) + assert.Equal(t, "yes", prodHost.ResolvedProperties[keyForwardAgent]) // Verify dbHost does not have alias (its alias is the IP itself) assert.NotNil(t, dbHost) @@ -72,7 +72,7 @@ Host 10.200.1.46 // dbHost should inherit User, Port, and ForwardAgent from wildcard assert.Equal(t, "default_user", dbHost.User) assert.Equal(t, "2222", dbHost.Port) - assert.Equal(t, "yes", dbHost.ResolvedProperties["ForwardAgent"]) + assert.Equal(t, "yes", dbHost.ResolvedProperties[keyForwardAgent]) } // TestManagerIncludes tests glob inclusion and recursive parsing. @@ -130,7 +130,7 @@ Host my-host User: "admin", Port: "22", Properties: map[string]string{ - "ForwardAgent": "yes", + keyForwardAgent: "yes", }, } err = mgr.AddHost(primaryPath, newHost) @@ -153,7 +153,7 @@ Host my-host assert.Equal(t, "192.168.1.10", addedHost.Name) assert.Equal(t, "admin", addedHost.User) assert.Equal(t, "22", addedHost.Port) - assert.Equal(t, "yes", addedHost.Properties["ForwardAgent"]) + assert.Equal(t, "yes", addedHost.Properties[keyForwardAgent]) // 2. Update host updatedHost := &Host{ @@ -162,8 +162,8 @@ Host my-host User: "root", Port: "222", Properties: map[string]string{ - "ForwardAgent": "no", - "ProxyJump": "jump-box", + keyForwardAgent: "no", + "ProxyJump": "jump-box", }, } err = mgr2.UpdateHost("added-host", updatedHost) @@ -186,7 +186,7 @@ Host my-host assert.Equal(t, "192.168.1.15", foundUpdated.Name) assert.Equal(t, "root", foundUpdated.User) assert.Equal(t, "222", foundUpdated.Port) - assert.Equal(t, "no", foundUpdated.Properties["ForwardAgent"]) + assert.Equal(t, "no", foundUpdated.Properties[keyForwardAgent]) assert.Equal(t, "jump-box", foundUpdated.Properties["ProxyJump"]) // 3. Delete host diff --git a/pkg/tui/constants.go b/pkg/tui/constants.go new file mode 100644 index 0000000..d0f21ba --- /dev/null +++ b/pkg/tui/constants.go @@ -0,0 +1,9 @@ +package tui + +const ( + actionAdd = "add" + actionEdit = "edit" + tabAll = "All" + keyEsc = "esc" + keyEnter = "enter" +) diff --git a/pkg/tui/model.go b/pkg/tui/model.go index 619a8d1..6b7fdaa 100644 --- a/pkg/tui/model.go +++ b/pkg/tui/model.go @@ -23,14 +23,6 @@ const ( ModeCommand ) -const ( - actionAdd = "add" - actionEdit = "edit" - tabAll = "All" - keyEsc = "esc" - keyEnter = "enter" -) - // Model holds the state machine parameters for the Bubble Tea application loop. type Model struct { Manager *config.Manager From d63568f724645887ba4d30cef7ea1d17502584e7 Mon Sep 17 00:00:00 2001 From: Sem Van Broekhoven <144097969+dotsem@users.noreply.github.com> Date: Sun, 24 May 2026 21:23:15 +0200 Subject: [PATCH 7/8] audit on actions --- .github/workflows/{ci.yml => push.yml} | 26 +++++++++++++++++++------- .github/workflows/release.yml | 6 ++++-- .goreleaser.yaml | 3 ++- 3 files changed, 25 insertions(+), 10 deletions(-) rename .github/workflows/{ci.yml => push.yml} (79%) diff --git a/.github/workflows/ci.yml b/.github/workflows/push.yml similarity index 79% rename from .github/workflows/ci.yml rename to .github/workflows/push.yml index 44a5df2..67b45d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/push.yml @@ -10,6 +10,9 @@ on: pull_request: branches: ["*"] +permissions: + contents: read + jobs: lint: name: Static Code Analysis @@ -27,7 +30,7 @@ jobs: - name: Check Formatting (gofmt) run: | - unformatted=$(gofmt -l .) + unformatted=$(gofmt -s -l .) if [ -n "$unformatted" ]; then echo "The following files are not formatted correctly:" echo "$unformatted" @@ -40,11 +43,11 @@ jobs: uses: actions/cache@v4 with: path: ~/go/bin/golangci-lint - key: ${{ runner.os }}-golangci-lint-${{ steps.setup-go.outputs.go-version }} + key: ${{ runner.os }}-golangci-lint-v2.11.4-${{ steps.setup-go.outputs.go-version }} - name: Install golangci-lint if: steps.cache-golangci-lint.outputs.cache-hit != 'true' - run: go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest + run: go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.4 - name: Run golangci-lint run: $(go env GOPATH)/bin/golangci-lint run --config=.golangci.yml @@ -57,20 +60,29 @@ jobs: uses: actions/checkout@v4 - name: Set up Go + id: setup-go uses: actions/setup-go@v5 with: go-version: "1.26" cache: true + - name: Cache govulncheck binary + id: cache-govulncheck + uses: actions/cache@v4 + with: + path: ~/go/bin/govulncheck + key: ${{ runner.os }}-govulncheck-v1.1.3-${{ steps.setup-go.outputs.go-version }} + + - name: Install govulncheck + if: steps.cache-govulncheck.outputs.cache-hit != 'true' + run: go install golang.org/x/vuln/cmd/govulncheck@v1.1.3 + - name: Run govulncheck - run: | - go install golang.org/x/vuln/cmd/govulncheck@latest - govulncheck ./... + run: govulncheck ./... test: name: Build & Test runs-on: ubuntu-latest - needs: [lint, vulncheck] steps: - name: Checkout repository uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0a08a16..35198ba 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,11 +6,13 @@ on: - "v*" permissions: - contents: write + contents: read jobs: goreleaser: runs-on: ubuntu-latest + permissions: + contents: write steps: - name: Checkout repository uses: actions/checkout@v4 @@ -27,7 +29,7 @@ jobs: uses: goreleaser/goreleaser-action@v6 with: distribution: goreleaser - version: latest + version: v2.7.0 args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 5099b8d..77d7d2b 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -15,7 +15,8 @@ builds: goarch: - amd64 - arm64 - main: ./cmd/main.go + main: ./cmd/tusshi/main.go + binary: tusshi archives: - format: tar.gz From 5c874692fb623aef410798655fd9e22363b4c101 Mon Sep 17 00:00:00 2001 From: Sem Van Broekhoven <144097969+dotsem@users.noreply.github.com> Date: Sun, 24 May 2026 21:26:36 +0200 Subject: [PATCH 8/8] fix action failing --- .github/workflows/push.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 67b45d1..ac4f6ff 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -43,11 +43,11 @@ jobs: uses: actions/cache@v4 with: path: ~/go/bin/golangci-lint - key: ${{ runner.os }}-golangci-lint-v2.11.4-${{ steps.setup-go.outputs.go-version }} + key: ${{ runner.os }}-golangci-lint-latest-${{ steps.setup-go.outputs.go-version }} - name: Install golangci-lint if: steps.cache-golangci-lint.outputs.cache-hit != 'true' - run: go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.4 + run: go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest - name: Run golangci-lint run: $(go env GOPATH)/bin/golangci-lint run --config=.golangci.yml @@ -71,11 +71,11 @@ jobs: uses: actions/cache@v4 with: path: ~/go/bin/govulncheck - key: ${{ runner.os }}-govulncheck-v1.1.3-${{ steps.setup-go.outputs.go-version }} + key: ${{ runner.os }}-govulncheck-latest-${{ steps.setup-go.outputs.go-version }} - name: Install govulncheck if: steps.cache-govulncheck.outputs.cache-hit != 'true' - run: go install golang.org/x/vuln/cmd/govulncheck@v1.1.3 + run: go install golang.org/x/vuln/cmd/govulncheck@latest - name: Run govulncheck run: govulncheck ./...