Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
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
31 changes: 31 additions & 0 deletions Tasks/GradleAuthenticateV0/_buildConfigs/Wif/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "gradleauthenticate",
"version": "0.1.0",
"description": "Azure Pipelines Gradle Authenticate Task",
"main": "gradleauth.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Microsoft/azure-pipelines-tasks.git"
},
"author": "Microsoft Corporation",
"license": "ISC",
"bugs": {
"url": "https://github.com/Microsoft/azure-pipelines-tasks/issues"
},
"homepage": "https://github.com/Microsoft/azure-pipelines-tasks#readme",
"dependencies": {
"@types/node": "^20.3.1",
"@types/mocha": "^5.2.7",
"@types/uuid": "^8.3.0",
"@types/q": "^1.5.2",
"azure-pipelines-tasks-artifacts-common": "^2.243.1",
"azure-pipelines-task-lib": "^4.15.0",
"fs-extra": "^0.30.0"
},
"devDependencies": {
"typescript": "5.1.6"
}
}
Loading