Skip to content

Commit

Permalink
Fix 'jf' path on cleanup and validate JobSummary supported CLI version (
Browse files Browse the repository at this point in the history
  • Loading branch information
sverdlov93 authored Sep 4, 2024
1 parent b5613d0 commit c10541d
Show file tree
Hide file tree
Showing 9 changed files with 304 additions and 159 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/auto-build-publish-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ concurrency:

jobs:
Auto-Build-Publish-Test:
name: Auto-Build-Publish-Test (${{ matrix.os }}) - (CLI ${{ matrix.cli-version }})
if: contains(github.event.pull_request.labels.*.name, 'safe to test') || github.event_name == 'push'
strategy:
fail-fast: false
matrix:
os: [ ubuntu, windows, macos ]
cli-version: [ "latest", "2.66.0" ]
runs-on: ${{ matrix.os }}-latest
steps:
- name: Checkout Repository
Expand Down Expand Up @@ -51,6 +53,8 @@ jobs:
- name: Setup JFrog CLI
id: setup-jfrog-cli
uses: ./
with:
version: ${{ matrix.cli-version }}
env:
JF_URL: http://localhost:8081/
JF_USER: admin
Expand Down
129 changes: 90 additions & 39 deletions lib/cleanup.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,31 +36,12 @@ const core = __importStar(require("@actions/core"));
const utils_1 = require("./utils");
function cleanup() {
return __awaiter(this, void 0, void 0, function* () {
if (!addCachedJfToPath()) {
core.error('Could not find JFrog CLI path in the step state. Skipping cleanup.');
if (!utils_1.Utils.loadFromCache(core.getInput(utils_1.Utils.CLI_VERSION_ARG))) {
core.warning('Could not find JFrog CLI executable. Skipping cleanup.');
return;
}
// Auto-publish build info if needed
try {
if (!core.getBooleanInput(utils_1.Utils.AUTO_BUILD_PUBLISH_DISABLE)) {
yield collectAndPublishBuildInfoIfNeeded();
}
}
catch (error) {
core.warning('failed while attempting to publish build info: ' + error);
}
// Generate job summary
try {
if (!core.getBooleanInput(utils_1.Utils.JOB_SUMMARY_DISABLE)) {
core.startGroup('Generating Job Summary');
yield utils_1.Utils.runCli(['generate-summary-markdown']);
yield utils_1.Utils.setMarkdownAsJobSummary();
core.endGroup();
}
}
catch (error) {
core.warning('failed while attempting to generate job summary: ' + error);
}
// Run post tasks related to Build Info (auto build publish, job summary)
yield buildInfoPostTasks();
// Cleanup JFrog CLI servers configuration
try {
core.startGroup('Cleanup JFrog CLI servers configuration');
Expand All @@ -74,36 +55,65 @@ function cleanup() {
}
});
}
function addCachedJfToPath() {
// Get the JFrog CLI path from step state. saveState/getState are methods to pass data between a step, and it's cleanup function.
const jfCliPath = core.getState(utils_1.Utils.JF_CLI_PATH_STATE);
if (!jfCliPath) {
// This means that the JFrog CLI was not installed in the first place, because there was a failure in the installation step.
return false;
}
core.addPath(jfCliPath);
return true;
/**
* Executes post tasks related to build information.
*
* This function performs several tasks after the main build process:
* 1. Checks if auto build publish and job summary are disabled.
* 2. Verifies connection to JFrog Artifactory.
* 3. Collects and publishes build information if needed.
* 4. Generates a job summary if required.
*/
function buildInfoPostTasks() {
return __awaiter(this, void 0, void 0, function* () {
const disableAutoBuildPublish = core.getBooleanInput(utils_1.Utils.AUTO_BUILD_PUBLISH_DISABLE);
const disableJobSummary = core.getBooleanInput(utils_1.Utils.JOB_SUMMARY_DISABLE) || !utils_1.Utils.isJobSummarySupported();
if (disableAutoBuildPublish && disableJobSummary) {
core.info(`Both auto-build-publish and job-summary are disabled. Skipping Build Info post tasks.`);
return;
}
// Check connection to Artifactory before proceeding with build info post tasks
if (!(yield checkConnectionToArtifactory())) {
return;
}
// Auto-publish build info if needed
if (!disableAutoBuildPublish) {
yield collectAndPublishBuildInfoIfNeeded();
}
else {
core.info('Auto build info publish is disabled. Skipping auto build info collection and publishing');
}
// Generate job summary if not disabled and the JFrog CLI version supports it
if (!disableJobSummary) {
yield generateJobSummary();
}
else {
core.info('Job summary is disabled. Skipping job summary generation');
}
});
}
function hasUnpublishedModules(workingDirectory) {
return __awaiter(this, void 0, void 0, function* () {
// Save the old value of the environment variable to revert it later
const origValue = process.env[utils_1.Utils.JFROG_CLI_COMMAND_SUMMARY_OUTPUT_DIR_ENV];
try {
core.startGroup('Check for unpublished modules');
// Avoid saving a command summary for this dry-run command
core.exportVariable(utils_1.Utils.JFROG_CLI_COMMAND_SUMMARY_OUTPUT_DIR_ENV, '');
// Running build-publish command with a dry-run flag to check if there are any unpublished modules, 'silent' to avoid polluting the logs
const responseStr = yield utils_1.Utils.runCliAndGetOutput(['rt', 'build-publish', '--dry-run'], { silent: true, cwd: workingDirectory });
const responseStr = yield utils_1.Utils.runCliAndGetOutput(['rt', 'build-publish', '--dry-run'], { cwd: workingDirectory });
// Parse the JSON string to an object
const response = JSON.parse(responseStr);
// Check if the "modules" key exists and if it's an array with more than one item
return response.modules != undefined && Array.isArray(response.modules) && response.modules.length > 0;
}
catch (error) {
core.error('Failed to parse JSON: ' + error);
return false; // Return false if parsing fails
core.warning('Failed to check if there are any unpublished modules: ' + error);
return false;
}
finally {
core.exportVariable(utils_1.Utils.JFROG_CLI_COMMAND_SUMMARY_OUTPUT_DIR_ENV, origValue);
core.endGroup();
}
});
}
Expand All @@ -121,14 +131,19 @@ function collectAndPublishBuildInfoIfNeeded() {
yield utils_1.Utils.runCli(['rt', 'build-add-git'], { cwd: workingDirectory });
}
catch (error) {
core.warning('failed while attempting to collect Git information: ' + error);
core.warning('Failed while attempting to collect Git information: ' + error);
}
finally {
core.endGroup();
}
// Publish the build info to Artifactory
try {
core.startGroup('Publish the build info to JFrog Artifactory');
yield utils_1.Utils.runCli(['rt', 'build-publish'], { cwd: workingDirectory });
}
finally {
core.endGroup();
}
core.startGroup('Publish the build info to JFrog Artifactory');
yield utils_1.Utils.runCli(['rt', 'build-publish'], { cwd: workingDirectory });
core.endGroup();
});
}
function getWorkingDirectory() {
Expand All @@ -138,4 +153,40 @@ function getWorkingDirectory() {
}
return workingDirectory;
}
function checkConnectionToArtifactory() {
return __awaiter(this, void 0, void 0, function* () {
try {
core.startGroup('Checking connection to JFrog Artifactory');
const pingResult = yield utils_1.Utils.runCliAndGetOutput(['rt', 'ping']);
if (pingResult !== 'OK') {
core.debug(`Ping result: ${pingResult}`);
core.warning('Could not connect to Artifactory. Skipping Build Info post tasks.');
return false;
}
return true;
}
catch (error) {
core.warning(`An error occurred while trying to connect to Artifactory: ${error}. Skipping Build Info post tasks.`);
return false;
}
finally {
core.endGroup();
}
});
}
function generateJobSummary() {
return __awaiter(this, void 0, void 0, function* () {
try {
core.startGroup('Generating Job Summary');
yield utils_1.Utils.runCli(['generate-summary-markdown']);
yield utils_1.Utils.setMarkdownAsJobSummary();
}
catch (error) {
core.warning('Failed while attempting to generate job summary: ' + error);
}
finally {
core.endGroup();
}
});
}
cleanup();
94 changes: 58 additions & 36 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,48 +200,45 @@ class Utils {
return __awaiter(this, void 0, void 0, function* () {
let version = core.getInput(Utils.CLI_VERSION_ARG);
let cliRemote = core.getInput(Utils.CLI_REMOTE_ARG);
let major = version.split('.')[0];
if (version === Utils.LATEST_CLI_VERSION) {
version = Utils.LATEST_RELEASE_VERSION;
major = '2';
}
else if ((0, semver_1.lt)(version, this.MIN_CLI_VERSION)) {
const isLatestVer = version === Utils.LATEST_CLI_VERSION;
if (!isLatestVer && (0, semver_1.lt)(version, this.MIN_CLI_VERSION)) {
throw new Error('Requested to download JFrog CLI version ' + version + ' but must be at least ' + this.MIN_CLI_VERSION);
}
let jfFileName = Utils.getJfExecutableName();
let jfrogFileName = Utils.getJFrogExecutableName();
if (this.loadFromCache(jfFileName, jfrogFileName, version)) {
// Download is not needed
if (!isLatestVer && this.loadFromCache(version)) {
core.info('Found JFrog CLI in cache. No need to download');
return;
}
// Download JFrog CLI
let downloadDetails = Utils.extractDownloadDetails(cliRemote, jfrogCredentials);
let url = Utils.getCliUrl(major, version, jfrogFileName, downloadDetails);
let url = Utils.getCliUrl(version, Utils.getJFrogExecutableName(), downloadDetails);
core.info('Downloading JFrog CLI from ' + url);
let downloadedExecutable = yield toolCache.downloadTool(url, undefined, downloadDetails.auth);
// Cache 'jf' and 'jfrog' executables
yield this.cacheAndAddPath(downloadedExecutable, version, jfFileName, jfrogFileName);
yield this.cacheAndAddPath(downloadedExecutable, version);
});
}
/**
* Try to load the JFrog CLI executables from cache.
*
* @param jfFileName - 'jf' or 'jf.exe'
* @param jfrogFileName - 'jfrog' or 'jfrog.exe'
* @param version - JFrog CLI version
* @returns true if the CLI executable was loaded from cache and added to path
*/
static loadFromCache(jfFileName, jfrogFileName, version) {
if (version === Utils.LATEST_RELEASE_VERSION) {
return false;
}
let jfExecDir = toolCache.find(jfFileName, version);
let jfrogExecDir = toolCache.find(jfrogFileName, version);
static loadFromCache(version) {
const jfFileName = Utils.getJfExecutableName();
const jfrogFileName = Utils.getJFrogExecutableName();
if (version === Utils.LATEST_CLI_VERSION) {
// If the version is 'latest', we keep it on cache as 100.100.100
version = Utils.LATEST_SEMVER;
}
const jfExecDir = toolCache.find(jfFileName, version);
const jfrogExecDir = toolCache.find(jfrogFileName, version);
if (jfExecDir && jfrogExecDir) {
core.addPath(jfExecDir);
core.addPath(jfrogExecDir);
// Save the JF CLI path to use on cleanup. saveState/getState are methods to pass data between a step, and its cleanup function.
core.saveState(Utils.JF_CLI_PATH_STATE, jfExecDir);
if (!Utils.isWindows()) {
(0, fs_1.chmodSync)((0, path_1.join)(jfExecDir, jfFileName), 0o555);
(0, fs_1.chmodSync)((0, path_1.join)(jfrogExecDir, jfrogFileName), 0o555);
}
return true;
}
return false;
Expand All @@ -250,11 +247,15 @@ class Utils {
* Add JFrog CLI executables to cache and to the system path.
* @param downloadedExecutable - Path to the downloaded JFrog CLI executable
* @param version - JFrog CLI version
* @param jfFileName - 'jf' or 'jf.exe'
* @param jfrogFileName - 'jfrog' or 'jfrog.exe'
*/
static cacheAndAddPath(downloadedExecutable, version, jfFileName, jfrogFileName) {
static cacheAndAddPath(downloadedExecutable, version) {
return __awaiter(this, void 0, void 0, function* () {
if (version === Utils.LATEST_CLI_VERSION) {
// If the version is 'latest', we keep it on cache as 100.100.100 as GitHub actions cache supports only semver versions
version = Utils.LATEST_SEMVER;
}
const jfFileName = Utils.getJfExecutableName();
const jfrogFileName = Utils.getJFrogExecutableName();
let jfCacheDir = yield toolCache.cacheFile(downloadedExecutable, jfFileName, jfFileName, version);
core.addPath(jfCacheDir);
let jfrogCacheDir = yield toolCache.cacheFile(downloadedExecutable, jfrogFileName, jfrogFileName, version);
Expand All @@ -263,13 +264,25 @@ class Utils {
(0, fs_1.chmodSync)((0, path_1.join)(jfCacheDir, jfFileName), 0o555);
(0, fs_1.chmodSync)((0, path_1.join)(jfrogCacheDir, jfrogFileName), 0o555);
}
// Save the JF CLI path to use on cleanup. saveState/getState are methods to pass data between a step, and it's cleanup function.
core.saveState(Utils.JF_CLI_PATH_STATE, jfCacheDir);
});
}
static getCliUrl(major, version, fileName, downloadDetails) {
let architecture = 'jfrog-cli-' + Utils.getArchitecture();
let artifactoryUrl = downloadDetails.artifactoryUrl.replace(/\/$/, '');
/**
* Get the JFrog CLI download URL.
* @param version - Requested version
* @param fileName - Executable file name
* @param downloadDetails - Source Artifactory details
*/
static getCliUrl(version, fileName, downloadDetails) {
const architecture = 'jfrog-cli-' + Utils.getArchitecture();
const artifactoryUrl = downloadDetails.artifactoryUrl.replace(/\/$/, '');
let major;
if (version === Utils.LATEST_CLI_VERSION) {
version = Utils.LATEST_RELEASE_VERSION;
major = '2';
}
else {
major = version.split('.')[0];
}
return `${artifactoryUrl}/${downloadDetails.repository}/v${major}/${version}/${architecture}/${fileName}`;
}
// Get Config Tokens created on your local machine using JFrog CLI.
Expand Down Expand Up @@ -411,11 +424,14 @@ class Utils {
*/
static runCliAndGetOutput(args, options) {
return __awaiter(this, void 0, void 0, function* () {
let output = yield (0, exec_1.getExecOutput)('jf', args, options);
let output;
output = yield (0, exec_1.getExecOutput)('jf', args, Object.assign(Object.assign({}, options), { ignoreReturnCode: true }));
if (output.exitCode !== core.ExitCode.Success) {
core.info(output.stdout);
core.info(output.stderr);
throw new Error('JFrog CLI exited with exit code ' + output.exitCode);
if (options === null || options === void 0 ? void 0 : options.silent) {
core.info(output.stdout);
core.info(output.stderr);
}
throw new Error(`JFrog CLI exited with exit code ${output.exitCode}`);
}
return output.stdout;
});
Expand Down Expand Up @@ -467,6 +483,10 @@ class Utils {
}
return;
}
static isJobSummarySupported() {
const version = core.getInput(Utils.CLI_VERSION_ARG);
return version === Utils.LATEST_CLI_VERSION || (0, semver_1.gt)(version, Utils.MIN_CLI_VERSION_JOB_SUMMARY);
}
/**
* Generates GitHub workflow unified Summary report.
* This function runs as part of post-workflow cleanup function,
Expand Down Expand Up @@ -621,14 +641,16 @@ Utils.MIN_CLI_VERSION = '1.46.4';
Utils.LATEST_CLI_VERSION = 'latest';
// The value in the download URL to set to get the latest version
Utils.LATEST_RELEASE_VERSION = '[RELEASE]';
// State name for saving JF CLI path to use on cleanup
Utils.JF_CLI_PATH_STATE = 'JF_CLI_PATH_STATE';
// 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';
// Directory name which holds markdown files for the Workflow summary
Utils.JOB_SUMMARY_DIR_NAME = 'jfrog-command-summary';
// JFrog CLI command summary output directory environment variable
Utils.JFROG_CLI_COMMAND_SUMMARY_OUTPUT_DIR_ENV = 'JFROG_CLI_COMMAND_SUMMARY_OUTPUT_DIR';
// Minimum JFrog CLI version supported for job summary command
Utils.MIN_CLI_VERSION_JOB_SUMMARY = '2.66.0';
// Inputs
// Version input
Utils.CLI_VERSION_ARG = 'version';
Expand Down
2 changes: 1 addition & 1 deletion node_modules/.package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@jfrog/setup-jfrog-cli",
"version": "4.3.2",
"version": "4.3.3",
"private": true,
"description": "Setup JFrog CLI in GitHub Actions",
"main": "lib/main.js",
Expand Down
Loading

0 comments on commit c10541d

Please sign in to comment.