Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
run: go install github.com/magefile/mage@latest

- name: Install golangci-lint
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: Install staticcheck
run: go install honnef.co/go/tools/cmd/staticcheck@latest
Expand Down Expand Up @@ -166,7 +166,7 @@ jobs:
run: go version

- name: Install golangci-lint
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: golangci-lint run --timeout=5m
Expand Down
97 changes: 46 additions & 51 deletions cli/.golangci.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
version: "2"

run:
timeout: 5m
tests: true
modules-download-mode: readonly

formatters:
enable:
- gofmt # Check for code formatting
- goimports # Check for import formatting
settings:
gofmt:
simplify: true

linters:
enable:
- errcheck # Check for unchecked errors
- gosimple # Simplify code
- govet # Vet examines Go source code
- ineffassign # Detect ineffectual assignments
- staticcheck # Go static analysis
- unused # Check for unused code
- gofmt # Check for code formatting
- goimports # Check for import formatting
- misspell # Check for misspelled words
- revive # Drop-in replacement for golint
- typecheck # Type-check Go code
- unparam # Report unused function parameters
- unconvert # Remove unnecessary type conversions
- goconst # Find repeated strings that could be constants
Expand All @@ -26,60 +31,50 @@ linters:
- gosec # Security checker for Go code
- bodyclose # Check for HTTP response body close
- gocritic # Comprehensive Go source code linter
settings:
errcheck:
check-type-assertions: true
check-blank: false # Allow _ = in tests
exclude-functions:
- (os).RemoveAll # Defer cleanup in tests is OK to ignore

gosec:
excludes:
- G204 # Subprocess launched with variable - needed for script execution
- G306 # File permissions - we need executable scripts

linters-settings:
errcheck:
check-type-assertions: true
check-blank: false # Allow _ = in tests
exclude-functions:
- (os).RemoveAll # Defer cleanup in tests is OK to ignore

gosec:
excludes:
- G204 # Subprocess launched with variable - needed for script execution
- G306 # File permissions - we need executable scripts
govet:
enable-all: true
disable:
- shadow # Disable shadow checking as it can be noisy
- fieldalignment # Disable struct field alignment - minor optimization

gofmt:
simplify: true
revive:
rules:
- name: exported
arguments:
- "checkPrivateReceivers"
- "disableStutteringCheck"

govet:
enable-all: true
disable:
- shadow # Disable shadow checking as it can be noisy
- fieldalignment # Disable struct field alignment - minor optimization
misspell:
locale: US

revive:
rules:
- name: exported
arguments:
- "checkPrivateReceivers"
- "disableStutteringCheck"

misspell:
locale: US

dupl:
threshold: 100
dupl:
threshold: 200

goconst:
min-len: 3
min-occurrences: 3

issues:
exclude-use-default: false
max-issues-per-linter: 0
max-same-issues: 0

# Exclude some linters from running on tests files
exclude-rules:
- path: _test\.go
linters:
- dupl
- goconst
goconst:
min-len: 3
min-occurrences: 3
exclusions:
rules:
- path: '(.+)_test\.go'
linters:
- dupl
- goconst

output:
formats:
- format: colored-line-number
colored-line-number:
path: stdout
print-issued-lines: true
print-linter-name: true
2 changes: 1 addition & 1 deletion cli/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/jongio/azd-exec/cli
go 1.26.0

require (
github.com/jongio/azd-core v0.5.0
github.com/jongio/azd-core v0.5.1
github.com/magefile/mage v1.15.0
github.com/spf13/cobra v1.10.2
)
Expand Down
4 changes: 2 additions & 2 deletions cli/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jongio/azd-core v0.5.0 h1:QjV7lLz/IoXPmT/LZ05nYPgB/wU8uJK2Wg8T+SGss+M=
github.com/jongio/azd-core v0.5.0/go.mod h1:d6S8InR9GR0Aw1+y6kvEVZoMoSqoACsckj/2mNT6nf0=
github.com/jongio/azd-core v0.5.1 h1:xrAWyRIjZFVF0EOTgwjEbcMzU8wpvI1xvp6pqiDhHxU=
github.com/jongio/azd-core v0.5.1/go.mod h1:jQCP+px3Pxb3B0fyShfvSVa3KsWT1j2jGXMsPpQezlI=
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
Expand Down
59 changes: 2 additions & 57 deletions cli/src/cmd/exec/commands/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,67 +2,12 @@
package commands

import (
"encoding/json"
"fmt"

"github.com/jongio/azd-core/cliout"
coreversion "github.com/jongio/azd-core/version"
"github.com/jongio/azd-exec/cli/src/internal/version"
"github.com/spf13/cobra"
)

// NewVersionCommand creates a new version command that displays extension version information.
// The command supports both human-readable and JSON output formats.
// The outputFormat parameter controls the output style via the --output/-o flag.
func NewVersionCommand(outputFormat *string) *cobra.Command {
var quiet bool

cmd := &cobra.Command{
Use: "version",
Short: "Display the extension version",
Long: `Display the version information for the azd exec extension.`,
Run: func(cmd *cobra.Command, args []string) {
// Set output format from flag
if *outputFormat == "json" {
if err := cliout.SetFormat("json"); err != nil {
cliout.Error("Failed to set output format: %v", err)
return
}
} else {
if err := cliout.SetFormat("default"); err != nil {
cliout.Error("Failed to set output format: %v", err)
return
}
}

if cliout.IsJSON() {
// JSON output mode
output := map[string]string{
"version": version.Version,
}
data, err := json.MarshalIndent(output, "", " ")
if err != nil {
cliout.Error("Error formatting JSON: %v", err)
return
}
fmt.Println(string(data))
} else {
// Human-readable output with colors
if quiet {
fmt.Println(version.Version)
} else {
cliout.Header("azd exec")
cliout.Label("Version", version.Version)
if version.BuildDate != "unknown" {
cliout.Label("Build Date", version.BuildDate)
}
if version.GitCommit != "unknown" {
cliout.Label("Git Commit", version.GitCommit)
}
}
}
},
}

cmd.Flags().BoolVarP(&quiet, "quiet", "q", false, "Display only the version number")
return cmd
return coreversion.NewCommand(version.Info, outputFormat)
}
20 changes: 11 additions & 9 deletions cli/src/internal/executor/command_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@ package executor
import (
"os/exec"
"strings"

"github.com/jongio/azd-core/shellutil"
)

// validShells maps known shell names to whether they're valid.
// This is used for validation and shell-specific argument construction.
var validShells = map[string]bool{
shellBash: true,
shellSh: true,
shellZsh: true,
shellPwsh: true,
shellPowerShell: true,
shellCmd: true,
shellutil.ShellBash: true,
shellutil.ShellSh: true,
shellutil.ShellZsh: true,
shellutil.ShellPwsh: true,
shellutil.ShellPowerShell: true,
shellutil.ShellCmd: true,
}

// buildCommand builds the exec.Cmd for the given shell and script.
Expand All @@ -32,20 +34,20 @@ func (e *Executor) buildCommand(shell, scriptOrPath string, isInline bool) *exec
shellLower := strings.ToLower(shell)

switch shellLower {
case shellBash, shellSh, shellZsh:
case shellutil.ShellBash, shellutil.ShellSh, shellutil.ShellZsh:
if isInline {
cmdArgs = []string{shell, "-c", scriptOrPath}
} else {
cmdArgs = []string{shell, scriptOrPath}
}
case shellPwsh, shellPowerShell:
case shellutil.ShellPwsh, shellutil.ShellPowerShell:
if isInline {
cmdArgs = []string{shell, "-Command", e.buildPowerShellInlineCommand(scriptOrPath)}
skipAppendArgs = true
} else {
cmdArgs = []string{shell, "-File", scriptOrPath}
}
case shellCmd:
case shellutil.ShellCmd:
cmdArgs = []string{shell, "/c", scriptOrPath}
default:
// Unknown shell: use Unix-like -c pattern as fallback
Expand Down
32 changes: 0 additions & 32 deletions cli/src/internal/executor/constants.go

This file was deleted.

6 changes: 4 additions & 2 deletions cli/src/internal/executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
"github.com/jongio/azd-core/shellutil"
)

const osWindows = "windows"

// Config holds the configuration for script execution.
// All fields are optional and have sensible defaults.
type Config struct {
Expand Down Expand Up @@ -252,7 +254,7 @@ func (e *Executor) hasKeyVaultReferences(envVars []string) bool {
// getDefaultShellForOS returns the default shell for the current operating system.
func getDefaultShellForOS() string {
if runtime.GOOS == osWindows {
return shellPowerShell
return shellutil.ShellPowerShell
}
return shellBash
return shellutil.ShellBash
}
8 changes: 4 additions & 4 deletions cli/src/internal/executor/executor_coverage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func TestExecute_FileValidation(t *testing.T) {

// Use OS-appropriate scripts to avoid Windows path translation issues.
scriptPath := filepath.Join(projectsDir, "bash", "simple.sh")
if runtime.GOOS == osWindows {
if runtime.GOOS == "windows" {
scriptPath = filepath.Join(projectsDir, "powershell", "simple.ps1")
}

Expand Down Expand Up @@ -186,8 +186,8 @@ func TestRunCommand_ErrorHandling(t *testing.T) {
exitCmd := "exit 1"
missingCmd := "nonexistent-command-xyz"
missingFile := "/nonexistent/file.sh"
if runtime.GOOS == osWindows {
shell = shellCmd
if runtime.GOOS == "windows" {
shell = "cmd"
exitCmd = "exit /b 1"
missingCmd = "nonexistent-command-xyz"
missingFile = "C:\\nonexistent\\file.cmd"
Expand Down Expand Up @@ -243,7 +243,7 @@ func TestExecutorWithArgs(t *testing.T) {

scriptPath := filepath.Join(projectsDir, "bash", "with-args.sh")
args := []string{"arg1", "arg2"}
if runtime.GOOS == osWindows {
if runtime.GOOS == "windows" {
scriptPath = filepath.Join(projectsDir, "powershell", "with-params.ps1")
args = []string{"-Name", "Test", "-Greeting", "Hi"}
}
Expand Down
Loading
Loading