Skip to content

Commit 85c1788

Browse files
authored
Merge pull request #116 from fossas/feat/support-gradle-root-deps
Feat/support gradle root deps
2 parents 8b28d1f + 99a9552 commit 85c1788

File tree

3 files changed

+119
-15
lines changed

3 files changed

+119
-15
lines changed

builders/gradle.go

+27-7
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,25 @@ func (builder *GradleBuilder) Analyze(m module.Module, allowUnresolved bool) ([]
5656
gradleLogger.Debugf("Running Gradle analysis: %#v %#v in %s", m, allowUnresolved, m.Dir)
5757

5858
moduleConfigurationKey := strings.Split(m.Name, ":")
59+
60+
taskName := "dependencies" // defaults to root gradle dependencies task
61+
62+
if moduleConfigurationKey[0] != "" {
63+
// a subtask was configured
64+
taskName = moduleConfigurationKey[0] + ":dependencies"
65+
}
66+
5967
taskConfiguration := "compile"
60-
taskName := moduleConfigurationKey[0]
61-
if len(moduleConfigurationKey) == 2 {
68+
if len(moduleConfigurationKey) == 2 && moduleConfigurationKey[1] != "" {
6269
taskConfiguration = moduleConfigurationKey[1]
6370
}
6471

65-
// TODO: We need to let the user configure the right configurations
6672
// NOTE: we are intentionally using exec.Command over runLogged here, due to path issues with defining cmd.Dir
67-
// TODO: set TERM=dumb
68-
dependenciesOutput, err := exec.Command(builder.GradleCmd, taskName+":dependencies", "-q", "--configuration="+taskConfiguration, "--offline", "-a").Output()
73+
dependenciesCmd := exec.Command(builder.GradleCmd, taskName, "-q", "--configuration="+taskConfiguration, "--offline", "-a")
74+
dependenciesCmd.Env = os.Environ()
75+
dependenciesCmd.Env = append(dependenciesCmd.Env, "TERM=dumb")
76+
77+
dependenciesOutput, err := dependenciesCmd.Output()
6978
if len(dependenciesOutput) == 0 || err != nil {
7079
return nil, fmt.Errorf("could not run Gradle task %s:dependencies", taskName)
7180
}
@@ -139,10 +148,21 @@ func (builder *GradleBuilder) DiscoverModules(dir string) ([]module.Config, erro
139148
}
140149
}
141150

142-
return moduleConfigurations, nil
151+
if len(moduleConfigurations) > 0 {
152+
return moduleConfigurations, nil
153+
}
154+
155+
// If task list succeeds but returns no subproject dependencies tasks, fall back to the root `dependencies` task
156+
return []module.Config{
157+
{
158+
Name: ":compile",
159+
Path: "build.gradle",
160+
Type: "gradle",
161+
},
162+
}, nil
143163
}
144164

145-
// Fall back to "app" as default task, even though technically it would be "" (root)
165+
// If task list fails, fall back to "app" as default task, even though technically it would be "" (root)
146166
return []module.Config{
147167
{
148168
Name: "app:compile",

cmd/fossa/init.go

+7
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import (
44
"fmt"
55
"os"
66
"regexp"
7+
"time"
78

9+
"github.com/briandowns/spinner"
810
logging "github.com/op/go-logging"
911
"github.com/urfave/cli"
1012

@@ -35,6 +37,10 @@ func initCmd(c *cli.Context) {
3537
}
3638

3739
func doInit(conf *config.CLIConfig, overwrite bool, includeAll bool) error {
40+
s := spinner.New(spinner.CharSets[11], 100*time.Millisecond)
41+
s.Writer = os.Stderr
42+
s.Suffix = " Initializing..."
43+
s.Start()
3844
findDir := "."
3945
if len(conf.Modules) == 0 || overwrite {
4046
if cwd, err := os.Getwd(); err == nil {
@@ -61,6 +67,7 @@ func doInit(conf *config.CLIConfig, overwrite bool, includeAll bool) error {
6167
} else {
6268
initLogger.Warningf("%d module(s) found in config file (`%s`); skipping initialization.", len(conf.Modules), conf.ConfigFilePath)
6369
}
70+
s.Stop()
6471
return nil
6572
}
6673

docs/integrations/gradle.md

+85-8
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,101 @@ Gradle support in FOSSA CLI depends on the following tools existing in your envi
1111

1212
### Automatic Configuration
1313

14-
When running `fossa init`, FOSSA will look for a root Gradle build (`build.gradle`) and interrogate it for build-able tasks. From there, it will write module configurations into your `.fossa.yml` for every task that supports a `:dependency` sub-task.
14+
`fossa init` will attempt a "best-effort" strategy to look through all available Gradle tasks/configurations and elect the most likely ones used for a production build.
15+
16+
1. Look for a root Gradle build (`build.gradle`)
17+
2. Run and parse the output of `gradle tasks --all -q -a --offline` to find all available sub-tasks that support a `:dependencies` command
18+
3. If task list succeeds but no sub-tasks are available, fallback to the root `dependencies` task in `build.gradle`
19+
4. Filter out any suspicious-looking tasks (i.e. labeled `test` or `testCompile`)
20+
5. Write tasks to configuration (`fossa.yml`)
1521

1622
If a gradle wrapper was provided in the same directory (`./gradlew`), `fossa` will prefer to use that over the local `gradle` binary.
1723

1824
### Manual Configuration
1925

20-
Add a `gradle` module with the path to the `pom.xml` in your project.
26+
`fossa init` will automatically generate this configuration for you, but if you'd like to manually configure, add a `gradle` module to your `fossa.yml`:
27+
28+
```yaml
29+
analyze:
30+
modules:
31+
- name: {task}:{configuration}
32+
path: {path-to-build.gradle}
33+
type: gradle
34+
```
35+
36+
For Gradle modules, `fossa` requires a few peices of information to run dependency analysis:
37+
38+
- `task` - the name of the task/subtask used to generate your build (most often this is `app` or empty string `''` for root)
39+
- `configuration` - the configuration your task runs during a production build (if undefined, defaults to `compile`)
40+
- `path` - path to your `*.gradle` build file relative to your repo root directory (usually this is just `build.gradle`)
41+
42+
If you don't specify a task, `fossa` will run the default `dependencies` located in your root `*.gradle` file:
43+
44+
```yaml
45+
analyze:
46+
modules:
47+
- name: :compile # runs the root `dependencies` task with `compile` configuration
48+
path: build.gradle
49+
type: gradle
50+
```
51+
52+
You can customize your configuration by modifying the `name` entry using the template `{task}:{configuration}`.
53+
54+
```yaml
55+
analyze:
56+
modules:
57+
- name: app:runtime # runs `app:dependencies` task with `runtime` configuration
58+
path: build.gradle
59+
type: gradle
60+
```
61+
62+
NOTE: If you don't specify a configuration at all, `fossa` will default to `compile`. For example:
2163

2264
```yaml
2365
analyze:
2466
modules:
25-
- name: your-mvn-project
26-
path: pom.xml
27-
type: mvn
67+
- name: app # runs `app:dependencies` task with `compile` configuration
68+
path: build.gradle
69+
type: gradle
70+
- name: '' # empty - runs root `dependencies` task with `compile` configuration
71+
path: build.gradle
72+
type: gradle
2873
```
2974
30-
## Design
75+
NOTE: While `app` and `compile` are two very common tasks/configurations, your Gradle build may be different. For instance, many Gradle builds may use the configuration `release` or `default` instead of `compile`. See the `Troubleshooting` section below for steps in figuring out the right configuration for you.
76+
77+
## Troubleshooting
78+
Since `fossa` operates directly on Gradle, it requires your build environment to be satisfied and Gradle tasks to reliably succeed before `fossa` can run.
79+
80+
Most issues are often fixed by simply verifying that your Gradle build succeeds.
81+
82+
### Errors during `fossa init`
83+
FOSSA's autoconfiguration depends on `gradle tasks --all`. If `fossa init` is failing to discover the right tasks/configuration, make sure:
84+
85+
1. `gradle tasks --all` succeeds in your environment
86+
2. Your `path` entry in `fossa.yml` is points to the right `*.gradle` file
87+
88+
### Issues with dependency data
89+
90+
If you're having trouble getting correct dependency data, try verifying the following:
91+
92+
1. Your configuration and task name is valid (i.e. `app:compile` vs `customTask:customConfiguration`) in `fossa.yml`
93+
2. You get the desired output from your configured dependencies task `gradle {subtask}:dependencies --configuration={configuration}` (i.e. `gradle app:dependencies --configuration=compile`)
94+
95+
If running the gradle command in your terminal does not succeed that means your gradle build is not properly configured.
96+
97+
If it does succeed but produces unexpected or empty output, it means you have likely selected the wrong task or configuration for `fossa` to analyze. Running without the `--configuration` will give you the full output and help you find the right configuration to select.
98+
99+
### Reporting issues
100+
101+
If you're still having problems, open up an issue on this repo with the full logs/output of `fossa -o --debug` and your `fossa.yml` file.
102+
103+
Optionally, provide your `build.gradle` for more context.
104+
105+
## Roadmap
31106

32-
### Analysis
107+
If you'd like to help us improve our Gradle integration, please feel free to file and assign yourself to an issue on this repo.
33108

34-
Analysis will support any tasks you can run `:dependendencies` on with the "compile" configuration. This is equivalent to `gradle app:dependencies --configuration=compile`.
109+
1. Support multiple independent "root" Gradle builds that may be segregated in a codebase
110+
2. Implement a cheap build validation method to tell the user whether a Gradle build is satisfied without invoking a Gradle runtime
111+
3. Support multiple configurations for a single module definition i.e. `app:compile:runtime`

0 commit comments

Comments
 (0)