diff --git a/.github/workflows/integ-test-dependency-submission.yml b/.github/workflows/integ-test-dependency-submission.yml index df348e9..d223fbd 100644 --- a/.github/workflows/integ-test-dependency-submission.yml +++ b/.github/workflows/integ-test-dependency-submission.yml @@ -112,7 +112,7 @@ jobs: uses: ./dependency-submission with: build-root-directory: .github/workflow-samples/groovy-dsl - additional-arguments: --no-build-cache + dependency-resolution-task: assemble - name: Check generated dependency graphs shell: bash run: | diff --git a/dependency-submission/action.yml b/dependency-submission/action.yml index c85998b..3835535 100644 --- a/dependency-submission/action.yml +++ b/dependency-submission/action.yml @@ -2,6 +2,7 @@ name: Gradle Dependency Submission description: Generates a dependency graph for a Gradle project and submits it via the Dependency Submission API inputs: + # Gradle execution configuration gradle-version: description: | Gradle version to use. If specified, this Gradle version will be downloaded, added to the PATH and used for invoking Gradle. @@ -12,6 +13,12 @@ inputs: description: Path to the root directory of the build. Default is the root of the GitHub workspace. required: false + dependency-resolution-task: + description: | + Task(s) that should be executed in order to resolve all project dependencies. + By default, the built-in `:ForceDependencyResolutionPlugin_resolveAllDependencies` task is executed. + required: false + additional-arguments: description: | Additional arguments to pass to Gradle when generating the dependency graph. diff --git a/docs/dependency-submission.md b/docs/dependency-submission.md index 576bed5..0667511 100644 --- a/docs/dependency-submission.md +++ b/docs/dependency-submission.md @@ -43,6 +43,21 @@ jobs: - name: Generate and submit dependency graph uses: gradle/actions/dependency-submission@v3 ``` + +### Gradle execution + +To generate a dependency graph, the `dependency-submission` action must perform a Gradle execution that resolves +the dependencies of the project. All dependencies that are resolved in this execution will be included in the +generated dependency graph. By default action executes a built-in task that is designed to resolve all build dependencies +(`:ForceDependencyResolutionPlugin_resolveAllDependencies`). + +The action looks for a Gradle project in the root of the workspace, and executes this project with +the Gradle wrapper, if configured for the project. If the wrapper is not configured, whatever `gradle` available +on the command-line will be used. + +The action provides the ability to override the Gradle version and task to execute, as well as provide +additional arguments that will be passed to Gradle on the command-line. See [Configuration Parameters](#configuration-parameters) below. + ### Publishing a Develocity Build ScanĀ® from your dependency submission workflow You can automatically publish a free Develocity Build Scan on every run of `gradle/actions/dependency-submission`. @@ -64,8 +79,6 @@ A Build Scan makes it easy to determine the source of any dependency vulnerabili In some cases, the default action configuration will not be sufficient, and additional action parameters will need to be specified. -See the example below for a summary, and the [Action Metadata file](action.yml) for a more detailed description of each input parameter. - ```yaml - name: Generate and save dependency graph uses: gradle/actions/dependency-submission@v3 @@ -76,6 +89,12 @@ See the example below for a summary, and the [Action Metadata file](action.yml) # The gradle project is not in the root of the repository. build-root-directory: my-gradle-project + # Choose a task that will trigger dependency resolution + dependency-resolution-task: myDependencyResolutionTask + + # Additional arguments that should be passed to execute Gradle + additonal-arguments: --no-configuration-cache + # Enable configuration-cache reuse for this build. cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} @@ -83,6 +102,8 @@ See the example below for a summary, and the [Action Metadata file](action.yml) dependency-graph: generate-and-upload ``` +See the [Action Metadata file](../dependency-submission/action.yml) for a more detailed description of each input parameter. + # Resolving a dependency vulnerability ## Finding the source of a dependency vulnerability diff --git a/sources/src/dependency-submission/main.ts b/sources/src/dependency-submission/main.ts index e043c66..73e5d0c 100644 --- a/sources/src/dependency-submission/main.ts +++ b/sources/src/dependency-submission/main.ts @@ -5,7 +5,13 @@ import * as gradle from '../execution/gradle' import * as dependencyGraph from '../dependency-graph' import {parseArgsStringToArgv} from 'string-argv' -import {BuildScanConfig, CacheConfig, DependencyGraphConfig, DependencyGraphOption} from '../input-params' +import { + BuildScanConfig, + CacheConfig, + DependencyGraphConfig, + DependencyGraphOption, + GradleExecutionConfig +} from '../input-params' /** * The main entry point for the action, called by Github Actions for the step. @@ -25,16 +31,22 @@ export async function run(): Promise { } // Only execute if arguments have been provided - const additionalArgs = core.getInput('additional-arguments') + const executionConfig = new GradleExecutionConfig() + const taskList = executionConfig.getDependencyResolutionTask() + const additionalArgs = executionConfig.getAdditionalArguments() const executionArgs = ` -Dorg.gradle.configureondemand=false -Dorg.gradle.dependency.verification=off -Dorg.gradle.unsafe.isolated-projects=false - :ForceDependencyResolutionPlugin_resolveAllDependencies + ${taskList} ${additionalArgs} ` const args: string[] = parseArgsStringToArgv(executionArgs) - await gradle.provisionAndMaybeExecute(args) + await gradle.provisionAndMaybeExecute( + executionConfig.getGradleVersion(), + executionConfig.getBuildRootDirectory(), + args + ) await dependencyGraph.complete(config) } catch (error) { diff --git a/sources/src/execution/gradle.ts b/sources/src/execution/gradle.ts index 0ec1108..35f09e4 100644 --- a/sources/src/execution/gradle.ts +++ b/sources/src/execution/gradle.ts @@ -1,19 +1,20 @@ import * as core from '@actions/core' import * as exec from '@actions/exec' -import * as path from 'path' -import * as params from '../input-params' import * as provisioner from './provision' import * as gradlew from './gradlew' -import {getWorkspaceDirectory} from '../input-params' -export async function provisionAndMaybeExecute(args: string[]): Promise { +export async function provisionAndMaybeExecute( + gradleVersion: string, + buildRootDirectory: string, + args: string[] +): Promise { // Download and install Gradle if required - const executable = await provisioner.provisionGradle() + const executable = await provisioner.provisionGradle(gradleVersion) // Only execute if arguments have been provided if (args.length > 0) { - await executeGradleBuild(executable, buildRootDirectory(), args) + await executeGradleBuild(executable, buildRootDirectory, args) } } @@ -30,13 +31,3 @@ async function executeGradleBuild(executable: string | undefined, root: string, core.setFailed(`Gradle build failed: see console output for details`) } } - -function buildRootDirectory(): string { - const baseDirectory = getWorkspaceDirectory() - const buildRootDirectoryInput = params.getBuildRootDirectory() - const resolvedBuildRootDirectory = - buildRootDirectoryInput === '' - ? path.resolve(baseDirectory) - : path.resolve(baseDirectory, buildRootDirectoryInput) - return resolvedBuildRootDirectory -} diff --git a/sources/src/execution/provision.ts b/sources/src/execution/provision.ts index 0637895..b7c3eb4 100644 --- a/sources/src/execution/provision.ts +++ b/sources/src/execution/provision.ts @@ -7,7 +7,6 @@ import * as cache from '@actions/cache' import * as toolCache from '@actions/tool-cache' import * as gradlew from './gradlew' -import * as params from '../input-params' import {handleCacheFailure} from '../caching/cache-utils' import {CacheConfig} from '../input-params' @@ -17,8 +16,7 @@ const gradleVersionsBaseUrl = 'https://services.gradle.org/versions' * Install any configured version of Gradle, adding the executable to the PATH. * @return Installed Gradle executable or undefined if no version configured. */ -export async function provisionGradle(): Promise { - const gradleVersion = params.getGradleVersion() +export async function provisionGradle(gradleVersion: string): Promise { if (gradleVersion !== '' && gradleVersion !== 'wrapper') { return addToPath(await installGradle(gradleVersion)) } diff --git a/sources/src/input-params.ts b/sources/src/input-params.ts index 393dfcc..eb9ac04 100644 --- a/sources/src/input-params.ts +++ b/sources/src/input-params.ts @@ -4,6 +4,7 @@ import * as cache from '@actions/cache' import {SUMMARY_ENV_VAR} from '@actions/core/lib/summary' import {parseArgsStringToArgv} from 'string-argv' +import path from 'path' export class DependencyGraphConfig { getDependencyGraphOption(): DependencyGraphOption { @@ -218,17 +219,33 @@ export class BuildScanConfig { } } -export function getGradleVersion(): string { - return core.getInput('gradle-version') -} +export class GradleExecutionConfig { + getGradleVersion(): string { + return core.getInput('gradle-version') + } -export function getBuildRootDirectory(): string { - return core.getInput('build-root-directory') -} + getBuildRootDirectory(): string { + const baseDirectory = getWorkspaceDirectory() + const buildRootDirectoryInput = core.getInput('build-root-directory') + const resolvedBuildRootDirectory = + buildRootDirectoryInput === '' + ? path.resolve(baseDirectory) + : path.resolve(baseDirectory, buildRootDirectoryInput) + return resolvedBuildRootDirectory + } -export function getArguments(): string[] { - const input = core.getInput('arguments') - return parseArgsStringToArgv(input) + getArguments(): string[] { + const input = core.getInput('arguments') + return parseArgsStringToArgv(input) + } + + getDependencyResolutionTask(): string { + return core.getInput('dependency-resolution-task') || ':ForceDependencyResolutionPlugin_resolveAllDependencies' + } + + getAdditionalArguments(): string { + return core.getInput('additional-arguments') + } } // Internal parameters diff --git a/sources/src/setup-gradle/main.ts b/sources/src/setup-gradle/main.ts index 6fca084..19e8eff 100644 --- a/sources/src/setup-gradle/main.ts +++ b/sources/src/setup-gradle/main.ts @@ -3,7 +3,7 @@ import * as core from '@actions/core' import * as setupGradle from '../setup-gradle' import * as gradle from '../execution/gradle' import * as dependencyGraph from '../dependency-graph' -import {BuildScanConfig, CacheConfig, DependencyGraphConfig, getArguments} from '../input-params' +import {BuildScanConfig, CacheConfig, DependencyGraphConfig, GradleExecutionConfig} from '../input-params' /** * The main entry point for the action, called by Github Actions for the step. @@ -16,8 +16,12 @@ export async function run(): Promise { // Configure the dependency graph submission await dependencyGraph.setup(new DependencyGraphConfig()) - const args: string[] = getArguments() - await gradle.provisionAndMaybeExecute(args) + const config = new GradleExecutionConfig() + await gradle.provisionAndMaybeExecute( + config.getGradleVersion(), + config.getBuildRootDirectory(), + config.getArguments() + ) } catch (error) { core.setFailed(String(error)) if (error instanceof Error && error.stack) {