|
| 1 | +"use strict"; |
| 2 | +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { |
| 3 | + if (k2 === undefined) k2 = k; |
| 4 | + var desc = Object.getOwnPropertyDescriptor(m, k); |
| 5 | + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { |
| 6 | + desc = { enumerable: true, get: function() { return m[k]; } }; |
| 7 | + } |
| 8 | + Object.defineProperty(o, k2, desc); |
| 9 | +}) : (function(o, m, k, k2) { |
| 10 | + if (k2 === undefined) k2 = k; |
| 11 | + o[k2] = m[k]; |
| 12 | +})); |
| 13 | +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { |
| 14 | + Object.defineProperty(o, "default", { enumerable: true, value: v }); |
| 15 | +}) : function(o, v) { |
| 16 | + o["default"] = v; |
| 17 | +}); |
| 18 | +var __importStar = (this && this.__importStar) || function (mod) { |
| 19 | + if (mod && mod.__esModule) return mod; |
| 20 | + var result = {}; |
| 21 | + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); |
| 22 | + __setModuleDefault(result, mod); |
| 23 | + return result; |
| 24 | +}; |
| 25 | +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { |
| 26 | + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } |
| 27 | + return new (P || (P = Promise))(function (resolve, reject) { |
| 28 | + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } |
| 29 | + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } |
| 30 | + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } |
| 31 | + step((generator = generator.apply(thisArg, _arguments || [])).next()); |
| 32 | + }); |
| 33 | +}; |
| 34 | +Object.defineProperty(exports, "__esModule", { value: true }); |
| 35 | +exports.Utils = void 0; |
| 36 | +const core = __importStar(require("@actions/core")); |
| 37 | +const exec_1 = require("@actions/exec"); |
| 38 | +const github_1 = require("@actions/github"); |
| 39 | +const tool_cache_1 = require("@actions/tool-cache"); |
| 40 | +const fs_1 = require("fs"); |
| 41 | +const os_1 = require("os"); |
| 42 | +const path_1 = require("path"); |
| 43 | +const simple_git_1 = require("simple-git"); |
| 44 | +const http_client_1 = require("@actions/http-client"); |
| 45 | +class Utils { |
| 46 | + static addToPath() { |
| 47 | + var _a; |
| 48 | + return __awaiter(this, void 0, void 0, function* () { |
| 49 | + let fileName = Utils.getExecutableName(); |
| 50 | + let version = core.getInput(Utils.VERSION_ARG); |
| 51 | + let major = version.split('.')[0]; |
| 52 | + if (version === this.LATEST_CLI_VERSION_ARG) { |
| 53 | + version = Utils.LATEST_RELEASE_VERSION; |
| 54 | + major = '2'; |
| 55 | + } |
| 56 | + else { |
| 57 | + if (this.loadFromCache(version)) { |
| 58 | + // Download is not needed |
| 59 | + return; |
| 60 | + } |
| 61 | + } |
| 62 | + // Download Frogbot |
| 63 | + const releasesRepo = (_a = process.env.JF_RELEASES_REPO) !== null && _a !== void 0 ? _a : ''; |
| 64 | + let url = Utils.getCliUrl(major, version, fileName, releasesRepo); |
| 65 | + core.debug('Downloading Frogbot from ' + url); |
| 66 | + let auth = this.generateAuthString(releasesRepo); |
| 67 | + let downloadDir = yield (0, tool_cache_1.downloadTool)(url, '', auth); |
| 68 | + // Cache 'frogbot' executable |
| 69 | + yield this.cacheAndAddPath(downloadDir, version, fileName); |
| 70 | + }); |
| 71 | + } |
| 72 | + static generateAuthString(releasesRepo) { |
| 73 | + var _a, _b, _c; |
| 74 | + if (!releasesRepo) { |
| 75 | + return ''; |
| 76 | + } |
| 77 | + let accessToken = (_a = process.env.JF_ACCESS_TOKEN) !== null && _a !== void 0 ? _a : ''; |
| 78 | + let username = (_b = process.env.JF_USER) !== null && _b !== void 0 ? _b : ''; |
| 79 | + let password = (_c = process.env.JF_PASSWORD) !== null && _c !== void 0 ? _c : ''; |
| 80 | + if (accessToken) { |
| 81 | + return 'Bearer ' + Buffer.from(accessToken).toString(); |
| 82 | + } |
| 83 | + else if (username && password) { |
| 84 | + return 'Basic ' + Buffer.from(username + ':' + password).toString('base64'); |
| 85 | + } |
| 86 | + return ''; |
| 87 | + } |
| 88 | + static setFrogbotEnv() { |
| 89 | + return __awaiter(this, void 0, void 0, function* () { |
| 90 | + core.exportVariable('JF_GIT_PROVIDER', 'github'); |
| 91 | + core.exportVariable('JF_GIT_OWNER', github_1.context.repo.owner); |
| 92 | + let owner = github_1.context.repo.repo; |
| 93 | + if (owner) { |
| 94 | + core.exportVariable('JF_GIT_REPO', owner.substring(owner.indexOf('/') + 1)); |
| 95 | + } |
| 96 | + core.exportVariable('JF_GIT_PULL_REQUEST_ID', github_1.context.issue.number); |
| 97 | + return github_1.context.eventName; |
| 98 | + }); |
| 99 | + } |
| 100 | + /** |
| 101 | + * Execute frogbot scan-pull-request command. |
| 102 | + */ |
| 103 | + static execScanPullRequest() { |
| 104 | + return __awaiter(this, void 0, void 0, function* () { |
| 105 | + if (!process.env.JF_GIT_BASE_BRANCH) { |
| 106 | + core.exportVariable('JF_GIT_BASE_BRANCH', github_1.context.ref); |
| 107 | + } |
| 108 | + let res = yield (0, exec_1.exec)(Utils.getExecutableName(), ['scan-pull-request']); |
| 109 | + if (res !== core.ExitCode.Success) { |
| 110 | + throw new Error('Frogbot exited with exit code ' + res); |
| 111 | + } |
| 112 | + }); |
| 113 | + } |
| 114 | + /** |
| 115 | + * Execute frogbot scan-repository command. |
| 116 | + */ |
| 117 | + static execCreateFixPullRequests() { |
| 118 | + return __awaiter(this, void 0, void 0, function* () { |
| 119 | + if (!process.env.JF_GIT_BASE_BRANCH) { |
| 120 | + // Get the current branch we are checked on |
| 121 | + const git = (0, simple_git_1.simpleGit)(); |
| 122 | + try { |
| 123 | + const currentBranch = yield git.branch(); |
| 124 | + core.exportVariable('JF_GIT_BASE_BRANCH', currentBranch.current); |
| 125 | + } |
| 126 | + catch (error) { |
| 127 | + throw new Error('Error getting current branch from the .git folder: ' + error); |
| 128 | + } |
| 129 | + } |
| 130 | + let res = yield (0, exec_1.exec)(Utils.getExecutableName(), ['scan-repository']); |
| 131 | + if (res !== core.ExitCode.Success) { |
| 132 | + throw new Error('Frogbot exited with exit code ' + res); |
| 133 | + } |
| 134 | + }); |
| 135 | + } |
| 136 | + /** |
| 137 | + * Try to load the Frogbot executables from cache. |
| 138 | + * |
| 139 | + * @param version - Frogbot version |
| 140 | + * @returns true if the CLI executable was loaded from cache and added to path |
| 141 | + */ |
| 142 | + static loadFromCache(version) { |
| 143 | + let execPath = (0, tool_cache_1.find)(Utils.TOOL_NAME, version); |
| 144 | + if (execPath) { |
| 145 | + core.addPath(execPath); |
| 146 | + return true; |
| 147 | + } |
| 148 | + return false; |
| 149 | + } |
| 150 | + /** |
| 151 | + * Add Frogbot executable to cache and to the system path. |
| 152 | + * @param downloadDir - The directory whereby the CLI was downloaded to |
| 153 | + * @param version - Frogbot version |
| 154 | + * @param fileName - 'frogbot' or 'frogbot.exe' |
| 155 | + */ |
| 156 | + static cacheAndAddPath(downloadDir, version, fileName) { |
| 157 | + return __awaiter(this, void 0, void 0, function* () { |
| 158 | + let cliDir = yield (0, tool_cache_1.cacheFile)(downloadDir, fileName, Utils.TOOL_NAME, version); |
| 159 | + if (!Utils.isWindows()) { |
| 160 | + let filePath = (0, path_1.normalize)((0, path_1.join)(cliDir, fileName)); |
| 161 | + (0, fs_1.chmodSync)(filePath, 0o555); |
| 162 | + } |
| 163 | + core.addPath(cliDir); |
| 164 | + }); |
| 165 | + } |
| 166 | + static getCliUrl(major, version, fileName, releasesRepo) { |
| 167 | + var _a; |
| 168 | + let architecture = 'frogbot-' + Utils.getArchitecture(); |
| 169 | + if (releasesRepo) { |
| 170 | + let platformUrl = (_a = process.env.JF_URL) !== null && _a !== void 0 ? _a : ''; |
| 171 | + if (!platformUrl) { |
| 172 | + throw new Error('Failed while downloading Frogbot from Artifactory, JF_URL must be set'); |
| 173 | + } |
| 174 | + // Remove trailing slash if exists |
| 175 | + platformUrl = platformUrl.replace(/\/$/, ''); |
| 176 | + return `${platformUrl}/artifactory/${releasesRepo}/artifactory/frogbot/v${major}/${version}/${architecture}/${fileName}`; |
| 177 | + } |
| 178 | + return `https://releases.jfrog.io/artifactory/frogbot/v${major}/${version}/${architecture}/${fileName}`; |
| 179 | + } |
| 180 | + static getArchitecture() { |
| 181 | + if (Utils.isWindows()) { |
| 182 | + return 'windows-amd64'; |
| 183 | + } |
| 184 | + if ((0, os_1.platform)().includes('darwin')) { |
| 185 | + if ((0, os_1.arch)().includes('arm')) { |
| 186 | + return (0, os_1.arch)().includes('64') ? 'mac-arm64' : 'mac-arm'; |
| 187 | + } |
| 188 | + return 'mac-386'; |
| 189 | + } |
| 190 | + if ((0, os_1.arch)().includes('arm')) { |
| 191 | + return (0, os_1.arch)().includes('64') ? 'linux-arm64' : 'linux-arm'; |
| 192 | + } |
| 193 | + if ((0, os_1.arch)().includes('ppc64le')) { |
| 194 | + return 'linux-ppc64le'; |
| 195 | + } |
| 196 | + if ((0, os_1.arch)().includes('ppc64')) { |
| 197 | + return 'linux-ppc64'; |
| 198 | + } |
| 199 | + return (0, os_1.arch)().includes('64') ? 'linux-amd64' : 'linux-386'; |
| 200 | + } |
| 201 | + static getExecutableName() { |
| 202 | + return Utils.isWindows() ? 'frogbot.exe' : 'frogbot'; |
| 203 | + } |
| 204 | + static isWindows() { |
| 205 | + return (0, os_1.platform)().startsWith('win'); |
| 206 | + } |
| 207 | + static getJfrogPlatformUrl() { |
| 208 | + var _a; |
| 209 | + return __awaiter(this, void 0, void 0, function* () { |
| 210 | + let jfrogUrl = (_a = process.env.JF_URL) !== null && _a !== void 0 ? _a : ''; |
| 211 | + if (!jfrogUrl) { |
| 212 | + throw new Error('JF_URL must be provided and point on your full platform URL, for example: https://mycompany.jfrog.io/'); |
| 213 | + } |
| 214 | + return jfrogUrl; |
| 215 | + }); |
| 216 | + } |
| 217 | + /** |
| 218 | + * This method will set up an OIDC token if the OIDC integration is set. |
| 219 | + * If OIDC integration is set but not working, the action will fail causing frogbot to fail |
| 220 | + * @param jfrogUrl - The JFrog platform URL |
| 221 | + */ |
| 222 | + static setupOidcTokenIfNeeded(jfrogUrl) { |
| 223 | + return __awaiter(this, void 0, void 0, function* () { |
| 224 | + const oidcProviderName = core.getInput(Utils.OIDC_INTEGRATION_PROVIDER_NAME_ARG); |
| 225 | + if (!oidcProviderName) { |
| 226 | + // No token is set if an oidc-provider-name wasn't provided |
| 227 | + return; |
| 228 | + } |
| 229 | + core.debug('Obtaining an access token through OpenID Connect...'); |
| 230 | + const audience = core.getInput(Utils.OIDC_AUDIENCE_ARG); |
| 231 | + let jsonWebToken; |
| 232 | + try { |
| 233 | + core.debug('Fetching JSON web token'); |
| 234 | + jsonWebToken = yield core.getIDToken(audience); |
| 235 | + } |
| 236 | + catch (error) { |
| 237 | + throw new Error(`Getting openID Connect JSON web token failed: ${error.message}`); |
| 238 | + } |
| 239 | + try { |
| 240 | + return yield this.initJfrogAccessTokenThroughOidcProtocol(jfrogUrl, jsonWebToken, oidcProviderName); |
| 241 | + } |
| 242 | + catch (error) { |
| 243 | + throw new Error(`OIDC authentication against JFrog platform failed, please check OIDC settings and mappings on the JFrog platform: ${error.message}`); |
| 244 | + } |
| 245 | + }); |
| 246 | + } |
| 247 | + /** |
| 248 | + * This method exchanges a JSON web token with a JFrog access token through the OpenID Connect protocol |
| 249 | + * If we've reached this stage, the jfrogUrl field should hold a non-empty value obtained from process.env.JF_URL |
| 250 | + * @param jfrogUrl - The JFrog platform URL |
| 251 | + * @param jsonWebToken - The JSON web token used in the token exchange |
| 252 | + * @param oidcProviderName - The OpenID Connect provider name |
| 253 | + */ |
| 254 | + static initJfrogAccessTokenThroughOidcProtocol(jfrogUrl, jsonWebToken, oidcProviderName) { |
| 255 | + return __awaiter(this, void 0, void 0, function* () { |
| 256 | + const exchangeUrl = jfrogUrl.replace(/\/$/, '') + '/access/api/v1/oidc/token'; |
| 257 | + core.debug('Exchanging GitHub JSON web token with a JFrog access token...'); |
| 258 | + const httpClient = new http_client_1.HttpClient(); |
| 259 | + const data = `{ |
| 260 | + "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange", |
| 261 | + "subject_token_type": "urn:ietf:params:oauth:token-type:id_token", |
| 262 | + "subject_token": "${jsonWebToken}", |
| 263 | + "provider_name": "${oidcProviderName}" |
| 264 | + }`; |
| 265 | + const additionalHeaders = { |
| 266 | + 'Content-Type': 'application/json', |
| 267 | + }; |
| 268 | + const response = yield httpClient.post(exchangeUrl, data, additionalHeaders); |
| 269 | + const responseString = yield response.readBody(); |
| 270 | + const responseJson = JSON.parse(responseString); |
| 271 | + process.env.JF_ACCESS_TOKEN = responseJson.access_token; |
| 272 | + if (responseJson.access_token) { |
| 273 | + core.setSecret(responseJson.access_token); |
| 274 | + } |
| 275 | + if (responseJson.errors) { |
| 276 | + throw new Error(`${JSON.stringify(responseJson.errors)}`); |
| 277 | + } |
| 278 | + }); |
| 279 | + } |
| 280 | +} |
| 281 | +exports.Utils = Utils; |
| 282 | +Utils.LATEST_RELEASE_VERSION = '[RELEASE]'; |
| 283 | +Utils.LATEST_CLI_VERSION_ARG = 'latest'; |
| 284 | +Utils.VERSION_ARG = 'version'; |
| 285 | +Utils.TOOL_NAME = 'frogbot'; |
| 286 | +// OpenID Connect audience input |
| 287 | +Utils.OIDC_AUDIENCE_ARG = 'oidc-audience'; |
| 288 | +// OpenID Connect provider_name input |
| 289 | +Utils.OIDC_INTEGRATION_PROVIDER_NAME_ARG = 'oidc-provider-name'; |
0 commit comments