Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -474,3 +474,5 @@ Tasks/XamariniOSV2/ @microsoft/azure-pipelines-tasks-and-agent @tarunramsing
Tasks/XamarinTestCloudV1/ @microsoft/azure-pipelines-tasks-and-agent @tarunramsinghani

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

Tasks/GradleAuthenticateV0/ @microsoft/azure-artifacts-packages
5 changes: 5 additions & 0 deletions Tasks/GradleAuthenticateV0/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
scripts-prepend-node-path=true

registry=https://pkgs.dev.azure.com/mseng/PipelineTools/_packaging/PipelineTools_PublicPackages/npm/registry/

always-auth=true
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"loc.friendlyName": "Gradle Authenticate",
"loc.helpMarkDown": "[Learn more about this task](https://docs.microsoft.com/azure/devops/pipelines/tasks/package/gradle-authenticate)",
"loc.description": "Provides credentials for Azure Artifacts feeds and external Gradle/Maven repositories",
"loc.instanceNameFormat": "Gradle Authenticate",
"loc.input.label.artifactsFeeds": "Feeds",
"loc.input.label.gradleServiceConnections": "Credentials for repositories outside this organization/collection",
"loc.input.help.gradleServiceConnections": "Credentials to use for external repositories located in the project's build.gradle.",
"loc.messages.Warning_FeedEntryAlreadyExists": "The settings for the feed or repository '%s' already exists in the gradle.properties file.",
"loc.messages.Warning_NoEndpointsToAuth": "No repositories were selected to authenticate, please check your task configuration.",
"loc.messages.Warning_TokenNotGenerated": "Unable to use a federated token",
"loc.messages.Info_GeneratingExternalRepositories": "Generating configs for %s external repositories.",
"loc.messages.Info_GeneratingInternalFeeds": "Generating configs for %s internal feeds.",
"loc.messages.Info_GradleUserHomeFolderDoesntExist": ".gradle folder not found at location %s, creating new folder.",
"loc.messages.Info_GradlePropertiesRead": "Adding authentication to gradle.properties file %s.",
"loc.messages.Info_CreatingGradleProperties": "Creating new gradle.properties at path %s.",
"loc.messages.Info_WritingToGradleProperties": "Writing new gradle.properties with added authentication.",
"loc.messages.Info_AddingFederatedFeedAuth": "Adding auth information from federated service connection %s for feed %s",
"loc.messages.Info_SuccessAddingFederatedFeedAuth": "Successfully added auth for feed %s with federated credentials.",
"loc.messages.Error_InvalidServiceConnection": "The service connection for %s is invalid.",
"loc.messages.Error_FailedCleanupGradle": "Failed to delete credentials from the gradle.properties file: %s",
"loc.messages.Error_FailedToGetServiceConnectionAuth": "Unable to get federated credentials from service connection: %s."
}
158 changes: 158 additions & 0 deletions Tasks/GradleAuthenticateV0/Tests/L0.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import fs = require("fs");
import assert = require("assert");
import path = require("path");
import * as tl from "azure-pipelines-task-lib/task";
import * as ttm from "azure-pipelines-task-lib/mock-test";

const testUserHomeDir = path.join(__dirname, "USER_HOME");
const gradleDirName = ".gradle"
const gradleDirPath = path.join(testUserHomeDir, gradleDirName);
const gradlePropertiesName = "gradle.properties";
const gradlePropertiesPath = path.join(gradleDirPath, gradlePropertiesName);
const gradlePropertiesOtherFeedName = path.join(__dirname, "Samples", "gradlePropertiesOtherFeedName.properties");
const gradlePropertiesFeedName1 = path.join(__dirname, "Samples", "gradlePropertiesFeedName1.properties");

const usernameRegex = /Username=/mig;
const passwordRegex = /Password=/mig;

describe("authenticate azure artifacts feeds for gradle", function() {
this.timeout(parseInt(process.env.TASK_TEST_TIMEOUT) || 20000);
var env;

this.beforeAll(async () => {
env = Object.assign({}, process.env);
process.env["USERPROFILE"] = testUserHomeDir;
process.env["HOME"] = testUserHomeDir;
});

beforeEach(async () => {
tl.mkdirP(gradleDirPath);
})

this.afterAll(async () => {
process.env = env;
})

afterEach(async () => {
tl.rmRF(gradleDirPath);
});

it("it should create a new gradle.properties in the .gradle folder and add auth for 1 feed.", async () => {
this.timeout(1000);

let tp: string = path.join(__dirname, "L0AuthGradleProperties.js");

let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);

await tr.runAsync();

assert.equal(tl.ls(null, [gradleDirPath]).length, 1, "Should have one file.");
const gradlePropertiesStats = tl.stats(gradlePropertiesPath);
assert(gradlePropertiesStats && gradlePropertiesStats.isFile(), "gradle.properties file should be created.");

const data = fs.readFileSync(gradlePropertiesPath, 'utf-8');

assert.equal(data.match(usernameRegex).length, 1, "Only one username entry should be created.");
assert.equal(data.match(passwordRegex).length, 1, "Only one password entry should be created.");
assert(data.includes("feedName1Username="), "feedName1Username entry should be present.");
assert(data.includes("feedName1Password="), "feedName1Password entry should be present.");

assert(tr.stderr.length === 0, "should not have written to stderr");
assert(tr.succeeded, "task should have succeeded");
});

it("it should read the existing gradle.properties and add auth for 1 new feed", async () => {
this.timeout(1000);

let tp: string = path.join(__dirname, "L0AuthGradlePropertiesExists.js");

let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);

tl.cp(gradlePropertiesOtherFeedName, gradlePropertiesPath);

await tr.runAsync();

assert.equal(tl.ls(null, [gradleDirPath]).length, 1, "Should have one file.");
const gradlePropertiesStats = tl.stats(gradlePropertiesPath);
assert(gradlePropertiesStats && gradlePropertiesStats.isFile(), "gradle.properties file should be present.");

const data = fs.readFileSync(gradlePropertiesPath, 'utf-8');

assert.equal(data.match(usernameRegex).length, 2, "2 username entries should be present.");
assert.equal(data.match(passwordRegex).length, 2, "2 password entries should be present.");
assert(data.includes("feedName1Username="), "feedName1Username entry should be present.");
assert(data.includes("otherFeedNameUsername="), "otherFeedNameUsername entry should not be deleted.");

assert(tr.stderr.length === 0, "should not have written to stderr");
assert(tr.succeeded, "task should have succeeded");
});

it("it should read the existing gradle.properties and not add any new entries.", async () => {
this.timeout(1000);

let tp: string = path.join(__dirname, "L0AuthGradlePropertiesExists.js");

let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);

tl.cp(gradlePropertiesFeedName1, gradlePropertiesPath);

await tr.runAsync();

assert.equal(tl.ls(null, [gradleDirPath]).length, 1, "Should have one file.");
const gradlePropertiesStats = tl.stats(gradlePropertiesPath);
assert(gradlePropertiesStats && gradlePropertiesStats.isFile(), "gradle.properties file should be present.");

const data = fs.readFileSync(gradlePropertiesPath, 'utf-8');

assert.equal(data.match(usernameRegex).length, 1, "Only one username entry should be present.");
assert.equal(data.match(passwordRegex).length, 1, "Only one password entry should be present.");

assert(tr.stderr.length === 0, "should not have written to stderr");
assert(tr.stdOutContained("vso[task.issue type=warning;source=TaskInternal;]loc_mock_Warning_FeedEntryAlreadyExists"), "Entry already exists warning should be displayed");
assert(tr.succeeded, "task should have succeeded");
});

it("it should create a new gradle.properties in the .gradle folder and add auth for 2 different types of service connections.", async () => {
this.timeout(1000);

let tp: string = path.join(__dirname, "L0ServiceConnections.js");

let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);

await tr.runAsync();

assert.equal(tl.ls(null, [gradleDirPath]).length, 1, "Should have one file.");
const gradlePropertiesStats = tl.stats(gradlePropertiesPath);
assert(gradlePropertiesStats && gradlePropertiesStats.isFile(), "gradle.properties file should be created.");

const data = fs.readFileSync(gradlePropertiesPath, 'utf-8');

assert.equal(data.match(usernameRegex).length, 2, "2 username entries should be created.");
assert.equal(data.match(passwordRegex).length, 2, "2 password entries should be created.");

assert(data.includes("tokenBasedUsername=AzureDevOps"), "tokenBased username should be AzureDevOps.");
assert(data.includes("tokenBasedPassword=--token--"), "tokenBased password should be the token.");

assert(data.includes("usernamePasswordBasedUsername=--testUserName--"), "usernamePasswordBased username should be set.");
assert(data.includes("usernamePasswordBasedPassword=--testPassword--"), "usernamePasswordBased password should be set.");

assert(tr.stderr.length === 0, "should not have written to stderr");
assert(tr.succeeded, "task should have succeeded");
});

it("it should warn if no inputs are provided.", async () => {
this.timeout(1000);

let tp: string = path.join(__dirname, "L0EmptyInput.js");

let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);

await tr.runAsync();

assert.equal(tl.ls(null, [gradleDirPath]).length, 0, "gradle.properties file should not be created.");

assert(tr.stderr.length === 0, "should not have written to stderr");
assert(tr.succeeded, "task should have succeeded");
assert(tr.stdOutContained("vso[task.issue type=warning;source=TaskInternal;]loc_mock_Warning_NoEndpointsToAuth"), "The no endpoints warning should be displayed");
});
});
36 changes: 36 additions & 0 deletions Tasks/GradleAuthenticateV0/Tests/L0AuthGradleProperties.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import tmrm = require("azure-pipelines-task-lib/mock-run");
import path = require("path");

let taskPath = path.join(__dirname, "..", "gradleauth.js");
let tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);

const testUserHomeDir = path.join(__dirname, "USER_HOME");
const gradleDirName = ".gradle"
const gradleDirPath = path.join(testUserHomeDir, gradleDirName);
const gradlePropertiesName = "gradle.properties";
const gradlePropertiesPath = path.join(gradleDirPath, gradlePropertiesName);

// Set inputs
tr.setInput("artifactsFeeds", "feedName1");
tr.setInput("verbosity", "verbose");
tr.setInput("gradleServiceConnections", "");

let mockApi = {
getSystemAccessToken: () => {
return "token";
}
};
tr.registerMock('azure-pipelines-tasks-artifacts-common/webapi', mockApi);

// provide answers for task mock
tr.setAnswers({
osType: {
"osType": "Windows NT"
},
exist: {
[gradleDirPath]: false,
[gradlePropertiesPath]: false
}
});

tr.run();
36 changes: 36 additions & 0 deletions Tasks/GradleAuthenticateV0/Tests/L0AuthGradlePropertiesExists.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import tmrm = require("azure-pipelines-task-lib/mock-run");
import path = require("path");

let taskPath = path.join(__dirname, "..", "gradleauth.js");
let tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);

const testUserHomeDir = path.join(__dirname, "USER_HOME");
const gradleDirName = ".gradle"
const gradleDirPath = path.join(testUserHomeDir, gradleDirName);
const gradlePropertiesName = "gradle.properties";
const gradlePropertiesPath = path.join(gradleDirPath, gradlePropertiesName);

// Set inputs
tr.setInput("artifactsFeeds", "feedName1");
tr.setInput("verbosity", "verbose");
tr.setInput("gradleServiceConnections", "");

let mockApi = {
getSystemAccessToken: () => {
return "token";
}
};
tr.registerMock('azure-pipelines-tasks-artifacts-common/webapi', mockApi);

// provide answers for task mock
tr.setAnswers({
osType: {
"osType": "Windows NT"
},
exist: {
[gradleDirPath]: true,
[gradlePropertiesPath]: true
}
});

tr.run();
25 changes: 25 additions & 0 deletions Tasks/GradleAuthenticateV0/Tests/L0EmptyInput.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import tmrm = require("azure-pipelines-task-lib/mock-run");
import path = require("path");

let taskPath = path.join(__dirname, "..", "gradleauth.js");
let tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);

// Set inputs
tr.setInput("artifactsFeeds", "");
tr.setInput("verbosity", "verbose");
tr.setInput("gradleServiceConnections", "");

let mockApi = {
getSystemAccessToken: () => {
return "token";
}
};
tr.registerMock('azure-pipelines-tasks-artifacts-common/webapi', mockApi);

tr.setAnswers({
osType: {
"osType": "Windows NT"
}
});

tr.run();
56 changes: 56 additions & 0 deletions Tasks/GradleAuthenticateV0/Tests/L0ServiceConnections.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import tmrm = require("azure-pipelines-task-lib/mock-run");
import path = require("path");

let taskPath = path.join(__dirname, "..", "gradleauth.js");
let tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);

const testUserHomeDir = path.join(__dirname, "USER_HOME");
const gradleDirName = ".gradle"
const gradleDirPath = path.join(testUserHomeDir, gradleDirName);
const gradlePropertiesName = "gradle.properties";
const gradlePropertiesPath = path.join(gradleDirPath, gradlePropertiesName);

// Set inputs
tr.setInput("artifactsFeeds", "");
tr.setInput("gradleServiceConnections", "tokenBased,usernamePasswordBased");
tr.setInput("verbosity", "verbose");

let mockApi = {
getSystemAccessToken: () => {
return "token";
}
};
tr.registerMock('azure-pipelines-tasks-artifacts-common/webapi', mockApi);

process.env["ENDPOINT_URL_tokenBased"] = "https://endpoint";
process.env["ENDPOINT_DATA_tokenBased_REPOSITORYID"] = "tokenBased";
process.env["ENDPOINT_AUTH_SCHEME_tokenBased"] = "token";
process.env["ENDPOINT_AUTH_tokenBased"] = JSON.stringify({
"parameters": {
"apitoken": "--token--"
}
});

process.env["ENDPOINT_URL_usernamePasswordBased"] = "https://endpoint";
process.env["ENDPOINT_DATA_usernamePasswordBased_REPOSITORYID"] = "usernamePasswordBased";
process.env["ENDPOINT_AUTH_SCHEME_usernamePasswordBased"] = "usernamepassword";
process.env["ENDPOINT_AUTH_usernamePasswordBased"] = JSON.stringify({
"parameters": {
"username": "--testUserName--",
"password": "--testPassword--"
}
});

// provide answers for task mock
tr.setAnswers({
osType: {
"osType": "Windows NT"
},
exist: {
[gradleDirPath]: false,
[gradlePropertiesPath]: false
}
});


tr.run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Azure Artifacts credentials for feedName1
feedName1Username=AzureDevOps
feedName1Password=existingToken
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Azure Artifacts credentials for otherFeedName
otherFeedNameUsername=AzureDevOps
otherFeedNamePassword=existingToken
Loading