Skip to content

Commit da7423c

Browse files
authored
Server configuration improvements (#227)
1 parent e7cc33a commit da7423c

File tree

7 files changed

+212
-16
lines changed

7 files changed

+212
-16
lines changed

README.md

+21
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
- [Setting JFrog CLI Version](#setting-jfrog-cli-version)
2020
- [Setting the JFrog Project Key](#setting-the-jfrog-project-key)
2121
- [Downloading JFrog CLI from Artifactory](#downloading-jfrog-cli-from-artifactory)
22+
- [Custom Server ID and Multi-Configuration](#custom-server-id-and-multi-configuration)
2223
- [JFrog Job Summary](#jfrog-job-summary)
2324
- [Code Scanning Alerts](#code-scanning-alerts)
2425
- [Example Projects](#example-projects)
@@ -301,6 +302,26 @@ Here's how you do this:
301302
```
302303
</details>
303304

305+
<details>
306+
<summary>Custom Server ID and Multi-Configuration</summary>
307+
308+
### Custom Server ID and Multi-Configuration
309+
310+
The action configures JFrog CLI with a default server ID, which is unique for each run of a workflow.
311+
312+
You may override the default server ID by providing a custom server ID:
313+
314+
```yml
315+
- uses: jfrog/setup-jfrog-cli@v4
316+
with:
317+
custom-server-id: my-server
318+
```
319+
320+
You may also use multiple configurations in the same workflow by providing a custom server ID for each configuration.
321+
322+
Alternating between configurations can be done by providing the `--server-id` option to JFrog CLI commands or by setting a default server using `jf c use <server-id>`.
323+
</details>
324+
304325
## JFrog Job Summary
305326

306327
Workflows using this GitHub action will output a summary of some of the key commands that were performed using JFrog CLI.

action.yml

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ inputs:
2323
description: "By default, if the workflow completes with collected build-info that has not been published using the jf rt build-publish command, the build-info will be automatically published to Artifactory. Set this to true to disable the automatic publication of build-info at the end of the workflow."
2424
default: "false"
2525
required: false
26+
custom-server-id:
27+
description: "Custom JFrog CLI configuration server ID to use instead of the default one generated by the action."
28+
required: false
2629
outputs:
2730
oidc-token:
2831
description: "JFrog OIDC token generated by the Setup JFrog CLI when setting oidc-provider-name."

lib/cleanup.js

+16-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ const core = __importStar(require("@actions/core"));
3636
const utils_1 = require("./utils");
3737
function cleanup() {
3838
return __awaiter(this, void 0, void 0, function* () {
39-
if (!utils_1.Utils.loadFromCache(core.getInput(utils_1.Utils.CLI_VERSION_ARG))) {
40-
core.warning('Could not find JFrog CLI executable. Skipping cleanup.');
39+
if (yield shouldSkipCleanup()) {
4140
return;
4241
}
4342
// Run post tasks related to Build Info (auto build publish, job summary)
@@ -198,4 +197,19 @@ function generateJobSummary() {
198197
}
199198
});
200199
}
200+
function shouldSkipCleanup() {
201+
return __awaiter(this, void 0, void 0, function* () {
202+
if (!utils_1.Utils.loadFromCache(core.getInput(utils_1.Utils.CLI_VERSION_ARG))) {
203+
core.warning('Could not find JFrog CLI executable. Skipping cleanup.');
204+
return true;
205+
}
206+
// Skip cleanup if no servers are configured (already removed)
207+
const servers = process.env[utils_1.Utils.JFROG_CLI_SERVER_IDS_ENV_VAR];
208+
if (!servers) {
209+
core.debug('No servers are configured. Skipping cleanup.');
210+
return true;
211+
}
212+
return false;
213+
});
214+
}
201215
cleanup();

lib/utils.js

+55-4
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ class Utils {
315315
let password = jfrogCredentials.password;
316316
let accessToken = jfrogCredentials.accessToken;
317317
if (url) {
318-
let configCmd = [Utils.SETUP_JFROG_CLI_SERVER_ID, '--url', url, '--interactive=false', '--overwrite=true'];
318+
let configCmd = [Utils.getServerIdForConfig(), '--url', url, '--interactive=false', '--overwrite=true'];
319319
if (accessToken) {
320320
configCmd.push('--access-token', accessToken);
321321
}
@@ -325,6 +325,36 @@ class Utils {
325325
return configCmd;
326326
}
327327
}
328+
/**
329+
* Get server ID for JFrog CLI configuration. Save the server ID in the servers env var if it doesn't already exist.
330+
*/
331+
static getServerIdForConfig() {
332+
let serverId = Utils.getCustomOrDefaultServerId();
333+
// Add new serverId to the servers env var if it doesn't already exist.
334+
if (Utils.getConfiguredJFrogServers().includes(serverId)) {
335+
return serverId;
336+
}
337+
const currentValue = process.env[Utils.JFROG_CLI_SERVER_IDS_ENV_VAR];
338+
const newVal = currentValue ? `${currentValue};${serverId}` : serverId;
339+
core.exportVariable(Utils.JFROG_CLI_SERVER_IDS_ENV_VAR, newVal);
340+
return serverId;
341+
}
342+
/**
343+
* Return custom server ID if provided, or default server ID otherwise.
344+
*/
345+
static getCustomOrDefaultServerId() {
346+
let customServerId = core.getInput(Utils.CUSTOM_SERVER_ID);
347+
if (customServerId) {
348+
return customServerId;
349+
}
350+
return Utils.getRunDefaultServerId();
351+
}
352+
/**
353+
* Return a server ID that is unique for this workflow run based on the GitHub repository and run ID.
354+
*/
355+
static getRunDefaultServerId() {
356+
return [Utils.SETUP_JFROG_CLI_SERVER_ID_PREFIX, process.env.GITHUB_REPOSITORY, process.env.GITHUB_RUN_ID].join('-');
357+
}
328358
static setCliEnv() {
329359
Utils.exportVariableIfNotSet('JFROG_CLI_ENV_EXCLUDE', '*password*;*secret*;*key*;*token*;*auth*;JF_ARTIFACTORY_*;JF_ENV_*;JF_URL;JF_USER;JF_PASSWORD;JF_ACCESS_TOKEN');
330360
Utils.exportVariableIfNotSet('JFROG_CLI_OFFER_CONFIG', 'false');
@@ -376,11 +406,28 @@ class Utils {
376406
}
377407
});
378408
}
409+
/**
410+
* Removed configured JFrog CLI servers that are saved in the servers env var, and unset the env var.
411+
*/
379412
static removeJFrogServers() {
380413
return __awaiter(this, void 0, void 0, function* () {
381-
yield Utils.runCli(['c', 'rm', '--quiet']);
414+
for (const serverId of Utils.getConfiguredJFrogServers()) {
415+
core.debug(`Removing server ID: '${serverId}'...`);
416+
yield Utils.runCli(['c', 'rm', serverId, '--quiet']);
417+
}
418+
core.exportVariable(Utils.JFROG_CLI_SERVER_IDS_ENV_VAR, '');
382419
});
383420
}
421+
/**
422+
* Split and return the configured JFrog CLI servers that are saved in the servers env var.
423+
*/
424+
static getConfiguredJFrogServers() {
425+
const serversValue = process.env[Utils.JFROG_CLI_SERVER_IDS_ENV_VAR];
426+
if (!serversValue) {
427+
return [];
428+
}
429+
return serversValue.split(';');
430+
}
384431
static getArchitecture() {
385432
if (Utils.isWindows()) {
386433
return 'windows-amd64';
@@ -726,8 +773,10 @@ Utils.LATEST_CLI_VERSION = 'latest';
726773
Utils.LATEST_RELEASE_VERSION = '[RELEASE]';
727774
// Placeholder CLI version to use to keep 'latest' in cache.
728775
Utils.LATEST_SEMVER = '100.100.100';
729-
// The default server id name for separate env config
730-
Utils.SETUP_JFROG_CLI_SERVER_ID = 'setup-jfrog-cli-server';
776+
// The prefix for the default server id name for JFrog CLI config
777+
Utils.SETUP_JFROG_CLI_SERVER_ID_PREFIX = 'setup-jfrog-cli-server';
778+
// Environment variable to hold all configured server IDs, separated by ';'
779+
Utils.JFROG_CLI_SERVER_IDS_ENV_VAR = 'SETUP_JFROG_CLI_SERVER_IDS';
731780
// Directory name which holds markdown files for the Workflow summary
732781
Utils.JOB_SUMMARY_DIR_NAME = 'jfrog-command-summary';
733782
// Directory name which holds security command summary files
@@ -753,6 +802,8 @@ Utils.OIDC_INTEGRATION_PROVIDER_NAME = 'oidc-provider-name';
753802
Utils.JOB_SUMMARY_DISABLE = 'disable-job-summary';
754803
// Disable auto build info publish feature flag
755804
Utils.AUTO_BUILD_PUBLISH_DISABLE = 'disable-auto-build-publish';
805+
// Custom server ID input
806+
Utils.CUSTOM_SERVER_ID = 'custom-server-id';
756807
// URL for the markdown header image
757808
// This is hosted statically because its usage is outside the context of the JFrog setup action.
758809
// It cannot be linked to the repository, as GitHub serves the image from a CDN,

src/cleanup.ts

+17-2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import * as core from '@actions/core';
22
import { Utils } from './utils';
33

44
async function cleanup() {
5-
if (!Utils.loadFromCache(core.getInput(Utils.CLI_VERSION_ARG))) {
6-
core.warning('Could not find JFrog CLI executable. Skipping cleanup.');
5+
if (await shouldSkipCleanup()) {
76
return;
87
}
8+
99
// Run post tasks related to Build Info (auto build publish, job summary)
1010
await buildInfoPostTasks();
1111

@@ -158,4 +158,19 @@ async function generateJobSummary() {
158158
}
159159
}
160160

161+
async function shouldSkipCleanup(): Promise<boolean> {
162+
if (!Utils.loadFromCache(core.getInput(Utils.CLI_VERSION_ARG))) {
163+
core.warning('Could not find JFrog CLI executable. Skipping cleanup.');
164+
return true;
165+
}
166+
167+
// Skip cleanup if no servers are configured (already removed)
168+
const servers: string | undefined = process.env[Utils.JFROG_CLI_SERVER_IDS_ENV_VAR];
169+
if (!servers) {
170+
core.debug('No servers are configured. Skipping cleanup.');
171+
return true;
172+
}
173+
return false;
174+
}
175+
161176
cleanup();

src/utils.ts

+60-4
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ export class Utils {
3333
private static readonly LATEST_RELEASE_VERSION: string = '[RELEASE]';
3434
// Placeholder CLI version to use to keep 'latest' in cache.
3535
public static readonly LATEST_SEMVER: string = '100.100.100';
36-
// The default server id name for separate env config
37-
public static readonly SETUP_JFROG_CLI_SERVER_ID: string = 'setup-jfrog-cli-server';
36+
// The prefix for the default server id name for JFrog CLI config
37+
public static readonly SETUP_JFROG_CLI_SERVER_ID_PREFIX: string = 'setup-jfrog-cli-server';
38+
// Environment variable to hold all configured server IDs, separated by ';'
39+
public static readonly JFROG_CLI_SERVER_IDS_ENV_VAR: string = 'SETUP_JFROG_CLI_SERVER_IDS';
3840
// Directory name which holds markdown files for the Workflow summary
3941
private static readonly JOB_SUMMARY_DIR_NAME: string = 'jfrog-command-summary';
4042
// Directory name which holds security command summary files
@@ -61,6 +63,8 @@ export class Utils {
6163
public static readonly JOB_SUMMARY_DISABLE: string = 'disable-job-summary';
6264
// Disable auto build info publish feature flag
6365
public static readonly AUTO_BUILD_PUBLISH_DISABLE: string = 'disable-auto-build-publish';
66+
// Custom server ID input
67+
private static readonly CUSTOM_SERVER_ID: string = 'custom-server-id';
6468
// URL for the markdown header image
6569
// This is hosted statically because its usage is outside the context of the JFrog setup action.
6670
// It cannot be linked to the repository, as GitHub serves the image from a CDN,
@@ -356,7 +360,7 @@ export class Utils {
356360
let accessToken: string | undefined = jfrogCredentials.accessToken;
357361

358362
if (url) {
359-
let configCmd: string[] = [Utils.SETUP_JFROG_CLI_SERVER_ID, '--url', url, '--interactive=false', '--overwrite=true'];
363+
let configCmd: string[] = [Utils.getServerIdForConfig(), '--url', url, '--interactive=false', '--overwrite=true'];
360364
if (accessToken) {
361365
configCmd.push('--access-token', accessToken);
362366
} else if (user && password) {
@@ -366,6 +370,40 @@ export class Utils {
366370
}
367371
}
368372

373+
/**
374+
* Get server ID for JFrog CLI configuration. Save the server ID in the servers env var if it doesn't already exist.
375+
*/
376+
private static getServerIdForConfig(): string {
377+
let serverId: string = Utils.getCustomOrDefaultServerId();
378+
379+
// Add new serverId to the servers env var if it doesn't already exist.
380+
if (Utils.getConfiguredJFrogServers().includes(serverId)) {
381+
return serverId;
382+
}
383+
const currentValue: string | undefined = process.env[Utils.JFROG_CLI_SERVER_IDS_ENV_VAR];
384+
const newVal: string = currentValue ? `${currentValue};${serverId}` : serverId;
385+
core.exportVariable(Utils.JFROG_CLI_SERVER_IDS_ENV_VAR, newVal);
386+
return serverId;
387+
}
388+
389+
/**
390+
* Return custom server ID if provided, or default server ID otherwise.
391+
*/
392+
private static getCustomOrDefaultServerId(): string {
393+
let customServerId: string = core.getInput(Utils.CUSTOM_SERVER_ID);
394+
if (customServerId) {
395+
return customServerId;
396+
}
397+
return Utils.getRunDefaultServerId();
398+
}
399+
400+
/**
401+
* Return a server ID that is unique for this workflow run based on the GitHub repository and run ID.
402+
*/
403+
static getRunDefaultServerId(): string {
404+
return [Utils.SETUP_JFROG_CLI_SERVER_ID_PREFIX, process.env.GITHUB_REPOSITORY, process.env.GITHUB_RUN_ID].join('-');
405+
}
406+
369407
public static setCliEnv() {
370408
Utils.exportVariableIfNotSet(
371409
'JFROG_CLI_ENV_EXCLUDE',
@@ -428,8 +466,26 @@ export class Utils {
428466
}
429467
}
430468

469+
/**
470+
* Removed configured JFrog CLI servers that are saved in the servers env var, and unset the env var.
471+
*/
431472
public static async removeJFrogServers() {
432-
await Utils.runCli(['c', 'rm', '--quiet']);
473+
for (const serverId of Utils.getConfiguredJFrogServers()) {
474+
core.debug(`Removing server ID: '${serverId}'...`);
475+
await Utils.runCli(['c', 'rm', serverId, '--quiet']);
476+
}
477+
core.exportVariable(Utils.JFROG_CLI_SERVER_IDS_ENV_VAR, '');
478+
}
479+
480+
/**
481+
* Split and return the configured JFrog CLI servers that are saved in the servers env var.
482+
*/
483+
public static getConfiguredJFrogServers(): string[] {
484+
const serversValue: string | undefined = process.env[Utils.JFROG_CLI_SERVER_IDS_ENV_VAR];
485+
if (!serversValue) {
486+
return [];
487+
}
488+
return serversValue.split(';');
433489
}
434490

435491
public static getArchitecture() {

test/main.spec.ts

+40-4
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ describe('Collect JFrog Credentials from env vars exceptions', () => {
111111
});
112112
});
113113

114-
test('Get separate env config', async () => {
114+
function testConfigCommand(expectedServerId: string) {
115115
// No url
116116
let configCommand: string[] | undefined = Utils.getSeparateEnvConfigArgs({} as JfrogCredentials);
117117
expect(configCommand).toBe(undefined);
@@ -121,14 +121,14 @@ test('Get separate env config', async () => {
121121

122122
// No credentials
123123
configCommand = Utils.getSeparateEnvConfigArgs(jfrogCredentials);
124-
expect(configCommand).toStrictEqual([Utils.SETUP_JFROG_CLI_SERVER_ID, '--url', DEFAULT_CLI_URL, '--interactive=false', '--overwrite=true']);
124+
expect(configCommand).toStrictEqual([expectedServerId, '--url', DEFAULT_CLI_URL, '--interactive=false', '--overwrite=true']);
125125

126126
// Basic authentication
127127
jfrogCredentials.username = 'user';
128128
jfrogCredentials.password = 'password';
129129
configCommand = Utils.getSeparateEnvConfigArgs(jfrogCredentials);
130130
expect(configCommand).toStrictEqual([
131-
Utils.SETUP_JFROG_CLI_SERVER_ID,
131+
expectedServerId,
132132
'--url',
133133
DEFAULT_CLI_URL,
134134
'--interactive=false',
@@ -145,14 +145,50 @@ test('Get separate env config', async () => {
145145
jfrogCredentials.accessToken = 'accessToken';
146146
configCommand = Utils.getSeparateEnvConfigArgs(jfrogCredentials);
147147
expect(configCommand).toStrictEqual([
148-
Utils.SETUP_JFROG_CLI_SERVER_ID,
148+
expectedServerId,
149149
'--url',
150150
DEFAULT_CLI_URL,
151151
'--interactive=false',
152152
'--overwrite=true',
153153
'--access-token',
154154
'accessToken',
155155
]);
156+
}
157+
158+
describe('JFrog CLI Configuration', () => {
159+
beforeAll(() => {
160+
process.env.GITHUB_REPOSITORY = 'owner/repo';
161+
process.env.GITHUB_RUN_ID = '1';
162+
});
163+
164+
afterAll(() => {
165+
['GITHUB_REPOSITORY', 'GITHUB_RUN_ID', Utils.JFROG_CLI_SERVER_IDS_ENV_VAR].forEach((envKey) => {
166+
delete process.env[envKey];
167+
});
168+
});
169+
const myCore: jest.Mocked<typeof core> = core as any;
170+
171+
test('Get separate env config', async () => {
172+
myCore.exportVariable = jest.fn().mockImplementation((name: string, val: string) => {
173+
process.env[name] = val;
174+
});
175+
176+
// Before setting a custom server ID, expect the default server ID to be used.
177+
testConfigCommand(Utils.getRunDefaultServerId());
178+
179+
// Expect the custom server ID to be used.
180+
let customServerId: string = 'custom-server-id';
181+
jest.spyOn(core, 'getInput').mockReturnValue(customServerId);
182+
testConfigCommand(customServerId);
183+
184+
// Expect the servers env var to include both servers.
185+
const servers: string[] = Utils.getConfiguredJFrogServers();
186+
expect(servers).toStrictEqual([Utils.getRunDefaultServerId(), customServerId]);
187+
});
188+
189+
test('Get default server ID', async () => {
190+
expect(Utils.getRunDefaultServerId()).toStrictEqual('setup-jfrog-cli-server-owner/repo-1');
191+
});
156192
});
157193

158194
describe('JFrog CLI V1 URL Tests', () => {

0 commit comments

Comments
 (0)