Skip to content

Commit

Permalink
releasing v1.3
Browse files Browse the repository at this point in the history
  • Loading branch information
gcooney committed Aug 16, 2019
1 parent c7db667 commit 471cf73
Show file tree
Hide file tree
Showing 2,410 changed files with 260,246 additions and 0 deletions.
2 changes: 2 additions & 0 deletions lib/entities/Application.js
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
2 changes: 2 additions & 0 deletions lib/entities/Deployment.js
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
2 changes: 2 additions & 0 deletions lib/entities/ExecutionResult.js
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
184 changes: 184 additions & 0 deletions lib/index.js
@@ -0,0 +1,184 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
}
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
}
Object.defineProperty(exports, "__esModule", { value: true });
const mablApiClient_1 = require("./mablApiClient");
const table_1 = require("./table");
const request_promise_native_1 = __importDefault(require("request-promise-native"));
const core = __importStar(require("@actions/core/lib/core"));
const DEFAULT_MABL_APP_URL = 'https://app.mabl.com';
const EXECUTION_POLL_INTERVAL_MILLIS = 10000;
const EXECUTION_COMPLETED_STATUSES = [
'succeeded',
'failed',
'cancelled',
'completed',
'terminated',
];
const GITHUB_BASE_URL = 'https://api.github.com';
function run() {
return __awaiter(this, void 0, void 0, function* () {
try {
const applicationId = core.getInput('application-id', {
required: false,
});
const environmentId = core.getInput('environment-id', {
required: false,
});
const apiKey = process.env.MABL_API_KEY || '';
if (!apiKey) {
core.setFailed('MABL_API_KEY required');
}
// plan override options
const browserTypes = core.getInput('browser-types', {
required: false,
});
const uri = core.getInput('uri', { required: false });
// deployment action options
const rebaselineImages = parseBoolean(core.getInput('rebaseline-images', {
required: false,
}));
const setStaticBaseline = parseBoolean(core.getInput('set-static-baseline', {
required: false,
}));
const continueOnPlanFailure = parseBoolean(core.getInput('continue-on-failure', { required: false }));
const pullRequest = yield getRelatedPullRequest();
const eventTimeString = core.getInput('event-time', { required: false });
const eventTime = eventTimeString ? parseInt(eventTimeString) : Date.now();
let properties = {
triggering_event_name: process.env.GITHUB_EVENT_NAME,
repository_commit_username: process.env.GITHUB_ACTOR,
repository_action: process.env.GITHUB_ACTION,
repository_branch_name: process.env.GITHUB_REF,
repository_name: process.env.GITHUB_REPOSITORY,
repository_url: `[email protected]:${process.env.GITHUB_REPOSITORY}.git`,
};
if (pullRequest) {
properties = Object.assign(properties, {
repository_pull_request_url: pullRequest.url,
repository_pull_request_number: pullRequest.number,
repository_pull_request_title: pullRequest.title,
repository_pull_request_created_at: pullRequest.created_at,
});
if (pullRequest.merged_at) {
properties.repository_pull_request_merged_at = pullRequest.merged_at;
}
}
const baseApiUrl = process.env.APP_URL || DEFAULT_MABL_APP_URL;
// set up http client
let apiClient = new mablApiClient_1.mablApiClient(apiKey);
const revision = process.env.GITHUB_SHA;
// send the deployment
core.debug('Creating Deployment');
let deployment = yield apiClient.postDeploymentEvent(applicationId, environmentId, browserTypes, uri, rebaselineImages, setStaticBaseline, revision, eventTime, properties);
core.setOutput('mabl-deployment-id', deployment.id);
let outputLink = baseApiUrl;
if (applicationId) {
let application = yield apiClient.getApplication(applicationId);
outputLink = `${baseApiUrl}/workspaces/${application.organization_id}/events/${deployment.id}`;
core.debug(`Deployment triggered. View output at: ${outputLink}`);
}
// poll Execution result until complete
let executionComplete = false;
while (!executionComplete) {
yield new Promise(resolve => setTimeout(resolve, EXECUTION_POLL_INTERVAL_MILLIS));
let executionResult = yield apiClient.getExecutionResults(deployment.id);
if (executionResult && executionResult.executions) {
let pendingExecutions = getExecutionsStillPending(executionResult);
if (pendingExecutions.length === 0) {
executionComplete = true;
}
else {
core.debug(`${pendingExecutions.length} mabl plan(s) are still running`);
}
}
}
core.debug('mabl deployment runs have completed');
let finalExecutionResult = yield apiClient.getExecutionResults(deployment.id);
finalExecutionResult.executions.forEach((execution) => {
table_1.prettyPrintExecution(execution);
});
core.setOutput('plans_run', '' + finalExecutionResult.plan_execution_metrics.total);
core.setOutput('plans_passed', '' + finalExecutionResult.plan_execution_metrics.passed);
core.setOutput('plans_failed', '' + finalExecutionResult.plan_execution_metrics.failed);
core.setOutput('journeys_run', '' + finalExecutionResult.plan_execution_metrics.total);
core.setOutput('journeys_passed', '' + finalExecutionResult.plan_execution_metrics.passed);
core.setOutput('journeys_failed', '' + finalExecutionResult.plan_execution_metrics.failed);
if (finalExecutionResult.plan_execution_metrics.failed === 0) {
core.debug('Deployment plans passed');
}
else if (continueOnPlanFailure) {
core.warning(`There were ${finalExecutionResult.journey_execution_metrics.failed} journey failures but the continueOnPlanFailure flag is set so the task has been marked as passing`);
core.setNeutral();
}
else {
core.setFailed(`${finalExecutionResult.journey_execution_metrics.failed} mabl Journey(s) failed`);
}
}
catch (err) {
core.setFailed(`mabl deployment task failed for the following reason: ${err}`);
}
});
}
function parseBoolean(toParse) {
return !!(toParse && toParse.toLowerCase() == 'true');
}
function getExecutionsStillPending(executionResult) {
return executionResult.executions.filter((execution) => {
return !(EXECUTION_COMPLETED_STATUSES.includes(execution.status) &&
execution.stop_time);
});
}
function getRelatedPullRequest() {
const targetUrl = `${GITHUB_BASE_URL}/repos/${process.env.GITHUB_REPOSITORY}/commits/${process.env.GITHUB_SHA}/pulls`;
const githubToken = process.env.GITHUB_TOKEN;
if (!githubToken) {
return Promise.resolve();
}
const postOptions = {
method: 'GET',
url: targetUrl,
headers: {
Authorization: `token ${githubToken}`,
Accept: 'application/vnd.github.groot-preview+json',
'Content-Type': 'application/json',
'User-Agent': 'mabl-action',
},
json: true,
};
return request_promise_native_1.default(postOptions)
.then(response => {
if (!response || !response.length) {
return;
}
return {
title: response[0].title,
number: response[0].number,
created_at: response[0].created_at,
merged_at: response[0].merged_at,
url: response[0].url,
};
})
.catch(error => {
if (error.status != 404) {
core.warning(error.message);
}
});
}
run();
120 changes: 120 additions & 0 deletions lib/mablApiClient.js
@@ -0,0 +1,120 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
}
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
}
Object.defineProperty(exports, "__esModule", { value: true });
const httpm = __importStar(require("typed-rest-client/HttpClient"));
const hm = __importStar(require("typed-rest-client/Handlers"));
const async_retry_1 = __importDefault(require("async-retry"));
class mablApiClient {
constructor(apiKey) {
this.baseUrl = process.env.APP_URL || 'https://api.mabl.com';
let bh = new hm.BasicCredentialHandler('key', apiKey);
this.httpClient = new httpm.HttpClient('mabl-azure-devops-extension', [bh], {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
});
}
makeGetRequest(path) {
return __awaiter(this, void 0, void 0, function* () {
return yield async_retry_1.default(() => __awaiter(this, void 0, void 0, function* () {
let response = yield this.httpClient.get(path);
if ((response.message.statusCode || 400) >= 400) {
throw `[${response.message.statusCode} - ${response.message.statusMessage}]`;
}
let body = yield response.readBody();
let obj = JSON.parse(body);
return obj;
}), {
retries: 3,
});
});
}
makePostRequest(path, requestBody) {
return __awaiter(this, void 0, void 0, function* () {
return yield async_retry_1.default(() => __awaiter(this, void 0, void 0, function* () {
let response = yield this.httpClient.post(path, JSON.stringify(requestBody));
if ((response.message.statusCode || 400) >= 400) {
throw `[${response.message.statusCode} - ${response.message.statusMessage}]`;
}
let body = yield response.readBody();
let obj = JSON.parse(body);
return obj;
}), {
retries: 3,
});
});
}
getApplication(applicationId) {
return __awaiter(this, void 0, void 0, function* () {
try {
let response = yield this.makeGetRequest(`${this.baseUrl}/v1/applications/${applicationId}`);
return response;
}
catch (e) {
throw `failed to get mabl application ($applicationId) from the API ${e}`;
}
});
}
getExecutionResults(eventId) {
return __awaiter(this, void 0, void 0, function* () {
try {
let response = yield this.makeGetRequest(`${this.baseUrl}/execution/result/event/${eventId}`);
return response;
}
catch (e) {
throw `failed to get mabl execution results for event ${eventId} from the API ${e}`;
}
});
}
postDeploymentEvent(applicationId, environmentId, browserTypes, uri, rebaselineImages, setStaticBaseline, revision, eventTime, properties) {
return __awaiter(this, void 0, void 0, function* () {
try {
let requestBody = this.buildRequestBody(applicationId, environmentId, browserTypes, uri, rebaselineImages, setStaticBaseline, revision, eventTime, properties);
return yield this.makePostRequest(`${this.baseUrl}/events/deployment/`, requestBody);
}
catch (e) {
throw `failed to create deployment through mabl API ${e}`;
}
});
}
buildRequestBody(applicationId, environmentId, browserTypes, uri, rebaselineImages, setStaticBaseline, revision, event_time, properties) {
let requestBody = {};
environmentId ? (requestBody.environment_id = environmentId) : null;
applicationId ? (requestBody.application_id = applicationId) : null;
let planOverrides = {};
browserTypes
? (planOverrides.browser_types = browserTypes.split(','))
: null;
uri ? (planOverrides.uri = uri) : null;
requestBody.plan_overrides = planOverrides;
revision ? (requestBody.revision = revision) : null;
event_time ? (requestBody.event_time = event_time) : null;
properties ? (requestBody.properties = properties) : null;
let actions = {};
rebaselineImages ? (actions.rebaseline_images = rebaselineImages) : null;
setStaticBaseline
? (actions.set_static_baseline = setStaticBaseline)
: null;
requestBody.actions = actions;
return requestBody;
}
}
exports.mablApiClient = mablApiClient;
61 changes: 61 additions & 0 deletions lib/table.js
@@ -0,0 +1,61 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
}
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
}
Object.defineProperty(exports, "__esModule", { value: true });
const cli_table3_1 = __importDefault(require("cli-table3"));
const moment = __importStar(require("moment"));
function prettyPrintExecution(execution) {
let planTable = new cli_table3_1.default({
head: [],
style: {
head: [],
border: [],
},
colWidths: [15, 30, 15, 13, 15, 20, 17, 130],
wordWrap: true,
});
planTable.push([
'Plan Name:',
execution.plan.name,
'Status:',
execution.success ? 'Passed' : 'Failed',
'Duration:',
moment.utc(execution.stop_time - execution.start_time).format('HH:mm:ss'),
'mabl App Link:',
execution.plan.app_href,
]);
let journeyTable = new cli_table3_1.default({
head: ['Browser', 'Status', 'Journey Name', 'Duration', 'mabl App Link'],
style: {
head: [],
border: [],
},
colWidths: [10, 15, 27, 15, 160],
wordWrap: true,
});
execution.journey_executions.forEach(jE => {
let journey = execution.journeys.find(journey => journey.id === jE.journey_id);
journeyTable.push([
jE.browser_type,
jE.success ? 'Passed' : 'Failed',
journey ? journey.name : jE.journey_id,
moment.utc(jE.stop_time - jE.start_time).format('HH:mm:ss'),
jE.app_href,
]);
});
outputTable(planTable);
outputTable(journeyTable);
}
exports.prettyPrintExecution = prettyPrintExecution;
function outputTable(table) {
let tableAsString = table.toString().replace(/[\r\n]+/, '\n ');
console.log(tableAsString);
}

0 comments on commit 471cf73

Please sign in to comment.