Skip to content

Commit

Permalink
Server configuration improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
RobiNino committed Nov 19, 2024
1 parent 18e785f commit 17a58d3
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 16 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
- [Setting JFrog CLI Version](#setting-jfrog-cli-version)
- [Setting the JFrog Project Key](#setting-the-jfrog-project-key)
- [Downloading JFrog CLI from Artifactory](#downloading-jfrog-cli-from-artifactory)
- [Custom Server ID and Multi-Configuration](#custom-server-id-and-multi-configuration)
- [JFrog Job Summary](#jfrog-job-summary)
- [Code Scanning Alerts](#code-scanning-alerts)
- [Example Projects](#example-projects)
Expand Down Expand Up @@ -301,6 +302,25 @@ Here's how you do this:
```
</details>

<details>
<summary>Custom Server ID and Multi-Configuration</summary>

### Custom Server ID and Multi-Configuration

The action configures JFrog CLI with a default server ID, which is unique for each run of a workflow.

You may override the default server ID by providing a custom server ID:

```yml
- uses: jfrog/setup-jfrog-cli@v4
with:
custom-server-id: my-server
```

You can also use multiple configurations in the same workflow by providing a custom server ID for each configuration.
Alternating between configurations can be done by providing the `--server-id` options to JFrog CLI commands or by setting a default server using `jf c use <server-id>`.
</details>

## JFrog Job Summary

Workflows using this GitHub action will output a summary of some of the key commands that were performed using JFrog CLI.
Expand Down
3 changes: 3 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ inputs:
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."
default: "false"
required: false
custom-server-id:
description: "Custom JFrog CLI configuration server ID to use instead of the default one generated by the action."
required: false
outputs:
oidc-token:
description: "JFrog OIDC token generated by the Setup JFrog CLI when setting oidc-provider-name."
Expand Down
18 changes: 16 additions & 2 deletions lib/cleanup.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ const core = __importStar(require("@actions/core"));
const utils_1 = require("./utils");
function cleanup() {
return __awaiter(this, void 0, void 0, function* () {
if (!utils_1.Utils.loadFromCache(core.getInput(utils_1.Utils.CLI_VERSION_ARG))) {
core.warning('Could not find JFrog CLI executable. Skipping cleanup.');
if (yield shouldSkipCleanup()) {
return;
}
// Run post tasks related to Build Info (auto build publish, job summary)
Expand Down Expand Up @@ -198,4 +197,19 @@ function generateJobSummary() {
}
});
}
function shouldSkipCleanup() {
return __awaiter(this, void 0, void 0, function* () {
if (!utils_1.Utils.loadFromCache(core.getInput(utils_1.Utils.CLI_VERSION_ARG))) {
core.warning('Could not find JFrog CLI executable. Skipping cleanup.');
return true;
}
// Skip cleanup if no servers are configured (already removed)
const servers = process.env[utils_1.Utils.JFROG_CLI_SERVER_IDS_ENV_VAR];
if (!servers) {
core.debug('No servers are configured. Skipping cleanup.');
return true;
}
return false;
});
}
cleanup();
59 changes: 55 additions & 4 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ class Utils {
let password = jfrogCredentials.password;
let accessToken = jfrogCredentials.accessToken;
if (url) {
let configCmd = [Utils.SETUP_JFROG_CLI_SERVER_ID, '--url', url, '--interactive=false', '--overwrite=true'];
let configCmd = [Utils.getServerIdForConfig(), '--url', url, '--interactive=false', '--overwrite=true'];
if (accessToken) {
configCmd.push('--access-token', accessToken);
}
Expand All @@ -321,6 +321,36 @@ class Utils {
return configCmd;
}
}
/**
* Get server ID for JFrog CLI configuration. Save the server ID in the servers env var if it doesn't already exist.
*/
static getServerIdForConfig() {
let serverId = Utils.getCustomOrDefaultServerId();
// Add new serverId to the servers env var if it doesn't already exist.
if (Utils.getConfiguredJFrogServers().includes(serverId)) {
return serverId;
}
const currentValue = process.env[Utils.JFROG_CLI_SERVER_IDS_ENV_VAR];
const newVal = currentValue ? `${currentValue};${serverId}` : serverId;
core.exportVariable(Utils.JFROG_CLI_SERVER_IDS_ENV_VAR, newVal);
return serverId;
}
/**
* Return custom server ID if provided, or default server ID otherwise.
*/
static getCustomOrDefaultServerId() {
let customServerId = core.getInput(Utils.CUSTOM_SERVER_ID);
if (customServerId) {
return customServerId;
}
return Utils.getRunDefaultServerId();
}
/**
* Return a server ID that is unique for this workflow run based on the GitHub repository and run ID.
*/
static getRunDefaultServerId() {
return [Utils.SETUP_JFROG_CLI_SERVER_ID_PREFIX, process.env.GITHUB_REPOSITORY, process.env.GITHUB_RUN_ID].join('-');
}
static setCliEnv() {
Utils.exportVariableIfNotSet('JFROG_CLI_ENV_EXCLUDE', '*password*;*secret*;*key*;*token*;*auth*;JF_ARTIFACTORY_*;JF_ENV_*;JF_URL;JF_USER;JF_PASSWORD;JF_ACCESS_TOKEN');
Utils.exportVariableIfNotSet('JFROG_CLI_OFFER_CONFIG', 'false');
Expand Down Expand Up @@ -372,11 +402,28 @@ class Utils {
}
});
}
/**
* Removed configured JFrog CLI servers that are saved in the servers env var, and unset the env var.
*/
static removeJFrogServers() {
return __awaiter(this, void 0, void 0, function* () {
yield Utils.runCli(['c', 'rm', '--quiet']);
for (const serverId of Utils.getConfiguredJFrogServers()) {
core.debug(`Removing server ID: '${serverId}'...`);
yield Utils.runCli(['c', 'rm', serverId, '--quiet']);
}
core.exportVariable(Utils.JFROG_CLI_SERVER_IDS_ENV_VAR, '');
});
}
/**
* Split and return the configured JFrog CLI servers that are saved in the servers env var.
*/
static getConfiguredJFrogServers() {
const serversValue = process.env[Utils.JFROG_CLI_SERVER_IDS_ENV_VAR];
if (!serversValue) {
return [];
}
return serversValue.split(';');
}
static getArchitecture() {
if (Utils.isWindows()) {
return 'windows-amd64';
Expand Down Expand Up @@ -722,8 +769,10 @@ Utils.LATEST_CLI_VERSION = 'latest';
Utils.LATEST_RELEASE_VERSION = '[RELEASE]';
// Placeholder CLI version to use to keep 'latest' in cache.
Utils.LATEST_SEMVER = '100.100.100';
// The default server id name for separate env config
Utils.SETUP_JFROG_CLI_SERVER_ID = 'setup-jfrog-cli-server';
// The prefix for the default server id name for JFrog CLI config
Utils.SETUP_JFROG_CLI_SERVER_ID_PREFIX = 'setup-jfrog-cli-server';
// Environment variable to hold all configured server IDs, separated by ';'
Utils.JFROG_CLI_SERVER_IDS_ENV_VAR = 'SETUP_JFROG_CLI_SERVER_IDS';
// Directory name which holds markdown files for the Workflow summary
Utils.JOB_SUMMARY_DIR_NAME = 'jfrog-command-summary';
// Directory name which holds security command summary files
Expand All @@ -749,6 +798,8 @@ Utils.OIDC_INTEGRATION_PROVIDER_NAME = 'oidc-provider-name';
Utils.JOB_SUMMARY_DISABLE = 'disable-job-summary';
// Disable auto build info publish feature flag
Utils.AUTO_BUILD_PUBLISH_DISABLE = 'disable-auto-build-publish';
// Custom server ID input
Utils.CUSTOM_SERVER_ID = 'custom-server-id';
// URL for the markdown header image
// This is hosted statically because its usage is outside the context of the JFrog setup action.
// It cannot be linked to the repository, as GitHub serves the image from a CDN,
Expand Down
19 changes: 17 additions & 2 deletions src/cleanup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import * as core from '@actions/core';
import { Utils } from './utils';

async function cleanup() {
if (!Utils.loadFromCache(core.getInput(Utils.CLI_VERSION_ARG))) {
core.warning('Could not find JFrog CLI executable. Skipping cleanup.');
if (await shouldSkipCleanup()) {
return;
}

// Run post tasks related to Build Info (auto build publish, job summary)
await buildInfoPostTasks();

Expand Down Expand Up @@ -158,4 +158,19 @@ async function generateJobSummary() {
}
}

async function shouldSkipCleanup(): Promise<boolean> {
if (!Utils.loadFromCache(core.getInput(Utils.CLI_VERSION_ARG))) {
core.warning('Could not find JFrog CLI executable. Skipping cleanup.');
return true;
}

// Skip cleanup if no servers are configured (already removed)
const servers: string | undefined = process.env[Utils.JFROG_CLI_SERVER_IDS_ENV_VAR];
if (!servers) {
core.debug('No servers are configured. Skipping cleanup.');
return true;
}
return false;
}

cleanup();
64 changes: 60 additions & 4 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ export class Utils {
private static readonly LATEST_RELEASE_VERSION: string = '[RELEASE]';
// Placeholder CLI version to use to keep 'latest' in cache.
public static readonly LATEST_SEMVER: string = '100.100.100';
// The default server id name for separate env config
public static readonly SETUP_JFROG_CLI_SERVER_ID: string = 'setup-jfrog-cli-server';
// The prefix for the default server id name for JFrog CLI config
public static readonly SETUP_JFROG_CLI_SERVER_ID_PREFIX: string = 'setup-jfrog-cli-server';
// Environment variable to hold all configured server IDs, separated by ';'
public static readonly JFROG_CLI_SERVER_IDS_ENV_VAR: string = 'SETUP_JFROG_CLI_SERVER_IDS';
// Directory name which holds markdown files for the Workflow summary
private static readonly JOB_SUMMARY_DIR_NAME: string = 'jfrog-command-summary';
// Directory name which holds security command summary files
Expand All @@ -61,6 +63,8 @@ export class Utils {
public static readonly JOB_SUMMARY_DISABLE: string = 'disable-job-summary';
// Disable auto build info publish feature flag
public static readonly AUTO_BUILD_PUBLISH_DISABLE: string = 'disable-auto-build-publish';
// Custom server ID input
private static readonly CUSTOM_SERVER_ID: string = 'custom-server-id';
// URL for the markdown header image
// This is hosted statically because its usage is outside the context of the JFrog setup action.
// It cannot be linked to the repository, as GitHub serves the image from a CDN,
Expand Down Expand Up @@ -352,7 +356,7 @@ export class Utils {
let accessToken: string | undefined = jfrogCredentials.accessToken;

if (url) {
let configCmd: string[] = [Utils.SETUP_JFROG_CLI_SERVER_ID, '--url', url, '--interactive=false', '--overwrite=true'];
let configCmd: string[] = [Utils.getServerIdForConfig(), '--url', url, '--interactive=false', '--overwrite=true'];
if (accessToken) {
configCmd.push('--access-token', accessToken);
} else if (user && password) {
Expand All @@ -362,6 +366,40 @@ export class Utils {
}
}

/**
* Get server ID for JFrog CLI configuration. Save the server ID in the servers env var if it doesn't already exist.
*/
private static getServerIdForConfig(): string {
let serverId: string = Utils.getCustomOrDefaultServerId();

// Add new serverId to the servers env var if it doesn't already exist.
if (Utils.getConfiguredJFrogServers().includes(serverId)) {
return serverId;
}
const currentValue: string | undefined = process.env[Utils.JFROG_CLI_SERVER_IDS_ENV_VAR];
const newVal: string = currentValue ? `${currentValue};${serverId}` : serverId;
core.exportVariable(Utils.JFROG_CLI_SERVER_IDS_ENV_VAR, newVal);
return serverId;
}

/**
* Return custom server ID if provided, or default server ID otherwise.
*/
private static getCustomOrDefaultServerId(): string {
let customServerId: string = core.getInput(Utils.CUSTOM_SERVER_ID);
if (customServerId) {
return customServerId;
}
return Utils.getRunDefaultServerId();
}

/**
* Return a server ID that is unique for this workflow run based on the GitHub repository and run ID.
*/
static getRunDefaultServerId(): string {
return [Utils.SETUP_JFROG_CLI_SERVER_ID_PREFIX, process.env.GITHUB_REPOSITORY, process.env.GITHUB_RUN_ID].join('-');
}

public static setCliEnv() {
Utils.exportVariableIfNotSet(
'JFROG_CLI_ENV_EXCLUDE',
Expand Down Expand Up @@ -424,8 +462,26 @@ export class Utils {
}
}

/**
* Removed configured JFrog CLI servers that are saved in the servers env var, and unset the env var.
*/
public static async removeJFrogServers() {
await Utils.runCli(['c', 'rm', '--quiet']);
for (const serverId of Utils.getConfiguredJFrogServers()) {
core.debug(`Removing server ID: '${serverId}'...`);
await Utils.runCli(['c', 'rm', serverId, '--quiet']);
}
core.exportVariable(Utils.JFROG_CLI_SERVER_IDS_ENV_VAR, '');
}

/**
* Split and return the configured JFrog CLI servers that are saved in the servers env var.
*/
public static getConfiguredJFrogServers(): string[] {
const serversValue: string | undefined = process.env[Utils.JFROG_CLI_SERVER_IDS_ENV_VAR];
if (!serversValue) {
return [];
}
return serversValue.split(';');
}

public static getArchitecture() {
Expand Down
44 changes: 40 additions & 4 deletions test/main.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ describe('Collect JFrog Credentials from env vars exceptions', () => {
});
});

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

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

// Basic authentication
jfrogCredentials.username = 'user';
jfrogCredentials.password = 'password';
configCommand = Utils.getSeparateEnvConfigArgs(jfrogCredentials);
expect(configCommand).toStrictEqual([
Utils.SETUP_JFROG_CLI_SERVER_ID,
expectedServerId,
'--url',
DEFAULT_CLI_URL,
'--interactive=false',
Expand All @@ -145,14 +145,50 @@ test('Get separate env config', async () => {
jfrogCredentials.accessToken = 'accessToken';
configCommand = Utils.getSeparateEnvConfigArgs(jfrogCredentials);
expect(configCommand).toStrictEqual([
Utils.SETUP_JFROG_CLI_SERVER_ID,
expectedServerId,
'--url',
DEFAULT_CLI_URL,
'--interactive=false',
'--overwrite=true',
'--access-token',
'accessToken',
]);
}

describe('JFrog CLI Configuration', () => {
beforeAll(() => {
process.env.GITHUB_REPOSITORY = 'owner/repo';
process.env.GITHUB_RUN_ID = '1';
});

afterAll(() => {
['GITHUB_REPOSITORY', 'GITHUB_RUN_ID', Utils.JFROG_CLI_SERVER_IDS_ENV_VAR].forEach((envKey) => {
delete process.env[envKey];
});
});
const myCore: jest.Mocked<typeof core> = core as any;

test('Get separate env config', async () => {
myCore.exportVariable = jest.fn().mockImplementation((name: string, val: string) => {
process.env[name] = val;
});

// Before setting a custom server ID, expect the default server ID to be used.
testConfigCommand(Utils.getRunDefaultServerId());

// Expect the custom server ID to be used.
let customServerId: string = 'custom-server-id';
jest.spyOn(core, 'getInput').mockReturnValue(customServerId);
testConfigCommand(customServerId);

// Expect the servers env var to include both servers.
const servers: string[] = Utils.getConfiguredJFrogServers();
expect(servers).toStrictEqual([Utils.getRunDefaultServerId(), customServerId]);
});

test('Get default server ID', async () => {
expect(Utils.getRunDefaultServerId()).toStrictEqual('setup-jfrog-cli-server-owner/repo-1');
});
});

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

0 comments on commit 17a58d3

Please sign in to comment.