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
23 changes: 19 additions & 4 deletions tasks/JFrogCliV2/jfrogCliRun.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,19 @@ async function RunTaskCbk(cliPath) {
process.env.JFROG_CLI_BUILD_NAME = tl.getVariable('Build.DefinitionName');
process.env.JFROG_CLI_BUILD_NUMBER = tl.getVariable('Build.BuildNumber');

serverId = utils.assembleUniqueServerId('jfrog_cli_cmd');
await utils.configureDefaultJfrogServer(serverId, cliPath, requiredWorkDir);
let configurationName = tl.getInput('configurationName', false);
if (configurationName) {
// Reuse an existing JFrog CLI configuration instead of creating a new one.
serverId = configurationName;
} else {
if (!tl.getInput('jfrogPlatformConnection', false)) {
tl.setResult(tl.TaskResult.Failed, "Either 'JFrog Platform service connection' or 'Configuration Name' must be provided.");
return;
}
serverId = utils.assembleUniqueServerId('jfrog_cli_cmd');
await utils.configureDefaultJfrogServer(serverId, cliPath, requiredWorkDir);
}
tl.setVariable('JFROG_CLI_CONFIG_NAME', serverId, false, true);

let cliCommandsList = tl.getInput('command', true).split('\n');
try {
Expand All @@ -58,7 +69,9 @@ async function RunTaskCbk(cliPath) {
tl.TaskResult.Failed,
"Unexpected JFrog CLI command prefix. Expecting the command to start with 'jf '. The command received is: " + cliCommand,
);
utils.taskDefaultCleanup(cliPath, requiredWorkDir, [serverId]);
if (!tl.getBoolInput('keepConfig')) {
utils.taskDefaultCleanup(cliPath, requiredWorkDir, [serverId]);
}
return;
}
// Remove 'jf' and space from the beginning of the command string, so we can use the CLI's path
Expand All @@ -77,7 +90,9 @@ async function RunTaskCbk(cliPath) {
} catch (executionException) {
tl.setResult(tl.TaskResult.Failed, executionException);
} finally {
utils.taskDefaultCleanup(cliPath, requiredWorkDir, [serverId]);
if (!tl.getBoolInput('keepConfig')) {
utils.taskDefaultCleanup(cliPath, requiredWorkDir, [serverId]);
}
}
tl.setResult(tl.TaskResult.Succeeded, 'Command Succeeded.', cliPath);
}
20 changes: 18 additions & 2 deletions tasks/JFrogCliV2/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
"type": "connectedService:jfrogPlatformService",
"label": "JFrog Platform service connection",
"defaultValue": "",
"required": true,
"helpMarkDown": "JFrog Platform service connection to use in the command."
"required": false,
"helpMarkDown": "JFrog Platform service connection to use in the command. Required unless 'Configuration Name' is provided to reuse an existing JFrog CLI configuration."
},
{
"name": "useCustomVersion",
Expand Down Expand Up @@ -63,6 +63,22 @@
"defaultValue": "",
"required": false,
"helpMarkDown": "The working directory where the command will run. When empty, the value of '$(System.DefaultWorkingDirectory)' is used."
},
{
"name": "configurationName",
"type": "string",
"label": "Configuration Name",
"defaultValue": "",
"required": false,
"helpMarkDown": "Reuse an existing JFrog CLI configuration by name instead of creating a new one from 'JFrog Platform service connection'. The named configuration must already exist in the JFrog CLI's config store (for example, created by a previous run of this task with 'Keep Configuration' enabled)."
},
{
"name": "keepConfig",
"type": "boolean",
"label": "Keep Configuration",
"defaultValue": "false",
"required": false,
"helpMarkDown": "Skip removing the JFrog CLI configuration when this task completes, so it can be reused by later steps via 'Configuration Name'. Use the 'JFROG_CLI_CONFIG_NAME' variable set by this task to reference it, and run a teardown task with 'Configuration Name' set and 'Keep Configuration' disabled to remove it at the end of the pipeline."
}
],
"execution": {
Expand Down
11 changes: 11 additions & 0 deletions tests/resources/jfrogCliV2PersistConfig/keepConfigSetup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const testUtils = require('../../testUtils');

// Creates a new JFrog CLI configuration and keeps it around (does not clean up on completion),
// so a later task invocation can reuse it via 'configurationName'.
let inputs = {
jfrogPlatformConnection: 'mock-service',
keepConfig: true,
command: 'jf rt ping',
};

testUtils.runPlatformTask(testUtils.genericCli, {}, inputs);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const testUtils = require('../../testUtils');

// Neither 'jfrogPlatformConnection' nor 'configurationName' is provided - the task must fail clearly.
let inputs = {
command: 'jf rt ping',
};

testUtils.runPlatformTask(testUtils.genericCli, {}, inputs);
10 changes: 10 additions & 0 deletions tests/resources/jfrogCliV2PersistConfig/reuseConfigurationName.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const testUtils = require('../../testUtils');

// Reuses the configuration created (and kept) by 'keepConfigSetup.js', identified by name,
// without providing a service connection - and tears it down (keepConfig defaults to false).
let inputs = {
configurationName: process.env.ADO_TEST_PERSISTED_CONFIG_NAME,
command: 'jf rt ping',
};

testUtils.runPlatformTask(testUtils.genericCli, {}, inputs);
40 changes: 40 additions & 0 deletions tests/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,46 @@ describe('JFrog Artifactory Extension Tests', (): void => {
);
});

describe('JFrog CLI V2 Persist Config Tests', (): void => {
const testDir: string = 'jfrogCliV2PersistConfig';

runSyncTest(
'Fails clearly when neither JFrog Platform connection nor Configuration Name is provided',
async (): Promise<void> => {
await mockTask(testDir, 'missingConnectionAndConfigName', true);
},
TestUtils.isSkipTest('generic'),
);

runSyncTest(
'Keeps and reuses the JFrog CLI configuration across two task invocations',
async (): Promise<void> => {
// Run the setup task directly (instead of via mockTask) so its stdout can be inspected
// for the JFROG_CLI_CONFIG_NAME value emitted via "##vso[task.setvariable ...]".
const taskJsonDummy: string = join(__dirname, 'resources', 'task.json');
const setupPath: string = join(__dirname, 'resources', testDir, 'keepConfigSetup.js');
const setupRunner: adoMockTest.MockTestRunner = new adoMockTest.MockTestRunner(setupPath, taskJsonDummy);
await setupRunner.runAsync();
tasksOutput += setupRunner.stderr + '\n' + setupRunner.stdout;
assert.ok(setupRunner.succeeded, '\nFailure in: ' + setupPath + '.\n' + tasksOutput);

const configNameMatch: RegExpMatchArray | null = setupRunner.stdout.match(
/##vso\[task\.setvariable variable=JFROG_CLI_CONFIG_NAME;[^\]]*\](\S+)/,
);
assert.ok(configNameMatch, 'Expected JFROG_CLI_CONFIG_NAME to be set in task output.\n' + setupRunner.stdout);
process.env.ADO_TEST_PERSISTED_CONFIG_NAME = (configNameMatch as RegExpMatchArray)[1];
try {
// Second invocation reuses the configuration by name, without a service connection,
// and tears it down (keepConfig defaults to false).
await mockTask(testDir, 'reuseConfigurationName');
} finally {
delete process.env.ADO_TEST_PERSISTED_CONFIG_NAME;
}
},
TestUtils.isSkipTest('generic'),
);
});

describe('Tools Installer Tests', (): void => {
runSyncTest(
'Download CLI',
Expand Down
Loading