diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3286fe5..50fed7a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: '1.24.x' + go-version: '1.26.x' cache: true - name: Download dependencies diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2262944..4386f2d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - go-version: ['1.24.x'] + go-version: ['1.26.x'] steps: - name: Checkout code @@ -44,11 +44,11 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: '1.24.x' + go-version: '1.26.x' cache: true - name: Run golangci-lint - uses: golangci/golangci-lint-action@v6 + uses: golangci/golangci-lint-action@v9 with: - version: latest + version: 'v2.10.1' args: --timeout=5m diff --git a/.gitignore b/.gitignore index ac1e20c..5e015dd 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ *.dll *.so *.dylib +.conjure # Test binary, built with `go test -c` *.test diff --git a/cmd/bundle/bundle_test.go b/cmd/bundle/bundle_test.go old mode 100644 new mode 100755 index c16a9b5..1ef18e7 --- a/cmd/bundle/bundle_test.go +++ b/cmd/bundle/bundle_test.go @@ -273,7 +273,7 @@ func createTempFile(pattern, content string) (string, error) { if err != nil { return "", err } - defer tmpFile.Close() + defer func() { _ = tmpFile.Close() }() _, err = tmpFile.WriteString(content) if err != nil { @@ -284,5 +284,5 @@ func createTempFile(pattern, content string) (string, error) { } func cleanupTempFile(path string) { - os.Remove(path) + _ = os.Remove(path) } diff --git a/cmd/list/list.go b/cmd/list/list.go old mode 100644 new mode 100755 index a19a9dd..752d144 --- a/cmd/list/list.go +++ b/cmd/list/list.go @@ -15,6 +15,7 @@ Example: conjure list templates, conjure list bundles, conjure list templates -t yaml, conjure list bundles -t kubernetes`, + Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { fmt.Println("=== Templates ===") listTemplates("", false) diff --git a/cmd/list/list_bundles.go b/cmd/list/list_bundles.go old mode 100644 new mode 100755 index fe968d0..c9a4016 --- a/cmd/list/list_bundles.go +++ b/cmd/list/list_bundles.go @@ -26,6 +26,7 @@ Examples: conjure list bundles conjure list bundles --versions conjure list bundles --type kubernetes`, + Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { bundleType, _ := cmd.Flags().GetString("type") showAllVersions, _ := cmd.Flags().GetBool("versions") diff --git a/cmd/list/list_templates.go b/cmd/list/list_templates.go old mode 100644 new mode 100755 index 6f7fb7d..ea17b16 --- a/cmd/list/list_templates.go +++ b/cmd/list/list_templates.go @@ -26,6 +26,7 @@ Examples: conjure list templates conjure list templates --versions conjure list templates --type yaml`, + Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { templateType, _ := cmd.Flags().GetString("type") showAllVersions, _ := cmd.Flags().GetBool("versions") diff --git a/cmd/root.go b/cmd/root.go old mode 100644 new mode 100755 index 61d89f4..a0c81a8 --- a/cmd/root.go +++ b/cmd/root.go @@ -46,7 +46,7 @@ Examples: fmt.Printf("built: %s\n", dateInfo) return } - cmd.Help() + _ = cmd.Help() }, PersistentPreRun: func(cmd *cobra.Command, args []string) { if showVersion { diff --git a/cmd/root_test.go b/cmd/root_test.go old mode 100644 new mode 100755 index c04e715..eedbfac --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -108,7 +108,7 @@ func TestResolveConfigPathRelative(t *testing.T) { if err != nil { t.Fatalf("failed to get original working directory: %v", err) } - defer os.Chdir(originalWd) + defer func() { _ = os.Chdir(originalWd) }() if err := os.Chdir(subDir); err != nil { t.Fatalf("failed to change to subdirectory: %v", err) @@ -123,6 +123,14 @@ func TestResolveConfigPathRelative(t *testing.T) { expectedPath := filepath.Clean(configFile) resolvedPath = filepath.Clean(resolvedPath) + // Resolve symlinks on both sides to handle macOS where /var -> /private/var + if evaled, err := filepath.EvalSymlinks(expectedPath); err == nil { + expectedPath = evaled + } + if evaled, err := filepath.EvalSymlinks(resolvedPath); err == nil { + resolvedPath = evaled + } + if resolvedPath != expectedPath { t.Errorf("expected path %q, got %q", expectedPath, resolvedPath) } @@ -161,7 +169,7 @@ func TestResolveConfigPathHomeDirExpansion(t *testing.T) { t.Skipf("failed to create test file in home directory: %v", err) return } - defer os.Remove(testFile) + defer func() { _ = os.Remove(testFile) }() resolvedPath, err := resolveConfigPath(tt.inputPath) if err != nil { diff --git a/cmd/template/template_test.go b/cmd/template/template_test.go old mode 100644 new mode 100755 index 28aff49..914ba61 --- a/cmd/template/template_test.go +++ b/cmd/template/template_test.go @@ -235,7 +235,7 @@ func createTempFile(pattern, content string) (string, error) { if err != nil { return "", err } - defer tmpFile.Close() + defer func() { _ = tmpFile.Close() }() _, err = tmpFile.WriteString(content) if err != nil { @@ -246,5 +246,5 @@ func createTempFile(pattern, content string) (string, error) { } func cleanupTempFile(path string) { - os.Remove(path) + _ = os.Remove(path) } diff --git a/go.mod b/go.mod old mode 100644 new mode 100755 index 1bd7464..b2091d4 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module github.com/wizardopstech/conjure -go 1.24.0 - -toolchain go1.24.7 +go 1.26.0 require ( github.com/charmbracelet/bubbletea v1.3.10 diff --git a/internal/indexer/indexer_test.go b/internal/indexer/indexer_test.go old mode 100644 new mode 100755 index 21470ef..6e5e182 --- a/internal/indexer/indexer_test.go +++ b/internal/indexer/indexer_test.go @@ -252,7 +252,7 @@ func TestIndexer_ValidateStructure(t *testing.T) { setupFunc: func(testDir string) string { templatesDir := filepath.Join(testDir, "templates") templateDir := filepath.Join(templatesDir, "test-template", "1.0.0") - os.MkdirAll(templateDir, 0755) + _ = os.MkdirAll(templateDir, 0755) metadata := `{ "schema_version": "v1", "template_name": "test-template", @@ -261,8 +261,8 @@ func TestIndexer_ValidateStructure(t *testing.T) { "version": "1.0.0", "variables": [] }` - os.WriteFile(filepath.Join(templateDir, "conjure.json"), []byte(metadata), 0600) - os.WriteFile(filepath.Join(templateDir, "template.tmpl"), []byte("test"), 0600) + _ = os.WriteFile(filepath.Join(templateDir, "conjure.json"), []byte(metadata), 0600) + _ = os.WriteFile(filepath.Join(templateDir, "template.tmpl"), []byte("test"), 0600) return templatesDir }, resourceType: "templates", @@ -273,8 +273,8 @@ func TestIndexer_ValidateStructure(t *testing.T) { setupFunc: func(testDir string) string { templatesDir := filepath.Join(testDir, "templates") templateDir := filepath.Join(templatesDir, "test-template", "1.0.0") - os.MkdirAll(templateDir, 0755) - os.WriteFile(filepath.Join(templateDir, "template.tmpl"), []byte("test"), 0600) + _ = os.MkdirAll(templateDir, 0755) + _ = os.WriteFile(filepath.Join(templateDir, "template.tmpl"), []byte("test"), 0600) return templatesDir }, resourceType: "templates", @@ -284,7 +284,7 @@ func TestIndexer_ValidateStructure(t *testing.T) { name: "no versions", setupFunc: func(testDir string) string { templatesDir := filepath.Join(testDir, "templates") - os.MkdirAll(filepath.Join(templatesDir, "test-template"), 0755) + _ = os.MkdirAll(filepath.Join(templatesDir, "test-template"), 0755) return templatesDir }, resourceType: "templates", diff --git a/internal/source/remote.go b/internal/source/remote.go old mode 100644 new mode 100755 index aae91b7..9dc47d0 --- a/internal/source/remote.go +++ b/internal/source/remote.go @@ -73,7 +73,7 @@ func (r *RemoteSource) fetchIndex(skipCache bool) error { if err != nil { return fmt.Errorf("failed to fetch index: %w", err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { return fmt.Errorf("failed to fetch index: HTTP %d", resp.StatusCode) @@ -219,7 +219,7 @@ func (r *RemoteSource) GetTemplate(name, requestedVersion string) (*TemplateCont if err != nil { return nil, fmt.Errorf("failed to download file '%s': %w", file.Name, err) } - defer os.Remove(tempFile) + defer func(f string) { _ = os.Remove(f) }(tempFile) content, err := os.ReadFile(tempFile) if err != nil { @@ -264,6 +264,7 @@ func (r *RemoteSource) GetBundle(name, requestedVersion string) (*BundleContent, } if err := r.fetchIndex(false); err != nil { + return nil, fmt.Errorf("failed to fetch bundle index: %w", err) } var bundleEntry *BundleIndexEntry @@ -327,7 +328,7 @@ func (r *RemoteSource) GetBundle(name, requestedVersion string) (*BundleContent, if err != nil { return nil, fmt.Errorf("failed to download file '%s': %w", file.Name, err) } - defer os.Remove(tempFile) + defer func(f string) { _ = os.Remove(f) }(tempFile) content, err := os.ReadFile(tempFile) if err != nil { @@ -415,7 +416,7 @@ func (r *RemoteSource) downloadFile(url string, expectedSize int64, expectedHash if err != nil { return "", fmt.Errorf("failed to download: %w", err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("failed to download: HTTP %d", resp.StatusCode) @@ -428,27 +429,27 @@ func (r *RemoteSource) downloadFile(url string, expectedSize int64, expectedHash tempPath := tempFile.Name() written, err := io.Copy(tempFile, resp.Body) - tempFile.Close() + _ = tempFile.Close() if err != nil { - os.Remove(tempPath) + _ = os.Remove(tempPath) return "", fmt.Errorf("failed to write file: %w", err) } if written != expectedSize { - os.Remove(tempPath) + _ = os.Remove(tempPath) return "", fmt.Errorf("file size mismatch: expected %d bytes, got %d bytes. This likely means index.json was generated with different line endings (Windows CRLF vs Linux LF) than the files on the remote repository", expectedSize, written) } if expectedHash != "" { if err := r.verifier.VerifySHA256(tempPath, expectedHash); err != nil { - os.Remove(tempPath) + _ = os.Remove(tempPath) return "", fmt.Errorf("%w. This means the file content on the remote repository doesn't match what was used to generate index.json", err) } } if err := os.Chmod(tempPath, cacheFilePerm); err != nil { - os.Remove(tempPath) + _ = os.Remove(tempPath) return "", fmt.Errorf("failed to set file permissions: %w", err) } diff --git a/internal/source/remote_test.go b/internal/source/remote_test.go old mode 100644 new mode 100755 index 23f7988..e3b5090 --- a/internal/source/remote_test.go +++ b/internal/source/remote_test.go @@ -70,7 +70,7 @@ func createMockServer() *httptest.Server { } w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(index) + _ = json.NewEncoder(w).Encode(index) }) mux.HandleFunc("/templates/test-template/1.0.0/conjure.json", func(w http.ResponseWriter, r *http.Request) { @@ -82,11 +82,11 @@ func createMockServer() *httptest.Server { "template_type": "yaml", "variables": []interface{}{}, } - json.NewEncoder(w).Encode(metadata) + _ = json.NewEncoder(w).Encode(metadata) }) mux.HandleFunc("/templates/test-template/1.0.0/template.tmpl", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("template content")) + _, _ = w.Write([]byte("template content")) }) mux.HandleFunc("/bundles/test-bundle/1.0.0/conjure.json", func(w http.ResponseWriter, r *http.Request) { @@ -99,11 +99,11 @@ func createMockServer() *httptest.Server { "shared_variables": []interface{}{}, "template_variables": map[string]interface{}{}, } - json.NewEncoder(w).Encode(metadata) + _ = json.NewEncoder(w).Encode(metadata) }) mux.HandleFunc("/bundles/test-bundle/1.0.0/app.yaml.tmpl", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("app template content")) + _, _ = w.Write([]byte("app template content")) }) return httptest.NewServer(mux) @@ -342,7 +342,7 @@ func TestRemoteSource_InvalidIndex(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/index.json" { - w.Write([]byte("invalid json")) + _, _ = w.Write([]byte("invalid json")) } })) defer server.Close() @@ -367,7 +367,7 @@ func TestRemoteSource_UnsupportedSchemaVersion(t *testing.T) { SchemaVersion: "v99", Templates: []TemplateIndexEntry{}, } - json.NewEncoder(w).Encode(index) + _ = json.NewEncoder(w).Encode(index) } })) defer server.Close() diff --git a/internal/source/verifier.go b/internal/source/verifier.go old mode 100644 new mode 100755 index 61adcab..fc6f792 --- a/internal/source/verifier.go +++ b/internal/source/verifier.go @@ -30,7 +30,7 @@ func (v *Verifier) ComputeSHA256(filePath string) (string, error) { if err != nil { return "", fmt.Errorf("failed to open file: %w", err) } - defer file.Close() + defer func() { _ = file.Close() }() hash := sha256.New() if _, err := io.Copy(hash, file); err != nil {