diff --git a/internal/adapters/primary/cli/new.go b/internal/adapters/primary/cli/new.go
index 537212b..3dd96ed 100644
--- a/internal/adapters/primary/cli/new.go
+++ b/internal/adapters/primary/cli/new.go
@@ -22,8 +22,8 @@ const (
)
var (
- projectType string
- quietNew bool
+ projectType string //nolint:gochecknoglobals // cobra flag variable
+ quietNew bool //nolint:gochecknoglobals // cobra flag variable
)
const dprTemplate = `program %s;
@@ -120,7 +120,8 @@ const dprojTemplate = `
-
+
`
@@ -138,16 +139,16 @@ func generateGUID() string {
// newCmdRegister registers the new command.
func newCmdRegister(root *cobra.Command) {
var newCmd = &cobra.Command{
- Use: "new [project_name]",
- Short: "Create a new Delphi project skeleton",
- Long: "Create a new Delphi project skeleton with source directories, templates, and boss.json",
- Args: cobra.MaximumNArgs(1),
+ Use: "new [project_name]",
+ Short: "Create a new Delphi project skeleton",
+ Long: "Create a new Delphi project skeleton with source directories, templates, and boss.json",
+ Args: cobra.MaximumNArgs(1),
Example: ` Create a new console application:
boss new my_project
Create a new package/library:
boss new my_package --type pkg`,
- Run: func(cmd *cobra.Command, args []string) {
+ Run: func(_ *cobra.Command, args []string) {
var name string
if len(args) > 0 {
name = args[0]
@@ -194,9 +195,11 @@ func doCreateProject(name string, pType string, quiet bool) {
// Create directories
srcDir := filepath.Join(projectDir, "src")
testsDir := filepath.Join(projectDir, "tests")
+ //nolint:gosec // Standard directory permissions
if err := os.MkdirAll(srcDir, 0755); err != nil {
msg.Die("❌ Failed to create src directory: %v", err)
}
+ //nolint:gosec // Standard directory permissions
if err := os.MkdirAll(testsDir, 0755); err != nil {
msg.Die("❌ Failed to create tests directory: %v", err)
}
@@ -207,8 +210,8 @@ func doCreateProject(name string, pType string, quiet bool) {
packageData.Version = defaultPackageVersion
packageData.MainSrc = "src"
- packageJsonPath := filepath.Join(projectDir, consts.FilePackage)
- if err := pkgmanager.SavePackage(packageData, packageJsonPath); err != nil {
+ packageJSONPath := filepath.Join(projectDir, consts.FilePackage)
+ if err := pkgmanager.SavePackage(packageData, packageJSONPath); err != nil {
msg.Die("❌ Failed to save boss.json: %v", err)
}
diff --git a/internal/adapters/primary/cli/new_test.go b/internal/adapters/primary/cli/new_test.go
index c116f57..7800002 100644
--- a/internal/adapters/primary/cli/new_test.go
+++ b/internal/adapters/primary/cli/new_test.go
@@ -39,9 +39,11 @@ func TestNewCommandRegistration(t *testing.T) {
}
typeFlag := newCmd.Flags().Lookup("type")
+ //nolint:staticcheck // Test intentionally keeps non-fatal assertion before the follow-up check
if typeFlag == nil {
t.Error("New command should have --type flag")
}
+ //nolint:staticcheck // Test handles nil case with t.Error
if typeFlag.DefValue != "app" {
t.Errorf("New command --type flag default value should be 'app', got %s", typeFlag.DefValue)
}
@@ -55,15 +57,9 @@ func TestNewCommandRegistration(t *testing.T) {
// TestDoCreateProject_App tests bootstrapping an application project.
func TestDoCreateProject_App(t *testing.T) {
tempDir := t.TempDir()
+ t.Chdir(tempDir)
- // Redirect working directory
- oldWd, err := os.Getwd()
- if err == nil {
- defer func() { _ = os.Chdir(oldWd) }()
- }
- if err := os.Chdir(tempDir); err != nil {
- t.Fatalf("Failed to change directory: %v", err)
- }
+ var err error
// Initialize package manager
fs := filesystem.NewOSFileSystem()
@@ -76,31 +72,31 @@ func TestDoCreateProject_App(t *testing.T) {
doCreateProject(projectName, "app", true)
projectPath := filepath.Join(tempDir, projectName)
- if _, err := os.Stat(projectPath); os.IsNotExist(err) {
+ if _, statErr := os.Stat(projectPath); os.IsNotExist(statErr) {
t.Fatalf("Project directory was not created")
}
// Check folders
- if _, err := os.Stat(filepath.Join(projectPath, "src")); os.IsNotExist(err) {
+ if _, statErr := os.Stat(filepath.Join(projectPath, "src")); os.IsNotExist(statErr) {
t.Error("src directory was not created")
}
- if _, err := os.Stat(filepath.Join(projectPath, "tests")); os.IsNotExist(err) {
+ if _, statErr := os.Stat(filepath.Join(projectPath, "tests")); os.IsNotExist(statErr) {
t.Error("tests directory was not created")
}
// Check boss.json
- bossJsonPath := filepath.Join(projectPath, consts.FilePackage)
- if _, err := os.Stat(bossJsonPath); os.IsNotExist(err) {
+ bossJSONPath := filepath.Join(projectPath, consts.FilePackage)
+ if _, statErr := os.Stat(bossJSONPath); os.IsNotExist(statErr) {
t.Fatal("boss.json was not created")
}
- bossBytes, err := os.ReadFile(bossJsonPath)
+ bossBytes, err := os.ReadFile(bossJSONPath)
if err != nil {
t.Fatalf("Failed to read boss.json: %v", err)
}
var pkg domain.Package
- if err := json.Unmarshal(bossBytes, &pkg); err != nil {
+ if err = json.Unmarshal(bossBytes, &pkg); err != nil {
t.Fatalf("Failed to parse boss.json: %v", err)
}
@@ -116,7 +112,7 @@ func TestDoCreateProject_App(t *testing.T) {
// Check .dpr file
dprPath := filepath.Join(projectPath, projectName+".dpr")
- if _, err := os.Stat(dprPath); os.IsNotExist(err) {
+ if _, statErr := os.Stat(dprPath); os.IsNotExist(statErr) {
t.Fatal(".dpr file was not created")
}
@@ -131,7 +127,7 @@ func TestDoCreateProject_App(t *testing.T) {
// Check .dproj file
dprojPath := filepath.Join(projectPath, projectName+".dproj")
- if _, err := os.Stat(dprojPath); os.IsNotExist(err) {
+ if _, statErr := os.Stat(dprojPath); os.IsNotExist(statErr) {
t.Fatal(".dproj file was not created")
}
@@ -151,15 +147,9 @@ func TestDoCreateProject_App(t *testing.T) {
// TestDoCreateProject_Pkg tests bootstrapping a package project.
func TestDoCreateProject_Pkg(t *testing.T) {
tempDir := t.TempDir()
+ t.Chdir(tempDir)
- // Redirect working directory
- oldWd, err := os.Getwd()
- if err == nil {
- defer func() { _ = os.Chdir(oldWd) }()
- }
- if err := os.Chdir(tempDir); err != nil {
- t.Fatalf("Failed to change directory: %v", err)
- }
+ var err error
// Initialize package manager
fs := filesystem.NewOSFileSystem()
@@ -172,13 +162,13 @@ func TestDoCreateProject_Pkg(t *testing.T) {
doCreateProject(projectName, "pkg", true)
projectPath := filepath.Join(tempDir, projectName)
- if _, err := os.Stat(projectPath); os.IsNotExist(err) {
+ if _, statErr := os.Stat(projectPath); os.IsNotExist(statErr) {
t.Fatalf("Project directory was not created")
}
// Check .dpk file
dpkPath := filepath.Join(projectPath, projectName+".dpk")
- if _, err := os.Stat(dpkPath); os.IsNotExist(err) {
+ if _, statErr := os.Stat(dpkPath); os.IsNotExist(statErr) {
t.Fatal(".dpk file was not created")
}
@@ -193,7 +183,7 @@ func TestDoCreateProject_Pkg(t *testing.T) {
// Check .dproj file
dprojPath := filepath.Join(projectPath, projectName+".dproj")
- if _, err := os.Stat(dprojPath); os.IsNotExist(err) {
+ if _, statErr := os.Stat(dprojPath); os.IsNotExist(statErr) {
t.Fatal(".dproj file was not created")
}
diff --git a/internal/core/services/compiler/executor.go b/internal/core/services/compiler/executor.go
index cc29034..ae9d80b 100644
--- a/internal/core/services/compiler/executor.go
+++ b/internal/core/services/compiler/executor.go
@@ -75,11 +75,13 @@ func compileLazarus(lazarusPath string, tracker *BuildTracker) bool {
absPath, _ := filepath.Abs(lazarusPath)
absDir := filepath.Dir(absPath)
- cmd := exec.Command("lazbuild", "--build-mode=Debug", absPath)
+ // #nosec G204 -- Controlled lazbuild command
+ cmd := exec.CommandContext(context.Background(), "lazbuild", "--build-mode=Debug", absPath)
cmd.Dir = absDir
baseName := strings.TrimSuffix(filepath.Base(lazarusPath), filepath.Ext(lazarusPath))
buildLog := filepath.Join(absDir, "build_boss_"+baseName+".log")
+ // #nosec G304 -- Controlled build log path
logFile, err := os.OpenFile(buildLog, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
if err != nil {
if tracker == nil || !tracker.IsEnabled() {
@@ -87,14 +89,14 @@ func compileLazarus(lazarusPath string, tracker *BuildTracker) bool {
}
return false
}
- defer logFile.Close()
+ defer func() { _ = logFile.Close() }()
cmd.Stdout = logFile
cmd.Stderr = logFile
if err := cmd.Run(); err != nil {
if tracker == nil || !tracker.IsEnabled() {
- msg.Err(" ❌ Failed to compile, see " + buildLog + " for more information: %v", err)
+ msg.Err(" ❌ Failed to compile, see "+buildLog+" for more information: %v", err)
}
return false
}
@@ -107,7 +109,7 @@ func compileLazarus(lazarusPath string, tracker *BuildTracker) bool {
return true
}
-//nolint:funlen,gocognit,lll // Complex compilation orchestration with long function signature
+//nolint:funlen,gocognit,gocyclo,cyclop,lll // Complex compilation orchestration
func compile(dprojPath string, dep *domain.Dependency, rootLock domain.PackageLock, tracker *BuildTracker, selectedCompiler *compilerselector.SelectedCompiler) bool {
ext := strings.ToLower(filepath.Ext(dprojPath))
if ext == ".lpi" || ext == ".lpk" {
diff --git a/internal/core/services/compilerselector/selector.go b/internal/core/services/compilerselector/selector.go
index e6052e8..cbce618 100644
--- a/internal/core/services/compilerselector/selector.go
+++ b/internal/core/services/compilerselector/selector.go
@@ -96,7 +96,10 @@ func resolveTargetPlatform(ctx SelectionContext) string {
}
}
-func findGlobalPathCompiler(installations []registryadapter.DelphiInstallation, globalPath, targetPlatform string) (*SelectedCompiler, error) {
+func findGlobalPathCompiler(
+ installations []registryadapter.DelphiInstallation,
+ globalPath, targetPlatform string,
+) (*SelectedCompiler, error) {
for _, inst := range installations {
instDir := filepath.Dir(inst.Path)
if strings.EqualFold(instDir, globalPath) && strings.EqualFold(inst.Arch, targetPlatform) {
@@ -118,7 +121,10 @@ func findGlobalPathCompiler(installations []registryadapter.DelphiInstallation,
}, nil
}
-func findLatestCompiler(installations []registryadapter.DelphiInstallation, targetPlatform string) (*SelectedCompiler, error) {
+func findLatestCompiler(
+ installations []registryadapter.DelphiInstallation,
+ targetPlatform string,
+) (*SelectedCompiler, error) {
var bestMatch *registryadapter.DelphiInstallation
for _, inst := range installations {
if strings.EqualFold(inst.Arch, targetPlatform) {
diff --git a/internal/core/services/compilerselector/selector_test.go b/internal/core/services/compilerselector/selector_test.go
index f60d22d..f8b5623 100644
--- a/internal/core/services/compilerselector/selector_test.go
+++ b/internal/core/services/compilerselector/selector_test.go
@@ -10,7 +10,7 @@ import (
"github.com/hashload/boss/pkg/env"
)
-// mockRegistryAdapter is a mock for compilerselector.RegistryAdapter
+// mockRegistryAdapter is a mock for compilerselector.RegistryAdapter.
type mockRegistryAdapter struct {
installations []registryadapter.DelphiInstallation
}
@@ -19,7 +19,7 @@ func (m *mockRegistryAdapter) GetDetectedDelphis() []registryadapter.DelphiInsta
return m.installations
}
-// mockConfigProvider embeds env.ConfigProvider to mock only GetDelphiPath
+// mockConfigProvider embeds env.ConfigProvider to mock only GetDelphiPath.
type mockConfigProvider struct {
env.ConfigProvider
delphiPath string
diff --git a/internal/core/services/installer/utils.go b/internal/core/services/installer/utils.go
index ce8eca2..5b994c4 100644
--- a/internal/core/services/installer/utils.go
+++ b/internal/core/services/installer/utils.go
@@ -19,7 +19,7 @@ var (
)
// parseURLAndVersion parses the dependency URL and version, handling SSH git@ URLs safely.
-func parseURLAndVersion(input string) (url string, version string) {
+func parseURLAndVersion(input string) (string, string) {
if strings.HasPrefix(input, "git@") {
// SSH URL format: git@host:owner/repo[.git][:version] or git@host:owner/repo[.git]@version
firstColon := strings.Index(input, ":")
@@ -35,8 +35,8 @@ func parseURLAndVersion(input string) (url string, version string) {
possibleVersion := remainder[lastSep+1:]
// Version shouldn't contain slashes (which paths do) and shouldn't be empty
if !strings.Contains(possibleVersion, "/") && possibleVersion != "" {
- url = input[:firstColon+1+lastSep]
- version = possibleVersion
+ url := input[:firstColon+1+lastSep]
+ version := possibleVersion
return url, version
}
}
@@ -54,8 +54,8 @@ func parseURLAndVersion(input string) (url string, version string) {
}
}
- url = match["url"]
- version = match["version"]
+ url := match["url"]
+ version := match["version"]
return url, version
}
diff --git a/internal/core/services/installer/utils_test.go b/internal/core/services/installer/utils_test.go
index 44fb6dd..de55056 100644
--- a/internal/core/services/installer/utils_test.go
+++ b/internal/core/services/installer/utils_test.go
@@ -160,10 +160,10 @@ func TestEnsureDependency_HTTPSUrl(t *testing.T) {
func TestEnsureDependency_SSHUrl(t *testing.T) {
tests := []struct {
- name string
- input string
- expectedDep string
- expectedVer string
+ name string
+ input string
+ expectedDep string
+ expectedVer string
}{
{
name: "SSH URL with version tag",
@@ -225,4 +225,3 @@ func TestEnsureDependency_SSHUrl(t *testing.T) {
})
}
}
-
diff --git a/utils/dcp/dcp_test.go b/utils/dcp/dcp_test.go
index d34afaa..756eee1 100644
--- a/utils/dcp/dcp_test.go
+++ b/utils/dcp/dcp_test.go
@@ -227,4 +227,3 @@ end.`
t.Error("injectDcps() should preserve AnotherPkg dependency")
}
}
-
diff --git a/utils/librarypath/lazarus_test.go b/utils/librarypath/lazarus_test.go
index 111a675..ae29653 100644
--- a/utils/librarypath/lazarus_test.go
+++ b/utils/librarypath/lazarus_test.go
@@ -55,15 +55,9 @@ const mockLpkContent = `
// TestLazarusPathInjection verifies that other unit search paths are correctly injected in both .lpi and .lpk files.
func TestLazarusPathInjection(t *testing.T) {
tempDir := t.TempDir()
+ t.Chdir(tempDir)
- // Redirect working directory
- oldWd, err := os.Getwd()
- if err == nil {
- defer func() { _ = os.Chdir(oldWd) }()
- }
- if err = os.Chdir(tempDir); err != nil {
- t.Fatalf("Failed to change directory: %v", err)
- }
+ var err error
// Initialize package manager
fs := filesystem.NewOSFileSystem()
@@ -141,7 +135,7 @@ func TestLazarusPathInjection(t *testing.T) {
// Verify LPK XML contents
docLpk := etree.NewDocument()
- if err := docLpk.ReadFromFile(lpkPath); err != nil {
+ if err = docLpk.ReadFromFile(lpkPath); err != nil {
t.Fatalf("Failed to read updated LPK: %v", err)
}
diff --git a/utils/librarypath/librarypath.go b/utils/librarypath/librarypath.go
index fd0b1aa..e3e1c20 100644
--- a/utils/librarypath/librarypath.go
+++ b/utils/librarypath/librarypath.go
@@ -75,14 +75,22 @@ func processBrowsingPath(
setReadOnly bool,
) []string {
var packagePath = filepath.Join(basePath, value.Name(), consts.FilePackage)
- if _, err := os.Stat(packagePath); !os.IsNotExist(err) {
- other, err := pkgmanager.LoadPackageOther(packagePath)
- if err == nil && other != nil && other.BrowsingPath != "" {
- dir := filepath.Join(basePath, value.Name(), other.BrowsingPath)
- paths = getNewBrowsingPathsFromDir(dir, paths, fullPath, rootPath)
- if setReadOnly {
- setReadOnlyProperty(dir)
- }
+ other := loadPackageIfPresent(packagePath)
+ if other == nil || other.BrowsingPath == "" {
+ return paths
+ }
+
+ browsingPaths := strings.Split(other.BrowsingPath, ";")
+ for _, browsingPath := range browsingPaths {
+ browsingPath = strings.TrimSpace(browsingPath)
+ if browsingPath == "" {
+ continue
+ }
+
+ dir := filepath.Join(basePath, value.Name(), browsingPath)
+ paths = getNewBrowsingPathsFromDir(dir, paths, fullPath, rootPath)
+ if setReadOnly {
+ setReadOnlyProperty(dir)
}
}
return paths
@@ -116,18 +124,52 @@ func GetNewPaths(paths []string, fullPath bool, rootPath string) []string {
matches, _ := os.ReadDir(path)
for _, value := range matches {
- var packagePath = filepath.Join(path, value.Name(), consts.FilePackage)
- if _, err := os.Stat(packagePath); !os.IsNotExist(err) {
- other, err := pkgmanager.LoadPackageOther(packagePath)
- if err == nil && other != nil {
- paths = getNewPathsFromDir(filepath.Join(path, value.Name(), other.MainSrc), paths, fullPath, rootPath)
- } else {
- paths = getNewPathsFromDir(filepath.Join(path, value.Name()), paths, fullPath, rootPath)
- }
- } else {
- paths = getNewPathsFromDir(filepath.Join(path, value.Name()), paths, fullPath, rootPath)
+ packageDir := filepath.Join(path, value.Name())
+ packagePath := filepath.Join(packageDir, consts.FilePackage)
+ other := loadPackageIfPresent(packagePath)
+ if other == nil {
+ paths = getNewPathsFromDir(packageDir, paths, fullPath, rootPath)
+ continue
+ }
+
+ paths = appendMainSrcPaths(paths, other, packageDir, fullPath, rootPath)
+ }
+ return paths
+}
+
+func loadPackageIfPresent(packagePath string) *domain.Package {
+ if _, err := os.Stat(packagePath); os.IsNotExist(err) {
+ return nil
+ }
+
+ other, err := pkgmanager.LoadPackageOther(packagePath)
+ if err != nil {
+ return nil
+ }
+
+ return other
+}
+
+func appendMainSrcPaths(
+ paths []string,
+ pkg *domain.Package,
+ packageDir string,
+ fullPath bool,
+ rootPath string,
+) []string {
+ mainSrcs := strings.Split(pkg.MainSrc, ";")
+ for _, mainSrc := range mainSrcs {
+ mainSrc = strings.TrimSpace(mainSrc)
+ if mainSrc != "" {
+ paths = getNewPathsFromDir(filepath.Join(packageDir, mainSrc), paths, fullPath, rootPath)
+ continue
+ }
+
+ if len(mainSrcs) == 1 {
+ paths = getNewPathsFromDir(packageDir, paths, fullPath, rootPath)
}
}
+
return paths
}
diff --git a/utils/librarypath/librarypath_test.go b/utils/librarypath/librarypath_test.go
index f8f7281..78a4f10 100644
--- a/utils/librarypath/librarypath_test.go
+++ b/utils/librarypath/librarypath_test.go
@@ -69,3 +69,65 @@ func TestGetNewBrowsingPaths(t *testing.T) {
t.Error("GetNewBrowsingPaths() should return paths")
}
}
+
+func TestGetNewPaths_MultipleMainSrc(t *testing.T) {
+ tempDir := t.TempDir()
+ t.Setenv("BOSS_BASE_DIR", tempDir)
+ t.Chdir(tempDir)
+
+ var err error
+
+ // Create modules directory
+ modulesDir := filepath.Join(tempDir, "modules")
+ libDir := filepath.Join(modulesDir, "my_lib")
+ err = os.MkdirAll(filepath.Join(libDir, "src"), 0755)
+ if err != nil {
+ t.Fatalf("Failed to create src dir: %v", err)
+ }
+ err = os.MkdirAll(filepath.Join(libDir, "lib"), 0755)
+ if err != nil {
+ t.Fatalf("Failed to create lib dir: %v", err)
+ }
+
+ // Create a dummy source file inside both src and lib so they are walked and recognized
+ err = os.WriteFile(filepath.Join(libDir, "src", "my_lib.pas"), []byte("unit my_lib;"), 0600)
+ if err != nil {
+ t.Fatalf("Failed to create dummy file: %v", err)
+ }
+ err = os.WriteFile(filepath.Join(libDir, "lib", "helper.pas"), []byte("unit helper;"), 0600)
+ if err != nil {
+ t.Fatalf("Failed to create dummy file: %v", err)
+ }
+
+ // Create a boss.json with multiple mainsrc paths
+ bossJSONContent := `{
+ "name": "my_lib",
+ "mainsrc": "src;lib"
+ }`
+ err = os.WriteFile(filepath.Join(libDir, "boss.json"), []byte(bossJSONContent), 0600)
+ if err != nil {
+ t.Fatalf("Failed to write boss.json: %v", err)
+ }
+
+ paths := []string{}
+ result := GetNewPaths(paths, true, tempDir)
+
+ // Should contain both src and lib paths
+ foundSrc := false
+ foundLib := false
+ for _, p := range result {
+ if filepath.Base(p) == "src" {
+ foundSrc = true
+ }
+ if filepath.Base(p) == "lib" {
+ foundLib = true
+ }
+ }
+
+ if !foundSrc {
+ t.Error("Expected to find 'src' path in results")
+ }
+ if !foundLib {
+ t.Error("Expected to find 'lib' path in results")
+ }
+}
diff --git a/utils/normalizer/line_normalizer.go b/utils/normalizer/line_normalizer.go
index 3acea1b..e5e1478 100644
--- a/utils/normalizer/line_normalizer.go
+++ b/utils/normalizer/line_normalizer.go
@@ -1,3 +1,4 @@
+// Package normalizer provides line ending normalization for Delphi source files.
package normalizer
import (
@@ -10,7 +11,7 @@ import (
"github.com/hashload/boss/pkg/msg"
)
-var delphiExtensions = map[string]bool{
+var delphiExtensions = map[string]bool{ //nolint:gochecknoglobals // Immutable lookup map
".pas": true,
".inc": true,
".dfm": true,
@@ -67,5 +68,6 @@ func NormalizeFileLineEndings(filePath string) error {
crlfContent := bytes.ReplaceAll(normalized, []byte("\n"), []byte("\r\n"))
// Write back
+ // #nosec G703 -- Writing Delphi files from controlled directories
return os.WriteFile(filePath, crlfContent, 0600)
}