Skip to content

Commit d73e87d

Browse files
committed
updating frogbot action
1 parent 07ec6b3 commit d73e87d

File tree

4 files changed

+490
-1
lines changed

4 files changed

+490
-1
lines changed

action/lib/src/main.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
const core = __importStar(require("@actions/core"));
36+
const utils_1 = require("./utils");
37+
function main() {
38+
return __awaiter(this, void 0, void 0, function* () {
39+
try {
40+
core.startGroup('Frogbot');
41+
let jfrogUrl = yield utils_1.Utils.getJfrogPlatformUrl();
42+
yield utils_1.Utils.setupOidcTokenIfNeeded(jfrogUrl);
43+
const eventName = yield utils_1.Utils.setFrogbotEnv();
44+
yield utils_1.Utils.addToPath();
45+
switch (eventName) {
46+
case 'pull_request':
47+
case 'pull_request_target':
48+
yield utils_1.Utils.execScanPullRequest();
49+
break;
50+
case 'push':
51+
case 'schedule':
52+
case 'workflow_dispatch':
53+
yield utils_1.Utils.execCreateFixPullRequests();
54+
break;
55+
default:
56+
core.setFailed(eventName + ' event is not supported by Frogbot');
57+
}
58+
}
59+
catch (error) {
60+
core.setFailed(error.message);
61+
}
62+
finally {
63+
core.endGroup();
64+
}
65+
});
66+
}
67+
main();

action/lib/src/utils.js

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
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

Comments
 (0)