From 126069f889170b8c58ac6635aade3d51c340023e Mon Sep 17 00:00:00 2001 From: Yahav Itzhak Date: Mon, 21 Mar 2022 11:30:20 +0200 Subject: [PATCH] Allow using 'jf' in addition to 'jfrog' (#63) --- .github/workflows/test.yml | 95 +++++++++++++++------------- README.md | 54 ++++++++-------- lib/cleanup.js | 4 +- lib/main.js | 4 +- lib/utils.js | 106 ++++++++++++++++++++++++-------- scripts/check-env.js | 4 +- src/cleanup.ts | 6 +- src/main.ts | 6 +- src/utils.ts | 123 +++++++++++++++++++++++++++++-------- test/main.spec.ts | 37 ++++++++--- 10 files changed, 299 insertions(+), 140 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9fa3831a4..98a9c98e4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,46 +1,57 @@ -name: 'Test' +name: "Test" on: [push, pull_request] jobs: - test: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macOS-latest] - version: ['', 'latest', '2.11.1'] - fail-fast: false - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Setup JFrog CLI - if: matrix.version != '' - uses: ./ - with: - version: ${{ matrix.version }} - env: - JF_ARTIFACTORY_LOCAL: eyJ2ZXJzaW9uIjoxLCJ1cmwiOiJodHRwOi8vMTI3LjAuMC4xOjgwODEvYXJ0aWZhY3RvcnkvIiwidXNlciI6ImFkbWluIiwicGFzc3dvcmQiOiJBUEI3REVaUlBpSHFIRFRRb2tMa3g5aGh6S1QiLCJzZXJ2ZXJJZCI6ImxvY2FsIn0= - - name: Setup default JFrog CLI - if: matrix.version == '' - uses: ./ - env: - JF_ARTIFACTORY_LOCAL: eyJ2ZXJzaW9uIjoxLCJ1cmwiOiJodHRwOi8vMTI3LjAuMC4xOjgwODEvYXJ0aWZhY3RvcnkvIiwidXNlciI6ImFkbWluIiwicGFzc3dvcmQiOiJBUEI3REVaUlBpSHFIRFRRb2tMa3g5aGh6S1QiLCJzZXJ2ZXJJZCI6ImxvY2FsIn0= - - name: Version # Check that the correct CLI version was downloaded - run: jfrog --version - - name: Sanity # Check local server successfully configured - run: jfrog c show local - - name: Check build URL - uses: wei/curl@master - with: - args: -I ${JFROG_CLI_BUILD_URL} - if: runner.os == 'Linux' - - name: Setup NodeJS # Download NodeJS to allow running the tests on macOS - uses: actions/setup-node@v1 - with: - node-version: '12.x' - - name: Check action environment - run: node scripts/check-env.js - - name: Install - run: npm i - - name: Unit tests - run: npm t + test: + runs-on: ${{ matrix.os }} + env: + JF_ENV_LOCAL: eyJ2ZXJzaW9uIjoxLCJ1cmwiOiJodHRwOi8vMTI3LjAuMC4xOjgwODEvYXJ0aWZhY3RvcnkvIiwidXNlciI6ImFkbWluIiwicGFzc3dvcmQiOiJBUEI3REVaUlBpSHFIRFRRb2tMa3g5aGh6S1QiLCJzZXJ2ZXJJZCI6ImxvY2FsIn0= + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macOS-latest] + version: ["", "latest", "1.29.0"] + fail-fast: false + steps: + # Checkout and install prerequisites + - name: Checkout + uses: actions/checkout@v2 + - name: Setup NodeJS + uses: actions/setup-node@v1 + with: + node-version: "14.x" + + # Run action according to the input version + - name: Setup JFrog CLI + if: matrix.version != '' + uses: ./ + with: + version: ${{ matrix.version }} + - name: Setup default JFrog CLI + if: matrix.version == '' + uses: ./ + + # Run --version + - name: Check versions + run: jf --version && jfrog --version + + # Check local server successfully configured + - name: Sanity + run: jf c show local || jfrog rt config show local + + # Check build URL + - name: Check build URL + uses: wei/curl@master + with: + args: -I ${JFROG_CLI_BUILD_URL} + if: runner.os == 'Linux' + + # Check environment variables + - name: Check action environment + run: node scripts/check-env.js + + # Install and run tests + - name: Install + run: npm i + - name: Unit tests + run: npm t diff --git a/README.md b/README.md index 171af4257..4bd1cecbc 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ This GitHub Action downloads, installs and configures JFrog CLI, so that it can be used as part of the workflow. In addition, the Action includes the following features, when using JFrog CLI to work with Artifactory. -* The connection details of the Artifactory servers used by JFrog CLI can be stored as secrets. Read more about it [here](#storing-artifactory-servers-details-as-secrets). +* The connection details of the JFrog platform used by JFrog CLI can be stored as secrets. Read more about it [here](#storing-JFrog-details-as-secrets). * There's no need to add the *build name* and *build number* options and arguments to commands which accept them. All build related operations will be automatically recorded with the *Workflow Name* as build name and *Run Number* as build number. @@ -20,51 +20,51 @@ All build related operations will be automatically recorded with the *Workflow N ```yml - uses: jfrog/setup-jfrog-cli@v2 -- run: jfrog --version +- run: jf --version ``` -## Storing Artifactory servers details as secrets +## Storing JFrog details as secrets ### General -The connection details of the Artifactory servers used by JFrog CLI can be stored as secrets. +The connection details of the JFrog platform used by JFrog CLI can be stored as secrets. ### Creating the configuration on your local machine -1. Make sure JFrog CLI version **1.29.0** or above is installed on your local machine by running ```jfrog -v```. -2. Configure the details of the Artifactory server by running ```jfrog c add```. -3. Export the details of the Artifactory server you configured, using the server ID you chose. Do this by running ```jfrog c export ```. +1. Make sure JFrog CLI version **1.29.0** or above is installed on your local machine by running ```jf -v```. +2. Configure the details of the JFrog platform by running ```jf c add```. +3. Export the details of the JFrog platform you configured, using the server ID you chose. Do this by running ```jf c export ```. 4. Copy the generated token to the clipboard and save it as a secret on GitHub. ### Using the secret in the workflow -To use the saved Artifactory server configuration in the workflow, all you need to do it to expose the secret to the workflow. -The secret should be exposed as an environment variable with the *JF_ARTIFACTORY_* prefix. +To use the saved JFrog platform configuration in the workflow, all you need to do it to expose the secret to the workflow. +The secret should be exposed as an environment variable with the *JF_ENV_* prefix. Here's how you do this: ```yml - uses: jfrog/setup-jfrog-cli@v2 env: - JF_ARTIFACTORY_1: ${{ secrets.JF_ARTIFACTORY_SECRET_1 }} + JF_ENV_1: ${{ secrets.JF_SECRET_ENV_1 }} - run: | # Ping the server - jfrog rt ping + jf rt ping ``` -As you can see in the example above, we created a secret named *JF_ARTIFACTORY_SECRET_1* and we exposed it to the workflow -as the *JF_ARTIFACTORY_1* environment variable. That's it - the ping command will now ping the configured Artifactory server. +As you can see in the example above, we created a secret named *JF_SECRET_ENV_1* and we exposed it to the workflow +as the *JF_ENV_1* environment variable. That's it - the ping command will now ping the configured Artifactory server. -If you have multiple Artifactory servers configured as secrets, you can use all of the in the workflow as follows: +If you have multiple JFrog platform configurations as secrets, you can use all of the in the workflow as follows: ```yml - uses: jfrog/setup-jfrog-cli@v2 env: - JF_ARTIFACTORY_1: ${{ secrets.JF_ARTIFACTORY_SECRET_1 }} - JF_ARTIFACTORY_2: ${{ secrets.JF_ARTIFACTORY_SECRET_2 }} + JF_ENV_1: ${{ secrets.JF_SECRET_ENV_1 }} + JF_ENV_2: ${{ secrets.JF_SECRET_ENV_2 }} - run: | - # Set the Artifactory server to use by providing the server ID (configured by the 'jfrog c add' command). - jfrog c use local-1 - # Ping local-1 - jfrog rt ping + # Set the JFrog configuration to use by providing the server ID (configured by the 'jf c add' command). + jf c use local-1 + # Ping local-1 Artifactory server + jf rt ping # Now use the second sever configuration exposed to the Action. - jfrog c use local-2 - # Ping local-2 - jfrog rt ping + jf c use local-2 + # Ping local-2 Xray server + jf xr ping ``` -| Important: When exposing more than one Artifactory servers to the Action, you should always add the ```jfrog c use``` command to specify the server to use. | +| Important: When exposing more than one JFrog configuration to the Action, you should always add the ```jf c use``` command to specify the server to use. | | --- | ## Setting the build name and build number when publishing build-info to Artifactory @@ -76,9 +76,9 @@ In the following example, all downloaded files are registered as dependencies of are registered as the build artifacts. ```yml - run: | - jfrog rt dl artifacts/ - jfrog rt u aether artifacts/ - jfrog rt bp + jf rt dl artifacts/ + jf rt u aether artifacts/ + jf rt bp ``` ## Setting JFrog CLI version diff --git a/lib/cleanup.js b/lib/cleanup.js index 397182c26..6128fd160 100644 --- a/lib/cleanup.js +++ b/lib/cleanup.js @@ -34,8 +34,8 @@ function cleanup() { return __awaiter(this, void 0, void 0, function* () { try { core.startGroup('Cleanup JFrog CLI servers configuration'); - let cliPath = yield utils_1.Utils.downloadCli(); - yield utils_1.Utils.removeArtifactoryServers(cliPath); + yield utils_1.Utils.addCliToPath(); + yield utils_1.Utils.removeJFrogServers(); } catch (error) { core.setFailed(error.message); diff --git a/lib/main.js b/lib/main.js index 43418ec90..b499f1b28 100644 --- a/lib/main.js +++ b/lib/main.js @@ -35,8 +35,8 @@ function main() { try { core.startGroup('Setup JFrog CLI'); utils_1.Utils.setCliEnv(); - let cliPath = yield utils_1.Utils.downloadCli(); - yield utils_1.Utils.configArtifactoryServers(cliPath); + yield utils_1.Utils.addCliToPath(); + yield utils_1.Utils.configJFrogServers(); } catch (error) { core.setFailed(error.message); diff --git a/lib/utils.js b/lib/utils.js index c69384232..93713294b 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -37,33 +37,66 @@ const os = __importStar(require("os")); const path = __importStar(require("path")); const semver = __importStar(require("semver")); class Utils { - static downloadCli() { + static addCliToPath() { return __awaiter(this, void 0, void 0, function* () { let version = core.getInput(Utils.CLI_VERSION_ARG); let major = version.split('.')[0]; if (version === this.LATEST_CLI_VERSION_ARG) { - version = '[RELEASE]'; + version = Utils.LATEST_RELEASE_VERSION; major = '2'; } else if (semver.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 fileName = Utils.getCliExecutableName(); - let cliDir = toolCache.find(fileName, version); - if (cliDir) { - core.addPath(cliDir); - return path.join(cliDir, fileName); + let jfFileName = Utils.getJfExecutableName(); + let jfrogFileName = Utils.getJFrogExecutableName(); + if (this.loadFromCache(jfFileName, jfrogFileName, version)) { + // Download is not needed + return; } - let url = Utils.getCliUrl(major, version, fileName); + // Download JFrog CLI + let url = Utils.getCliUrl(major, version, jfrogFileName); core.debug('Downloading JFrog CLI from ' + url); let downloadDir = yield toolCache.downloadTool(url); - cliDir = yield toolCache.cacheFile(downloadDir, fileName, fileName, version); - let cliPath = path.join(cliDir, fileName); + // Cache 'jf' and 'jfrog' executables + yield this.cacheAndAddPath(downloadDir, version, jfFileName); + yield this.cacheAndAddPath(downloadDir, version, jfrogFileName); + }); + } + /** + * 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); + if (jfExecDir && jfrogExecDir) { + core.addPath(jfExecDir); + core.addPath(jfrogExecDir); + return true; + } + return false; + } + /** + * Add JFrog CLI executables to cache and to the system path. + * @param downloadDir - The directory whereby the CLI was downloaded to + * @param version - JFrog CLI version + * @param fileName - 'jf', 'jfrog', 'jf.exe', or 'jfrog.exe' + */ + static cacheAndAddPath(downloadDir, version, fileName) { + return __awaiter(this, void 0, void 0, function* () { + let cliDir = yield toolCache.cacheFile(downloadDir, fileName, fileName, version); if (!Utils.isWindows()) { - fs.chmodSync(cliPath, 0o555); + fs.chmodSync(path.join(cliDir, fileName), 0o555); } core.addPath(cliDir); - return cliPath; }); } static getCliUrl(major, version, fileName) { @@ -71,13 +104,25 @@ class Utils { return 'https://releases.jfrog.io/artifactory/jfrog-cli/v' + major + '/' + version + '/' + architecture + '/' + fileName; } static getServerTokens() { - return Object.keys(process.env) - .filter((env) => env.match(Utils.SERVER_TOKEN_PREFIX)) - .map((envKey) => process.env[envKey] || ''); + let serverTokens = new Set(Object.keys(process.env) + .filter((envKey) => envKey.match(Utils.SERVER_TOKEN_PREFIX)) + .filter((envKey) => process.env[envKey]) + .map((envKey) => { var _a; return ((_a = process.env[envKey]) === null || _a === void 0 ? void 0 : _a.trim()) || ''; })); + let legacyServerTokens = new Set(Object.keys(process.env) + .filter((envKey) => envKey.match(Utils.SERVER_TOKEN_LEGACY_PREFIX)) + .filter((envKey) => process.env[envKey]) + .map((envKey) => { var _a; return ((_a = process.env[envKey]) === null || _a === void 0 ? void 0 : _a.trim()) || ''; })); + if (legacyServerTokens.size > 0) { + core.warning('The "JF_ARTIFACTORY_" prefix for environment variables is deprecated and is expected to be removed in v3. ' + + 'Please use the "JF_ENV_" prefix instead. The environment variables value should not be changed.'); + } + legacyServerTokens.forEach((serverToken) => serverTokens.add(serverToken)); + return serverTokens; } static setCliEnv() { - core.exportVariable('JFROG_CLI_ENV_EXCLUDE', '*password*;*secret*;*key*;*token*;JF_ARTIFACTORY_*'); + core.exportVariable('JFROG_CLI_ENV_EXCLUDE', '*password*;*secret*;*key*;*token*;*auth*;JF_ARTIFACTORY_*;JF_ENV_*'); core.exportVariable('JFROG_CLI_OFFER_CONFIG', 'false'); + core.exportVariable('CI', 'true'); let buildNameEnv = process.env.GITHUB_WORKFLOW; if (buildNameEnv) { core.exportVariable('JFROG_CLI_BUILD_NAME', buildNameEnv); @@ -89,7 +134,7 @@ class Utils { core.exportVariable('JFROG_CLI_BUILD_URL', process.env.GITHUB_SERVER_URL + '/' + process.env.GITHUB_REPOSITORY + '/actions/runs/' + process.env.GITHUB_RUN_ID); core.exportVariable('JFROG_CLI_USER_AGENT', Utils.USER_AGENT); } - static configArtifactoryServers(cliPath) { + static configJFrogServers() { return __awaiter(this, void 0, void 0, function* () { let useOldConfig = Utils.useOldConfig(); if (useOldConfig) { @@ -98,17 +143,17 @@ class Utils { } for (let serverToken of Utils.getServerTokens()) { let importCmd = useOldConfig ? ['rt', 'c', 'import', serverToken] : ['c', 'import', serverToken]; - yield Utils.runCli(cliPath, importCmd); + yield Utils.runCli(importCmd); } }); } - static removeArtifactoryServers(cliPath) { + static removeJFrogServers() { return __awaiter(this, void 0, void 0, function* () { if (Utils.useOldConfig()) { - yield Utils.runCli(cliPath, ['rt', 'c', 'clear', '--interactive=false']); + yield Utils.runCli(['rt', 'c', 'clear', '--interactive=false']); } else { - yield Utils.runCli(cliPath, ['c', 'rm', '--quiet']); + yield Utils.runCli(['c', 'rm', '--quiet']); } }); } @@ -124,15 +169,24 @@ class Utils { } return os.arch().includes('64') ? 'linux-amd64' : 'linux-386'; } - static getCliExecutableName() { + static getJfExecutableName() { + return Utils.isWindows() ? 'jf.exe' : 'jf'; + } + static getJFrogExecutableName() { return Utils.isWindows() ? 'jfrog.exe' : 'jfrog'; } static isWindows() { return os.platform().startsWith('win'); } - static runCli(cliPath, args) { + /** + * Execute JFrog CLI command. + * This GitHub Action downloads the requested 'jfrog' executable and stores it as 'jfrog' and 'jf'. + * Therefore the 'jf' executable is expected to be in the path also for older CLI versions. + * @param args - CLI arguments + */ + static runCli(args) { return __awaiter(this, void 0, void 0, function* () { - let res = yield exec_1.exec(cliPath, args); + let res = yield exec_1.exec('jf', args); if (res !== core.ExitCode.Success) { throw new Error('JFrog CLI exited with exit code ' + res); } @@ -152,9 +206,11 @@ class Utils { } exports.Utils = Utils; Utils.USER_AGENT = 'setup-jfrog-cli-github-action/' + require('../package.json').version; -Utils.SERVER_TOKEN_PREFIX = /^JF_ARTIFACTORY_.*$/; +Utils.SERVER_TOKEN_LEGACY_PREFIX = /^JF_ARTIFACTORY_.*$/; +Utils.SERVER_TOKEN_PREFIX = /^JF_ENV_.*$/; // Since 1.45.0, 'jfrog rt c' command changed to 'jfrog c add' Utils.NEW_CONFIG_CLI_VERSION = '1.45.0'; Utils.CLI_VERSION_ARG = 'version'; Utils.MIN_CLI_VERSION = '1.29.0'; Utils.LATEST_CLI_VERSION_ARG = 'latest'; +Utils.LATEST_RELEASE_VERSION = '[RELEASE]'; diff --git a/scripts/check-env.js b/scripts/check-env.js index ecef2428e..7c3f0e54d 100644 --- a/scripts/check-env.js +++ b/scripts/check-env.js @@ -6,8 +6,8 @@ checkEnv('JFROG_CLI_OFFER_CONFIG', 'false'); checkEnv('JFROG_CLI_BUILD_NAME', process.env.GITHUB_WORKFLOW); checkEnv('JFROG_CLI_BUILD_NUMBER', process.env.GITHUB_RUN_NUMBER); -checkEnv('JFROG_CLI_ENV_EXCLUDE', '*password*;*secret*;*key*;*token*;JF_ARTIFACTORY_*'); -checkEnv('JFROG_CLI_USER_AGENT', 'setup-jfrog-cli-github-action/' + require('../package.json').version) +checkEnv('JFROG_CLI_ENV_EXCLUDE', '*password*;*secret*;*key*;*token*;*auth*;JF_ARTIFACTORY_*;JF_ENV_*'); +checkEnv('JFROG_CLI_USER_AGENT', 'setup-jfrog-cli-github-action/' + require('../package.json').version); function checkEnv(envKey, expectedValue) { // Verify that the environment variable is not empty diff --git a/src/cleanup.ts b/src/cleanup.ts index 0e50b68d2..d0eb6c992 100644 --- a/src/cleanup.ts +++ b/src/cleanup.ts @@ -4,10 +4,10 @@ import { Utils } from './utils'; async function cleanup() { try { core.startGroup('Cleanup JFrog CLI servers configuration'); - let cliPath: string = await Utils.downloadCli(); - await Utils.removeArtifactoryServers(cliPath); + await Utils.addCliToPath(); + await Utils.removeJFrogServers(); } catch (error) { - core.setFailed(error.message); + core.setFailed((error).message); } finally { core.endGroup(); } diff --git a/src/main.ts b/src/main.ts index 82866fe75..c4fe1ec47 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,10 +5,10 @@ async function main() { try { core.startGroup('Setup JFrog CLI'); Utils.setCliEnv(); - let cliPath: string = await Utils.downloadCli(); - await Utils.configArtifactoryServers(cliPath); + await Utils.addCliToPath(); + await Utils.configJFrogServers(); } catch (error) { - core.setFailed(error.message); + core.setFailed((error).message); } finally { core.endGroup(); } diff --git a/src/utils.ts b/src/utils.ts index 24b1ff4a1..534aa85cf 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -8,38 +8,78 @@ import * as semver from 'semver'; export class Utils { public static readonly USER_AGENT: string = 'setup-jfrog-cli-github-action/' + require('../package.json').version; - public static readonly SERVER_TOKEN_PREFIX: RegExp = /^JF_ARTIFACTORY_.*$/; + public static readonly SERVER_TOKEN_LEGACY_PREFIX: RegExp = /^JF_ARTIFACTORY_.*$/; + public static readonly SERVER_TOKEN_PREFIX: RegExp = /^JF_ENV_.*$/; // Since 1.45.0, 'jfrog rt c' command changed to 'jfrog c add' public static readonly NEW_CONFIG_CLI_VERSION: string = '1.45.0'; public static readonly CLI_VERSION_ARG: string = 'version'; public static readonly MIN_CLI_VERSION: string = '1.29.0'; public static readonly LATEST_CLI_VERSION_ARG: string = 'latest'; - public static async downloadCli(): Promise { + private static readonly LATEST_RELEASE_VERSION: string = '[RELEASE]'; + + public static async addCliToPath() { let version: string = core.getInput(Utils.CLI_VERSION_ARG); let major: string = version.split('.')[0]; if (version === this.LATEST_CLI_VERSION_ARG) { - version = '[RELEASE]'; + version = Utils.LATEST_RELEASE_VERSION; major = '2'; } else if (semver.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 fileName: string = Utils.getCliExecutableName(); - let cliDir: string = toolCache.find(fileName, version); - if (cliDir) { - core.addPath(cliDir); - return path.join(cliDir, fileName); + + let jfFileName: string = Utils.getJfExecutableName(); + let jfrogFileName: string = Utils.getJFrogExecutableName(); + if (this.loadFromCache(jfFileName, jfrogFileName, version)) { + // Download is not needed + return; } - let url: string = Utils.getCliUrl(major, version, fileName); + + // Download JFrog CLI + let url: string = Utils.getCliUrl(major, version, jfrogFileName); core.debug('Downloading JFrog CLI from ' + url); let downloadDir: string = await toolCache.downloadTool(url); - cliDir = await toolCache.cacheFile(downloadDir, fileName, fileName, version); - let cliPath: string = path.join(cliDir, fileName); + + // Cache 'jf' and 'jfrog' executables + await this.cacheAndAddPath(downloadDir, version, jfFileName); + await this.cacheAndAddPath(downloadDir, version, jfrogFileName); + } + + /** + * 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 + */ + private static loadFromCache(jfFileName: string, jfrogFileName: string, version: string): boolean { + if (version === Utils.LATEST_RELEASE_VERSION) { + return false; + } + let jfExecDir: string = toolCache.find(jfFileName, version); + let jfrogExecDir: string = toolCache.find(jfrogFileName, version); + if (jfExecDir && jfrogExecDir) { + core.addPath(jfExecDir); + core.addPath(jfrogExecDir); + return true; + } + return false; + } + + /** + * Add JFrog CLI executables to cache and to the system path. + * @param downloadDir - The directory whereby the CLI was downloaded to + * @param version - JFrog CLI version + * @param fileName - 'jf', 'jfrog', 'jf.exe', or 'jfrog.exe' + */ + private static async cacheAndAddPath(downloadDir: string, version: string, fileName: string) { + let cliDir: string = await toolCache.cacheFile(downloadDir, fileName, fileName, version); + if (!Utils.isWindows()) { - fs.chmodSync(cliPath, 0o555); + fs.chmodSync(path.join(cliDir, fileName), 0o555); } core.addPath(cliDir); - return cliPath; } public static getCliUrl(major: string, version: string, fileName: string): string { @@ -47,15 +87,36 @@ export class Utils { return 'https://releases.jfrog.io/artifactory/jfrog-cli/v' + major + '/' + version + '/' + architecture + '/' + fileName; } - public static getServerTokens(): string[] { - return Object.keys(process.env) - .filter((env) => env.match(Utils.SERVER_TOKEN_PREFIX)) - .map((envKey) => process.env[envKey] || ''); + public static getServerTokens(): Set { + let serverTokens: Set = new Set( + Object.keys(process.env) + .filter((envKey) => envKey.match(Utils.SERVER_TOKEN_PREFIX)) + .filter((envKey) => process.env[envKey]) + .map((envKey) => process.env[envKey]?.trim() || '') + ); + + let legacyServerTokens: Set = new Set( + Object.keys(process.env) + .filter((envKey) => envKey.match(Utils.SERVER_TOKEN_LEGACY_PREFIX)) + .filter((envKey) => process.env[envKey]) + .map((envKey) => process.env[envKey]?.trim() || '') + ); + + if (legacyServerTokens.size > 0) { + core.warning( + 'The "JF_ARTIFACTORY_" prefix for environment variables is deprecated and is expected to be removed in v3. ' + + 'Please use the "JF_ENV_" prefix instead. The environment variables value should not be changed.' + ); + } + + legacyServerTokens.forEach((serverToken) => serverTokens.add(serverToken)); + return serverTokens; } public static setCliEnv() { - core.exportVariable('JFROG_CLI_ENV_EXCLUDE', '*password*;*secret*;*key*;*token*;JF_ARTIFACTORY_*'); + core.exportVariable('JFROG_CLI_ENV_EXCLUDE', '*password*;*secret*;*key*;*token*;*auth*;JF_ARTIFACTORY_*;JF_ENV_*'); core.exportVariable('JFROG_CLI_OFFER_CONFIG', 'false'); + core.exportVariable('CI', 'true'); let buildNameEnv: string | undefined = process.env.GITHUB_WORKFLOW; if (buildNameEnv) { core.exportVariable('JFROG_CLI_BUILD_NAME', buildNameEnv); @@ -71,7 +132,7 @@ export class Utils { core.exportVariable('JFROG_CLI_USER_AGENT', Utils.USER_AGENT); } - public static async configArtifactoryServers(cliPath: string) { + public static async configJFrogServers() { let useOldConfig: boolean = Utils.useOldConfig(); if (useOldConfig) { let version: string = core.getInput(Utils.CLI_VERSION_ARG); @@ -79,15 +140,15 @@ export class Utils { } for (let serverToken of Utils.getServerTokens()) { let importCmd: string[] = useOldConfig ? ['rt', 'c', 'import', serverToken] : ['c', 'import', serverToken]; - await Utils.runCli(cliPath, importCmd); + await Utils.runCli(importCmd); } } - public static async removeArtifactoryServers(cliPath: string) { + public static async removeJFrogServers() { if (Utils.useOldConfig()) { - await Utils.runCli(cliPath, ['rt', 'c', 'clear', '--interactive=false']); + await Utils.runCli(['rt', 'c', 'clear', '--interactive=false']); } else { - await Utils.runCli(cliPath, ['c', 'rm', '--quiet']); + await Utils.runCli(['c', 'rm', '--quiet']); } } @@ -104,7 +165,11 @@ export class Utils { return os.arch().includes('64') ? 'linux-amd64' : 'linux-386'; } - public static getCliExecutableName() { + public static getJfExecutableName() { + return Utils.isWindows() ? 'jf.exe' : 'jf'; + } + + public static getJFrogExecutableName() { return Utils.isWindows() ? 'jfrog.exe' : 'jfrog'; } @@ -112,8 +177,14 @@ export class Utils { return os.platform().startsWith('win'); } - public static async runCli(cliPath: string, args: string[] | undefined) { - let res: number = await exec(cliPath, args); + /** + * Execute JFrog CLI command. + * This GitHub Action downloads the requested 'jfrog' executable and stores it as 'jfrog' and 'jf'. + * Therefore the 'jf' executable is expected to be in the path also for older CLI versions. + * @param args - CLI arguments + */ + public static async runCli(args: string[]) { + let res: number = await exec('jf', args); if (res !== core.ExitCode.Success) { throw new Error('JFrog CLI exited with exit code ' + res); } diff --git a/test/main.spec.ts b/test/main.spec.ts index 49fabcfdd..b8eba4f54 100644 --- a/test/main.spec.ts +++ b/test/main.spec.ts @@ -3,21 +3,42 @@ import { Utils } from '../src/utils'; jest.mock('os'); describe('JFrog CLI action Tests', () => { + beforeEach(() => { + ['JF_ARTIFACTORY_1', 'JF_ARTIFACTORY_2', 'ARTIFACTORY_JF_1', 'JF_ENV_1', 'JF_ENV_2', 'ENV_JF_1', 'JF_ENV_LOCAL'].forEach((envKey) => { + delete process.env[envKey]; + }); + }); + test('Get server tokens', async () => { - let serverTokens: string[] = Utils.getServerTokens(); - expect(serverTokens).toStrictEqual([]); + let serverTokens: Set = Utils.getServerTokens(); + expect(serverTokens.size).toBe(0); - process.env['ARTIFACTORY_JF_1'] = 'ILLEGAL_SERVER_TOKEN'; + process.env['ENV_JF_1'] = 'ILLEGAL_SERVER_TOKEN'; serverTokens = Utils.getServerTokens(); - expect(serverTokens).toStrictEqual([]); + expect(serverTokens.size).toBe(0); - process.env['JF_ARTIFACTORY_1'] = 'DUMMY_SERVER_TOKEN_1'; + process.env['JF_ENV_1'] = 'DUMMY_SERVER_TOKEN_1'; serverTokens = Utils.getServerTokens(); - expect(serverTokens).toStrictEqual(['DUMMY_SERVER_TOKEN_1']); + expect(serverTokens).toStrictEqual(new Set(['DUMMY_SERVER_TOKEN_1'])); - process.env['JF_ARTIFACTORY_2'] = 'DUMMY_SERVER_TOKEN_2'; + process.env['JF_ENV_2'] = 'DUMMY_SERVER_TOKEN_2'; serverTokens = Utils.getServerTokens(); - expect(serverTokens).toStrictEqual(['DUMMY_SERVER_TOKEN_1', 'DUMMY_SERVER_TOKEN_2']); + expect(serverTokens).toStrictEqual(new Set(['DUMMY_SERVER_TOKEN_1', 'DUMMY_SERVER_TOKEN_2'])); + }); + + test('Get legacy server tokens', async () => { + process.env['ARTIFACTORY_JF_1'] = 'ILLEGAL_SERVER_TOKEN'; + expect(Utils.getServerTokens().size).toBe(0); + + process.env['JF_ARTIFACTORY_1'] = 'DUMMY_SERVER_TOKEN_1'; + expect(Utils.getServerTokens()).toStrictEqual(new Set(['DUMMY_SERVER_TOKEN_1'])); + + process.env['JF_ARTIFACTORY_2'] = 'DUMMY_SERVER_TOKEN_2'; + expect(Utils.getServerTokens()).toStrictEqual(new Set(['DUMMY_SERVER_TOKEN_1', 'DUMMY_SERVER_TOKEN_2'])); + + process.env['JF_ENV_1'] = 'DUMMY_SERVER_TOKEN_1'; + process.env['JF_ENV_2'] = 'DUMMY_SERVER_TOKEN_3'; + expect(Utils.getServerTokens()).toStrictEqual(new Set(['DUMMY_SERVER_TOKEN_1', 'DUMMY_SERVER_TOKEN_2', 'DUMMY_SERVER_TOKEN_3'])); }); describe('JFrog CLI V1 URL Tests', () => {