Skip to content

Commit b6f4cfa

Browse files
Support for scanning scala projects (#301)
* Scala project support added * Add sbt support for docker images
1 parent f316d5a commit b6f4cfa

23 files changed

+1142
-64
lines changed

.github/workflows/codescene.yml

Lines changed: 0 additions & 61 deletions
This file was deleted.

build/docker/alpine.Dockerfile

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ RUN wget https://services.gradle.org/distributions/gradle-$GRADLE_VERSION-bin.zi
3737
unzip gradle-$GRADLE_VERSION-bin.zip -d $GRADLE_HOME && \
3838
rm gradle-$GRADLE_VERSION-bin.zip
3939

40+
# Add SBT, used for Scala resolution
41+
ENV SBT_VERSION="1.10.11"
42+
ENV SBT_HOME="/usr/lib/sbt"
43+
ENV PATH="$SBT_HOME/bin:$PATH"
44+
RUN wget https://github.com/sbt/sbt/releases/download/v${SBT_VERSION}/sbt-${SBT_VERSION}.tgz && \
45+
mkdir -p $SBT_HOME && \
46+
tar -zxvf sbt-${SBT_VERSION}.tgz -C $SBT_HOME --strip-components=1 && \
47+
rm sbt-${SBT_VERSION}.tgz && \
48+
ln -s $SBT_HOME/bin/sbt /usr/bin/sbt
49+
4050
# g++ needed to compile python packages with C dependencies (numpy, scipy, etc.)
4151
RUN apk --no-cache --update add \
4252
openjdk21-jdk \
@@ -47,7 +57,8 @@ RUN apk --no-cache --update add \
4757
npm \
4858
yarn \
4959
g++ \
50-
curl
60+
curl \
61+
bash
5162

5263
RUN apk --no-cache --update add dotnet8-sdk go~=1.23 --repository=https://dl-cdn.alpinelinux.org/alpine/v3.20/community
5364

@@ -68,7 +79,7 @@ RUN apk add --no-cache --virtual build-dependencies curl && \
6879
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer \
6980
&& apk del build-dependencies
7081

71-
RUN php -v && composer --version
82+
RUN php -v && composer --version && sbt --version
7283

7384
CMD [ "debricked", "scan" ]
7485

build/docker/debian.Dockerfile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,18 @@ RUN apt update -y && \
111111
sh -c 'echo "deb https://packages.sury.org/php/ bookworm main" > /etc/apt/sources.list.d/php.list' && \
112112
apt -y clean && rm -rf /var/lib/apt/lists/*
113113

114+
# Add SBT, used for Scala resolution
115+
ENV SBT_VERSION="1.10.11"
116+
ENV SBT_HOME="/usr/lib/sbt"
117+
ENV PATH="$SBT_HOME/bin:$PATH"
118+
RUN curl -fsSLO https://github.com/sbt/sbt/releases/download/v${SBT_VERSION}/sbt-${SBT_VERSION}.tgz && \
119+
mkdir -p $SBT_HOME && \
120+
tar -zxvf sbt-${SBT_VERSION}.tgz -C $SBT_HOME --strip-components=1 && \
121+
rm sbt-${SBT_VERSION}.tgz && \
122+
ln -s $SBT_HOME/bin/sbt /usr/bin/sbt
123+
124+
RUN sbt --version
125+
114126
RUN apt -y update && apt -y install \
115127
php8.3 \
116128
php8.3-curl \

internal/file/finder.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,14 @@ func (finder *Finder) GetSupportedFormats() ([]*CompiledFormat, error) {
213213
return nil, err
214214
}
215215

216+
sbtEntry := &Format{
217+
ManifestFileRegex: "^build\\.sbt$",
218+
DocumentationUrl: "https://docs.debricked.com/overview/language-support/scala-sbt",
219+
LockFileRegexes: []string{""},
220+
}
221+
222+
formats = append(formats, sbtEntry)
223+
216224
var compiledDependencyFileFormats []*CompiledFormat
217225
for _, format := range formats {
218226
compiledDependencyFileFormat, err := NewCompiledFormat(format)

internal/resolution/pm/maven/testdata/cmd_factory_mock.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package testdata
22

3-
import "os/exec"
3+
import (
4+
"os/exec"
5+
"runtime"
6+
)
47

58
type CmdFactoryMock struct {
69
Err error
@@ -12,5 +15,10 @@ func (f CmdFactoryMock) MakeDependencyTreeCmd(_ string) (*exec.Cmd, error) {
1215
if len(f.Arg) == 0 {
1316
f.Arg = `"MakeDependencyTreeCmd"`
1417
}
18+
19+
if runtime.GOOS == "windows" && f.Name == "echo" {
20+
return exec.Command("cmd", "/C", f.Name, f.Arg), nil
21+
}
22+
1523
return exec.Command(f.Name, f.Arg), f.Err
1624
}

internal/resolution/pm/pm.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/debricked/cli/internal/resolution/pm/npm"
1010
"github.com/debricked/cli/internal/resolution/pm/nuget"
1111
"github.com/debricked/cli/internal/resolution/pm/pip"
12+
"github.com/debricked/cli/internal/resolution/pm/sbt"
1213
"github.com/debricked/cli/internal/resolution/pm/yarn"
1314
)
1415

@@ -28,5 +29,6 @@ func Pms() []IPm {
2829
bower.NewPm(),
2930
nuget.NewPm(),
3031
composer.NewPm(),
32+
sbt.NewPm(),
3133
}
3234
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# SBT (Scala) Resolution Logic
2+
3+
The resolution of SBT (Scala Build Tool) dependencies works as follows:
4+
5+
1. Parse the `build.sbt` file to identify any modules
6+
2. Run `sbt makePom` in the project directory to generate a POM file
7+
3. Find the generated `.pom` file (typically in `target/scala-<version>/<project>-<version>.pom`)
8+
4. Copy/rename the `.pom` file to `pom.xml` in the same directory as `build.sbt`
9+
5. Use the existing Maven resolver to handle the `pom.xml` file
10+
11+
This approach allows SBT projects to leverage the existing Maven resolution logic after the POM file is generated.
12+
13+
## Requirements
14+
15+
1. SBT must be installed and available in the PATH
16+
2. The SBT project must be configured to support the `makePom` command (most SBT projects support this by default)
17+
3. Maven dependencies must be resolvable as described in the Maven resolution documentation
18+
19+
## Private Dependencies
20+
21+
Similar to Maven projects, SBT projects might use dependencies from repositories other than the default ones. The
22+
SBT `makePom` command will include these repository configurations in the generated POM file, and then the Maven
23+
resolution process will handle them as described in the Maven README.
24+
25+
## Troubleshooting
26+
27+
If you encounter issues with SBT resolution:
28+
29+
1. Verify SBT is installed and accessible in the PATH
30+
2. Try running `sbt makePom` manually in your project directory to check if it works
31+
3. Inspect the generated `.pom` file in `target/scala-*/` to ensure it contains the correct dependencies
32+
4. Check if any repository authentication is required for your dependencies
33+
34+
## Error Messages
35+
36+
Common error messages and their meanings:
37+
38+
- `SBT wasn't found`: The SBT executable isn't installed or isn't in the PATH
39+
- `Failed to generate Maven POM file`: There was an error during the POM generation process
40+
- `SBT configuration file not found`: The build.sbt file couldn't be found or accessed
41+
- `Failed to parse SBT build file`: The build.sbt file contains syntax errors
42+
- `We weren't able to retrieve one or more dependencies or plugins`: Network issues prevented dependency resolution
43+
44+
## Example Command
45+
46+
```shell
47+
debricked resolve /path/to/scala/project
48+
```
49+
50+
This will:
51+
52+
1. Find all `build.sbt` files in the specified path
53+
2. Generate a POM file for each one
54+
3. Resolve dependencies using the Maven resolver
55+
4. Create `maven.debricked.lock` files with the dependency information
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package sbt
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"regexp"
7+
)
8+
9+
type IBuildService interface {
10+
ParseBuildModules(path string) ([]string, error)
11+
FindPomFile(dir string) (string, error)
12+
RenamePomToXml(pomFile, destDir string) (string, error)
13+
}
14+
15+
type BuildService struct{}
16+
17+
func (b BuildService) ParseBuildModules(path string) ([]string, error) {
18+
content, err := os.ReadFile(path)
19+
if err != nil {
20+
return nil, err
21+
}
22+
23+
moduleRegex := regexp.MustCompile(`project\s*\(\s*"([^"]+)"\s*\)`)
24+
matches := moduleRegex.FindAllStringSubmatch(string(content), -1)
25+
26+
modules := make([]string, 0, len(matches))
27+
for _, match := range matches {
28+
if len(match) > 1 {
29+
modules = append(modules, match[1])
30+
}
31+
}
32+
33+
return modules, nil
34+
}
35+
36+
func (b BuildService) FindPomFile(dir string) (string, error) {
37+
targetDir := filepath.Join(dir, "target")
38+
39+
scalaVersionDirs, err := filepath.Glob(filepath.Join(targetDir, "scala-*"))
40+
if err != nil || len(scalaVersionDirs) == 0 {
41+
return "", err
42+
}
43+
44+
for _, scalaDir := range scalaVersionDirs {
45+
pomFiles, err := filepath.Glob(filepath.Join(scalaDir, "*.pom"))
46+
if err == nil && len(pomFiles) > 0 {
47+
return pomFiles[0], nil
48+
}
49+
}
50+
51+
return "", nil
52+
}
53+
54+
func (b BuildService) RenamePomToXml(pomFile, destDir string) (string, error) {
55+
content, err := os.ReadFile(pomFile)
56+
if err != nil {
57+
return "", err
58+
}
59+
60+
pomXmlPath := filepath.Join(destDir, "pom.xml")
61+
err = os.WriteFile(pomXmlPath, content, 0600)
62+
if err != nil {
63+
return "", err
64+
}
65+
66+
return pomXmlPath, nil
67+
}

0 commit comments

Comments
 (0)