Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server configuration improvements #227

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
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
21 changes: 21 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,26 @@ 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 may 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` option 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