Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions .codacy/codacy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ runtimes:
- node@22.2.0
- python@3.11.11
- dart@3.7.2
- java@17.0.10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need remove this one from here, and add it only on the specific tool test. Does it make sense @zhamborova with the new strucuture? Then, if you don't have it here, the integration-test will fail if you don't put the java there

Copy link
Contributor Author

@andrzej-janczak andrzej-janczak May 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, we should stop using this for developing maybe
But do we need to start in my PR 😄

tools:
- eslint@8.57.0
- trivy@0.59.1
Expand Down
8 changes: 5 additions & 3 deletions .cursor/rules/cursor.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ alwaysApply: true
# Your rule content

## Key Rules
- use full names like e.g. feature, instead of feat
- avoid code copy pasting and duplication, refactor to function eagerly when possible
- use fulnames like e.g. feature, instaed of feat
- run go build after each code modification to see if app compiles
- remove dead unused code
- remove dead unused code
- look for constants like file permissons in `constants` folder

## Code Style Guidelines
- **Imports**: Standard lib first, external packages second, internal last
Expand All @@ -24,4 +26,4 @@ alwaysApply: true
- `cmd/`: CLI command implementations
- `config/`: Configuration handling
- `tools/`: Tool-specific implementations
- `utils/`: Utility functions and static - look for static like default file permisson here
- `utils/`: Utility functions
7 changes: 7 additions & 0 deletions cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ func configFileTemplate(tools []tools.Tool) string {
needsNode := false
needsPython := false
needsDart := false
needsJava := false

// Default versions
defaultVersions := map[string]string{
Expand Down Expand Up @@ -168,6 +169,8 @@ func configFileTemplate(tools []tools.Tool) string {
needsPython = true
} else if tool.Uuid == DartAnalyzer {
needsDart = true
} else if tool.Uuid == PMD {
needsJava = true
}
}

Expand All @@ -186,11 +189,15 @@ func configFileTemplate(tools []tools.Tool) string {
if needsDart {
sb.WriteString(" - dart@3.7.2\n")
}
if needsJava {
sb.WriteString(" - java@17.0.10\n")
}
} else {
// In local mode with no tools specified, include all runtimes
sb.WriteString(" - node@22.2.0\n")
sb.WriteString(" - python@3.11.11\n")
sb.WriteString(" - dart@3.7.2\n")
sb.WriteString(" - java@17.0.10\n")
}

sb.WriteString("tools:\n")
Expand Down
45 changes: 40 additions & 5 deletions config/runtimes-installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ func InstallRuntimes(config *ConfigType) error {

// InstallRuntime installs a specific runtime
func InstallRuntime(name string, runtimeInfo *plugins.RuntimeInfo) error {

// Check if the runtime is already installed
if isRuntimeInstalled(runtimeInfo) {
logger.Info("Runtime already installed", logrus.Fields{
Expand All @@ -63,6 +64,15 @@ func InstallRuntime(name string, runtimeInfo *plugins.RuntimeInfo) error {
return fmt.Errorf("failed to download and extract runtime %s: %w", name, err)
}

// Verify that the runtime binaries are available
if !isRuntimeInstalled(runtimeInfo) {
logger.Error("Runtime binaries not found after extraction", logrus.Fields{
"runtime": name,
"version": runtimeInfo.Version,
})
return fmt.Errorf("runtime %s was extracted but binaries are not available", name)
}

return nil
}

Expand All @@ -71,18 +81,30 @@ func isRuntimeInstalled(runtimeInfo *plugins.RuntimeInfo) bool {
// If there are no binaries, check the install directory
if len(runtimeInfo.Binaries) == 0 {
_, err := os.Stat(runtimeInfo.InstallDir)
return err == nil
if err != nil {
logger.Debug("Runtime install directory not found", logrus.Fields{
"directory": runtimeInfo.InstallDir,
"error": err,
})
return false
}
return true
}

// Check if at least one binary exists
for _, binaryPath := range runtimeInfo.Binaries {
for binaryName, binaryPath := range runtimeInfo.Binaries {
_, err := os.Stat(binaryPath)
if err == nil {
return true
if err != nil {
logger.Debug("Runtime binary not found", logrus.Fields{
"binary": binaryName,
"path": binaryPath,
"error": err,
})
return false
}
}

return false
return true
}

// downloadAndExtractRuntime downloads and extracts a runtime
Expand Down Expand Up @@ -140,6 +162,19 @@ func downloadAndExtractRuntime(runtimeInfo *plugins.RuntimeInfo) error {
return fmt.Errorf("failed to extract runtime: %w", err)
}

// Ensure binaries have executable permissions
for _, binaryPath := range runtimeInfo.Binaries {
fullPath := filepath.Join(Config.RuntimesDirectory(), filepath.Base(runtimeInfo.InstallDir), binaryPath)
if err := os.Chmod(fullPath, utils.DefaultDirPerms); err != nil {
logger.Debug("Failed to set binary permissions", logrus.Fields{
"binary": binaryPath,
"path": fullPath,
"error": err,
})
return fmt.Errorf("failed to set binary permissions for %s: %w", binaryPath, err)
}
}

logger.Debug("Runtime extraction completed", logrus.Fields{
"runtime": runtimeInfo.Name,
"version": runtimeInfo.Version,
Expand Down
24 changes: 24 additions & 0 deletions constants/permissions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package constants

const (
// FilePermission represents the default file permission (rw-r--r--)

Check notice on line 4 in constants/permissions.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

constants/permissions.go#L4

comment on exported const DefaultFilePerms should be of the form "DefaultFilePerms ..."
// This permission gives:
// - read/write (rw-) permissions to the owner
// - read-only (r--) permissions to the group
// - read-only (r--) permissions to others
DefaultFilePerms = 0644

// DefaultDirPerms represents the default directory permission (rwxr-xr-x)
// This permission gives:
// - read/write/execute (rwx) permissions to the owner
// - read/execute (r-x) permissions to the group
// - read/execute (r-x) permissions to others
//
// Execute permission on directories is required to:
// - List directory contents (ls)
// - Access files within the directory (cd)
// - Create/delete files in the directory
// Without execute permission, users cannot traverse into or use the directory,
// even if they have read/write permissions on files inside it
DefaultDirPerms = 0755 // For directories
)
46 changes: 40 additions & 6 deletions plugins/runtime-utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,14 @@

// binary represents a binary executable provided by the runtime
type binary struct {
Name string `yaml:"name"`
Path string `yaml:"path"`
Name string `yaml:"name"`
Path interface{} `yaml:"path"` // Can be either string or map[string]string
}

// binaryPath represents OS-specific paths for a binary
type binaryPath struct {
Darwin string `yaml:"darwin"`
Linux string `yaml:"linux"`
}

// pluginConfig holds the structure of the plugin.yaml file
Expand Down Expand Up @@ -48,6 +54,7 @@
// templateData holds the data to be used in template substitution
type templateData struct {
Version string
MajorVersion string
FileName string
OS string
Arch string
Expand Down Expand Up @@ -118,14 +125,31 @@

// Process binary paths
for _, binary := range plugin.Config.Binaries {
binaryPath := path.Join(installDir, binary.Path)
var binaryPath string

switch path := binary.Path.(type) {
case string:
// If path is a simple string, use it directly
binaryPath = path
case map[string]interface{}:
// If path is a map, get the OS-specific path
if osPath, ok := path[runtime.GOOS]; ok {
binaryPath = osPath.(string)
} else {
return nil, fmt.Errorf("no binary path specified for OS %s", runtime.GOOS)
}
default:
return nil, fmt.Errorf("invalid path format for binary %s", binary.Name)
}

fullPath := path.Join(installDir, binaryPath)

Check warning on line 145 in plugins/runtime-utils.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

plugins/runtime-utils.go#L145

`path.Join(...)` always joins using a forward slash.

// Add file extension for Windows executables
if runtime.GOOS == "windows" && !strings.HasSuffix(binaryPath, ".exe") {
binaryPath += ".exe"
if runtime.GOOS == "windows" && !strings.HasSuffix(fullPath, ".exe") {
fullPath += ".exe"
}

info.Binaries[binary.Name] = binaryPath
info.Binaries[binary.Name] = fullPath
}

return info, nil
Expand Down Expand Up @@ -172,6 +196,14 @@
return p.Config.Download.Extension.Default
}

// getMajorVersion extracts the major version from a version string (e.g. "17.0.10" -> "17")
func (p *runtimePlugin) getMajorVersion(version string) string {
if idx := strings.Index(version, "."); idx != -1 {
return version[:idx]
}
return version
}

// GetFileName generates the filename based on the template in plugin.yaml
func (p *runtimePlugin) getFileName(version string) string {
goos := runtime.GOOS
Expand All @@ -185,6 +217,7 @@
// Prepare template data
data := templateData{
Version: version,
MajorVersion: p.getMajorVersion(version),
OS: mappedOS,
Arch: mappedArch,
ReleaseVersion: releaseVersion,
Expand Down Expand Up @@ -230,6 +263,7 @@
// Prepare template data
data := templateData{
Version: version,
MajorVersion: p.getMajorVersion(version),
FileName: fileName,
OS: mappedOS,
Arch: mappedArch,
Expand Down
20 changes: 20 additions & 0 deletions plugins/runtimes/java/plugin.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: java
description: Java Runtime Environment
download:
url_template: "https://github.com/adoptium/temurin{{.MajorVersion}}-binaries/releases/download/jdk-{{.Version}}%2B7/OpenJDK{{.MajorVersion}}U-jdk_{{.Arch}}_{{.OS}}_hotspot_{{.Version}}_7.{{.Extension}}"
file_name_template: "jdk-{{.Version}}+7"
extension:
default: "tar.gz"
arch_mapping:
"386": "x86-32"
"amd64": "x64"
"arm": "arm"
"arm64": "aarch64"
os_mapping:
"darwin": "mac"
"linux": "linux"
binaries:
- name: java
path:
darwin: "Contents/Home/bin/java"
linux: "bin/java"
Loading
Loading