Skip to content

Commit

Permalink
Gradle V4: New major version of the Gradle task (#20596)
Browse files Browse the repository at this point in the history
* Removed references to jacoco in code coverage for Gradle V4

* Removed references to Code Coverage in GradleV4

* Removed redundant functions, and references to code coverage in Tests

* Updated the minor version of the task

* Updated CODEOWNERS and make-options.json

* task build after adding task under node20 in make-options.json

* update to  make-options.json

* updated minor version

* removed gradlev4 from local packages section in make-options.json, and removed _generated/GradleV4

* Added .npmrc and updated packages

* Added .npmrc and updated packages

* updated CODEOWNERS file

* updated broken link in README

* added _generated/README

* updated minor version
  • Loading branch information
sahilbhattMS authored Jan 20, 2025
1 parent 3ca51a0 commit 6b39cf3
Show file tree
Hide file tree
Showing 205 changed files with 27,631 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,8 @@ Tasks/GradleV2/ @microsoft/azure-pipelines-tasks-and-agent @tarunramsinghani

Tasks/GradleV3/ @microsoft/azure-pipelines-tasks-and-agent @tarunramsinghani

Tasks/GradleV4/ @microsoft/azure-pipelines-tasks-and-agent @tarunramsinghani

Tasks/GruntV0/ @microsoft/azure-pipelines-tasks-and-agent @tarunramsinghani

Tasks/GulpV0/ @microsoft/azure-pipelines-tasks-and-agent @tarunramsinghani
Expand Down
3 changes: 3 additions & 0 deletions Tasks/GradleV4/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
registry=https://pkgs.dev.azure.com/mseng/PipelineTools/_packaging/PipelineTools_PublicPackages/npm/registry/

always-auth=true
96 changes: 96 additions & 0 deletions Tasks/GradleV4/Modules/environment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import * as tl from 'azure-pipelines-task-lib/task';
import * as javaCommon from 'azure-pipelines-tasks-java-common/java-common';
import { IExecOptions, ToolRunner } from 'azure-pipelines-task-lib/toolrunner';

// Setting the access token env var to both VSTS and AZURE_ARTIFACTS for
// backwards compatibility with repos that already use the older env var.
const accessTokenEnvSettingLegacy: string = 'VSTS_ENV_ACCESS_TOKEN';
const accessTokenEnvSetting: string = 'AZURE_ARTIFACTS_ENV_ACCESS_TOKEN';

/**
* Extract system access token from endpoint
* @returns {string} access token to access account feeds or empty string
*/
function getSystemAccessToken(): string {
tl.debug('Getting credentials for account feeds');

const authorizationData: tl.EndpointAuthorization = tl.getEndpointAuthorization('SYSTEMVSSCONNECTION', false);

if (authorizationData && authorizationData.scheme === 'OAuth') {
tl.debug('Got auth token');
return authorizationData.parameters['AccessToken'];
}

tl.warning(tl.loc('FeedTokenUnavailable'));

return '';
}

/**
* Update JAVA_HOME if user selected specific JDK version or set path manually
* @param {string} javaHomeSelection - value of the `Set JAVA_HOME by` task input
*/
export function setJavaHome(javaHomeSelection: string): void {
let specifiedJavaHome: string;
let javaTelemetryData: any = {};

if (javaHomeSelection === 'JDKVersion') {
tl.debug('Using JDK version to find and set JAVA_HOME');

const jdkVersion: string = tl.getInput('jdkVersion');
const jdkArchitecture: string = tl.getInput('jdkArchitecture');

javaTelemetryData = { 'jdkVersion': jdkVersion };

if (jdkVersion !== 'default') {
specifiedJavaHome = javaCommon.findJavaHome(jdkVersion, jdkArchitecture);
}
} else {
tl.debug('Using path from user input to set JAVA_HOME');

const jdkUserInputPath: string = tl.getPathInput('jdkUserInputPath', true, true);
specifiedJavaHome = jdkUserInputPath;

javaTelemetryData = { 'jdkVersion': 'custom' };
}

javaCommon.publishJavaTelemetry('Gradle', javaTelemetryData);

if (specifiedJavaHome) {
tl.debug(`Set JAVA_HOME to ${specifiedJavaHome}`);
process.env['JAVA_HOME'] = specifiedJavaHome;
}
}

/**
* Get execution options for Gradle.
*
* This function does the following things:
* - Get a snapshot of the process environment variables
* - Update the snapshot to include system access token
* @returns {IExecOptions} object with execution options for Gradle
*/
export function getExecOptions(): IExecOptions {
const env: NodeJS.ProcessEnv = process.env;
env[accessTokenEnvSetting] = env[accessTokenEnvSettingLegacy] = getSystemAccessToken();
return <IExecOptions>{
env: env
};
}

/**
* Configure the JVM associated with this run.
* @param {string} gradleOptions - Gradle options provided by the user
*/
export function setGradleOpts(gradleOptions: string): void {
if (gradleOptions) {
process.env['GRADLE_OPTS'] = gradleOptions;
tl.debug(`GRADLE_OPTS is now set to ${gradleOptions}`);
}
}

export function extractGradleVersion(str: string): string {
const regex = /^Gradle (?<version>\d+\.\d+(?:\.\d+)?.*$)/m;
const match = str.match(regex);
return match?.groups?.version || 'unknown';
}
36 changes: 36 additions & 0 deletions Tasks/GradleV4/Modules/project-configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as fs from 'fs';
import * as os from 'os';
import * as tl from 'azure-pipelines-task-lib/task';


/**
* Configure wrapper script:
* - For Windows - set `*.bat` extension
* - For Linux/macOS - set script as executable
* @param {string} wrapperScript - Relative path from the repository root to the Gradle Wrapper script.
* @returns {string} path to the wrapper script
*/
export function configureWrapperScript(wrapperScript: string): string {
let script: string = wrapperScript;
const isWindows: RegExpMatchArray = os.type().match(/^Win/);

if (isWindows) {
// append .bat extension name on Windows platform
if (!script.endsWith('bat')) {
tl.debug('Append .bat extension name to gradlew script.');
script += '.bat';
}
}

if (fs.existsSync(script)) {
try {
// Make sure the wrapper script is executable
fs.accessSync(script, fs.constants.X_OK)
} catch (err) {
// If not, show warning and chmodding the gradlew file to make it executable
tl.warning(tl.loc('chmodGradlew'));
fs.chmodSync(script, '755');
}
}
return script;
}
46 changes: 46 additions & 0 deletions Tasks/GradleV4/Modules/publish-test-results.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import * as tl from 'azure-pipelines-task-lib/task';

const TESTRUN_SYSTEM = 'VSTS - gradle';


/**
* Publish unit tests results to Azure DevOps
* @param {boolean} publishJUnitResults - if set to `true`, the result of the unit tests will be published otherwise publishing will be skipped
* @param {string} testResultsFiles - pattern for test results files
*/
export function publishTestResults(publishJUnitResults: boolean, testResultsFiles: string): number {
if (publishJUnitResults) {
let matchingTestResultsFiles: string[] = [];

// check for pattern in testResultsFiles
if (testResultsFiles.indexOf('*') >= 0 || testResultsFiles.indexOf('?') >= 0) {
tl.debug('Pattern found in testResultsFiles parameter');

const buildFolder: string = tl.getVariable('System.DefaultWorkingDirectory');

// The find options are as default, except the `skipMissingFiles` option is set to `true`
// so there will be a warning instead of an error if an item will not be found
const findOptions: tl.FindOptions = {
allowBrokenSymbolicLinks: false,
followSpecifiedSymbolicLink: true,
followSymbolicLinks: true,
skipMissingFiles: true
};

matchingTestResultsFiles = tl.findMatch(buildFolder, testResultsFiles, findOptions, { matchBase: true });
} else {
tl.debug('No pattern found in testResultsFiles parameter');
matchingTestResultsFiles = [testResultsFiles];
}

if (!matchingTestResultsFiles || matchingTestResultsFiles.length === 0) {
console.log(tl.loc('NoTestResults', testResultsFiles));
return 0;
}

const tp: tl.TestPublisher = new tl.TestPublisher('JUnit');
const testRunTitle = tl.getInput('testRunTitle');

tp.publish(matchingTestResultsFiles, 'true', '', '', testRunTitle, 'true', TESTRUN_SYSTEM);
}
}
32 changes: 32 additions & 0 deletions Tasks/GradleV4/Modules/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ITaskResult, ICodeAnalysisResult } from '../interfaces';
import { TaskResult } from 'azure-pipelines-task-lib';

/**
* Resolve task status based on code analysis run results
* @param {ICodeAnalysisResult} codeAnalysisResult - Code analysis run data
* @returns {ITaskResult} task status and message
*/
export function resolveTaskResult(codeAnalysisResult: ICodeAnalysisResult): ITaskResult {
let status: TaskResult;
let message: string = '';

if (codeAnalysisResult.gradleResult === 0) {
status = TaskResult.Succeeded;
message = 'Build succeeded.';
} else if (codeAnalysisResult.gradleResult === -1) {
status = TaskResult.Failed;

if (codeAnalysisResult.statusFailed) {
message = `Code analysis failed. Gradle exit code: ${codeAnalysisResult.gradleResult}. Error: ${codeAnalysisResult.analysisError}`;
} else {
message = `Build failed. Gradle exit code: ${codeAnalysisResult.gradleResult}`;
}
}

const taskResult: ITaskResult = {
status: status,
message: message
};

return taskResult;
}
71 changes: 71 additions & 0 deletions Tasks/GradleV4/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Build your code using Gradle in Azure Pipelines

### Parameters for Gradle build task are explained below

- **Gradle Wrapper :** This is a Required field. The location in the repository of the gradlew wrapper used for the build. Note that on Windows build agents (including the hosted pool), you must use the `gradlew.bat` wrapper. Xplat build agents use the `gradlew` shell script. To Know more [click here](https://docs.gradle.org/current/userguide/gradle_wrapper.html)

- **Options :** Specify any command line options you want to pass to the Gradle wrapper. To know more [click here](https://docs.gradle.org/current/userguide/gradle_command_line.html)

- **Goal(s) :** The task(s) for Gradle to execute. A list of tasks can be taken from `gradlew tasks` issued from a command prompt. To know more [click here](https://docs.gradle.org/current/userguide/tutorial_using_tasks.html)

#### JUnit Test Results
Use the next three options to manage your JUnit test results in Azure Pipelines

- **Publish to Azure Pipelines :** Select this option to publish JUnit Test results produced by the Gradle build to Azure Pipelines/TFS. Each test result file matching `Test Results Files` will be published as a test run in Azure Pipelines.

- **Test Results Files :** This option will appear if you select the above option. Here, provide Test results files path. Wildcards can be used. For example, `**/TEST-*.xml` for all xml files whose name starts with `TEST-."`

- **Test Run Title :** This option will appear if you select the `Publish to Azure Pipelines/TFS` option. Here provide a name for the Test Run

#### Advanced
Use the next options to manage your `JAVA_HOME` attribute by JDK Version and Path

- **Working Directory :** Directory on the build agent where the Gradle wrapper will be invoked from. Defaults to the repository root.

- **Set JAVA_HOME by :** Select to set `JAVA_HOME` either by providing a path or let Azure Pipelines set the `JAVA_HOME` based on JDK version choosen. By default it is set to `JDK Version`

- **JDK Version :** Here provide the PATH to `JAVA_HOME` if you want to set it by path or select the appropriate JDK version.

- **JDK Architecture :** Select the approriate JDK Architecture. By default it is set to `x86`

#### Code Analysis

- **Run SonarQube Analysis :** You can choose to run SonarQube analysis after executing the current goals. 'install' or 'package' goals should be executed first. To know more about this option [click here](https://devblogs.microsoft.com/devops/the-gradle-build-task-now-supports-sonarqube-analysis/)

- **Run Checkstyle :** You can choose to run the Checkstyle static code analysis tool, which checks the compliance of your source code with coding rules. You will receive a code analysis report with the number of violations detected, as well as the original report files if there were any violations.

- **Run PMD :** You can choose to run the PMD static code analysis tool, which examines your source code for possible bugs. You will receive a code analysis report with the number of violations detected, as well as the original report files if there were any violations.

- **Run FindBugs :** You can choose to run the FindBugs static code analysis tool, which examines the bytecode of your program for possible bugs. You will receive a code analysis report with the number of violations detected, as well as the original report files if there were any violations.

### Q&A

#### How do I generate a wrapper from my Gradle project?

The Gradle wrapper allows the build agent to download and configure the exact Gradle environment that is checked into the repository without having any software configuration on the build agent itself other than the JVM.

- **1.** Create the Gradle wrapper by issuing the following command from the root project directory where your build.gradle resides:
`jamal@fabrikam> gradle wrapper`


- **2.** Upload your Gradle wrapper to your remote repository.

There is a binary artifact that is generated by the gradle wrapper (located at `gradle/wrapper/gradle-wrapper.jar`). This binary file is small and doesn't require updating. If you need to change the Gradle configuration run on the build agent, you update the `gradle-wrapper.properties`.

The repository should look something like this:

```ssh
|-- gradle/
`-- wrapper/
`-- gradle-wrapper.jar
`-- gradle-wrapper.properties
|-- src/
|-- .gitignore
|-- build.gradle
|-- gradlew
|-- gradlew.bat
```




Loading

0 comments on commit 6b39cf3

Please sign in to comment.