feat(misc): add first version of the nx insights plugin

This commit is contained in:
Victor Savkin 2019-12-05 17:45:01 -05:00 committed by Victor Savkin
parent a346817d15
commit 6eae959184
23 changed files with 353 additions and 128 deletions

View File

@ -1,92 +0,0 @@
matrix:
include:
- os: linux
language: node_js
node_js: 12
dist: trusty
sudo: required
cache:
npm: false
addons:
chrome: stable
before_install:
- export DISPLAY=:99.0; sh -e /etc/init.d/xvfb start;
- npm install yarn@1.19.0 -g
install:
- yarn install --network-timeout 1000000 --frozen-lockfile
script:
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then yarn checkformat --head=$TRAVIS_PULL_REQUEST_SHA --base=$(git merge-base HEAD $TRAVIS_BRANCH); fi'
- yarn checkcommit
- yarn checkimports
- yarn documentation
- yarn test:all
- os: linux
language: node_js
node_js: 12
dist: trusty
sudo: required
cache:
npm: false
addons:
chrome: stable
before_install:
- export DISPLAY=:99.0; sh -e /etc/init.d/xvfb start;
- npm install yarn@1.19.0 -g
install:
- yarn install --network-timeout 1000000
script:
- yarn e2e-ci1 nx
- os: linux
language: node_js
node_js: 12
dist: trusty
sudo: required
cache:
npm: false
addons:
chrome: stable
before_install:
- export DISPLAY=:99.0; sh -e /etc/init.d/xvfb start;
- npm install yarn@1.19.0 -g
install:
- yarn install --network-timeout 1000000
script:
- yarn e2e-ci2 nx
- os: linux
language: node_js
node_js: 12
dist: trusty
sudo: required
cache:
npm: false
addons:
chrome: stable
before_install:
- export DISPLAY=:99.0; sh -e /etc/init.d/xvfb start;
- npm install yarn@1.19.0 -g
install:
- yarn install --network-timeout 1000000
script:
- yarn e2e-ci1 angular
- os: linux
language: node_js
node_js: 12
dist: trusty
sudo: required
cache:
npm: false
addons:
chrome: stable
before_install:
- export DISPLAY=:99.0; sh -e /etc/init.d/xvfb start;
- npm install yarn@1.19.0 -g
install:
- yarn install --network-timeout 1000000
script:
- yarn e2e-ci2 angular
notifications:
email: false
webhooks:
on_success: true
on_failure: true
on_start: true

View File

@ -2,7 +2,7 @@
<div align="center">
[![Build Status](https://travis-ci.org/nrwl/nx.svg?branch=master)](https://travis-ci.org/nrwl/nx)
[![CircleCI](https://circleci.com/gh/nrwl/nx.svg?style=svg)](https://circleci.com/gh/nrwl/nx)
[![License](https://img.shields.io/npm/l/@nrwl/workspace.svg?style=flat-square)]()
[![NPM Version](https://badge.fury.io/js/%40nrwl%2Fworkspace.svg)](https://www.npmjs.com/@nrwl/workspace)
[![Semantic Release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square)]()
@ -22,7 +22,7 @@
### Use Modern Tools
Using Nx, you can add TypeScript, Cypress, Jest, Prettier, Angular, React, Next.js and Nest into your dev workflow. Nx sets up these tools and allows you to use them seamlessly. Nx fully integrates with the other modern tools you already use and love.
Using Nx, you can add TypeScript, Cypress, Jest, Prettier, Angular, React, Storybook, Next.js and Nest into your dev workflow. Nx sets up these tools and allows you to use them seamlessly. Nx fully integrates with the other modern tools you already use and love.
### Build Full-Stack Applications

View File

@ -2,6 +2,7 @@
"angular",
"cypress",
"express",
"insights",
"jest",
"nest",
"next",

View File

@ -2,6 +2,7 @@
"angular",
"cypress",
"express",
"insights",
"jest",
"nest",
"next",

View File

@ -2,6 +2,7 @@
"angular",
"cypress",
"express",
"insights",
"jest",
"nest",
"next",

View File

@ -50,15 +50,6 @@ forEachCli(cliName => {
`
);
updateFile(`nx.json`, c => {
const content = JSON.parse(c);
content['tasksRunnerOptions'] = {
json: {
runner: '@nrwl/workspace/src/tasks-runner/json-output-tasks-runner'
}
};
return JSON.stringify(content);
});
const testOutput = JSON.parse(
runCommand(
`npm run nx print-affected --silent -- --files=apps/${myapp}/src/main.tsx --target=test`

View File

@ -26,6 +26,7 @@
"documentation": "./scripts/documentation/documentation.sh && yarn format && ./scripts/documentation/check-documentation.sh"
},
"devDependencies": {
"axios": "^0.19.0",
"@angular-devkit/architect": "0.803.14",
"@angular-devkit/build-angular": "0.803.14",
"@angular-devkit/build-ng-packagr": "0.803.14",

View File

@ -0,0 +1,3 @@
# Nx Insights
An optional plugin integrating Nx with Nx Insights.

View File

@ -0,0 +1,13 @@
{
"name": "Nx Insights",
"version": "0.1",
"schematics": {
"init": {
"factory": "./src/schematics/init/init",
"schema": "./src/schematics/init/schema.json",
"description": "Initialize the @nrwl/insights plugin",
"aliases": ["ng-add"],
"hidden": true
}
}
}

View File

@ -0,0 +1,3 @@
import insightsTaskRunner from './src/insights-task-runner';
export default insightsTaskRunner;

View File

@ -0,0 +1,29 @@
{
"name": "@nrwl/insights",
"version": "0.0.1",
"description": "Nx Insights plugin for Nx",
"repository": {
"type": "git",
"url": "git+https://github.com/nrwl/nx.git"
},
"keywords": [
"Monorepo",
"Nx",
"Insights"
],
"main": "index.js",
"types": "index.d.ts",
"author": "Victor Savkin",
"license": "MIT",
"bugs": {
"url": "https://github.com/nrwl/nx/issues"
},
"homepage": "https://nx.dev",
"schematics": "./collection.json",
"peerDependencies": {
"@nrwl/workspace": "*"
},
"dependencies": {
"axios": "^0.19.0"
}
}

View File

@ -0,0 +1,225 @@
import {
AffectedEvent,
Task,
TaskCompleteEvent,
TasksRunner
} from '@nrwl/workspace/src/tasks-runner/tasks-runner';
import { Observable, Subject } from 'rxjs';
import {
defaultTasksRunner,
DefaultTasksRunnerOptions
} from '@nrwl/workspace/src/tasks-runner/default-tasks-runner';
import * as fs from 'fs';
import { DependencyGraph } from '@nrwl/workspace/src/command-line/shared';
import { TasksMap } from '@nrwl/workspace/src/command-line/run-tasks/run-command';
const axios = require('axios');
interface InsightsTaskRunnerOptions extends DefaultTasksRunnerOptions {
insightsUrl?: string;
}
type Context = {
dependencyGraph: DependencyGraph;
tasksMap: TasksMap;
target: string;
};
const insightsTaskRunner: TasksRunner<InsightsTaskRunnerOptions> = (
tasks: Task[],
options: InsightsTaskRunnerOptions,
context: Context
): Observable<AffectedEvent> => {
const res = new Subject<AffectedEvent>();
const notifier = createNotifier(options, context);
let commandResult = true;
notifier.startCommand(tasks).then(() => {
defaultTasksRunner(tasks, options).subscribe({
next: (t: TaskCompleteEvent) => {
commandResult = commandResult && t.success;
res.next(t);
notifier.endTask(t.task.id, stringifyStatus(t.success), '');
},
complete: () => {
notifier.endCommand(stringifyStatus(commandResult)).then(() => {
printNxInsightsStatus(notifier, commandResult);
res.complete();
});
}
});
});
return res;
};
function createNotifier(
options: InsightsTaskRunnerOptions,
context: Context
): Notifier {
if (!process.env.NX_INSIGHTS_AUTH_TOKEN) {
reportSetupError(`NX_INSIGHTS_AUTH_TOKEN env variable is not set.`);
return new EmptyNotifier();
}
if (
process.env.NX_INSIGHTS_AUTH_TOKEN &&
!process.env.NX_INSIGHTS_BRANCH_ID
) {
reportSetupError(`NX_INSIGHTS_BRANCH_ID env variable is not set.`);
return new EmptyNotifier();
}
if (process.env.NX_INSIGHTS_AUTH_TOKEN && !process.env.NX_INSIGHTS_RUN_ID) {
reportSetupError(`NX_INSIGHTS_RUN_ID env variable is not set.`);
return new EmptyNotifier();
}
return new InsightsNotifier(options, context);
}
function reportSetupError(reason: string) {
console.error(`WARNING: Nx won't send data to Nx Insights.`);
console.error(`Reason: ${reason}`);
}
function printNxInsightsStatus(
notifier: EmptyNotifier,
commandResult: boolean
) {
if (notifier.errors.length > 0) {
if (commandResult) {
console.error(
`The command succeeded, but we were unable to send the data to Nx Insights:`
);
} else {
console.error(`We were unable to send the data to Nx Insights.`);
}
console.error(`Errors:`);
console.error(notifier.errors[0]);
}
}
function stringifyStatus(s: boolean) {
return s ? 'success' : 'failure';
}
interface Notifier {
errors: string[];
startCommand(tasks: Task[]): Promise<any>;
endCommand(result: string): Promise<any>;
endTask(taskId: string, result: string, log: string): void;
}
class EmptyNotifier implements Notifier {
errors = [];
startCommand(tasks: Task[]) {
return Promise.resolve();
}
endCommand(result: string) {
return Promise.resolve();
}
endTask(taskId: string, result: string, log: string) {}
}
class InsightsNotifier implements Notifier {
axiosInstance: any;
errors: string[] = [];
endTaskNotifications = [];
commandId: string;
constructor(
private readonly options: InsightsTaskRunnerOptions,
private readonly context: Context
) {
this.commandId = this.generateCommandId();
this.axiosInstance = axios.create({
baseURL: options.insightsUrl || 'https://nrwl.api.io',
timeout: 30000,
headers: { authorization: `auth ${this.envOptions.authToken}` }
});
}
startCommand(tasks: Task[]) {
const files = this.collectGlobalFiles();
const p = this.makePostRequest('nx-insights-record-start-command', {
workspaceId: '---', // should remove it
runContext: '---', // should remove it
branchId: this.envOptions.branchId,
runId: this.envOptions.runId,
commandId: this.commandId,
target: this.context.target,
packageJson: files.packageJson,
workspaceJson: files.workspaceJson,
nxJson: files.nxJson,
dependencyGraph: JSON.stringify(this.context.dependencyGraph),
startTime: new Date().toISOString()
});
return p.then(() => tasks.map(t => this.startTask(t)));
}
endCommand(result: string): Promise<any> {
return Promise.all(this.endTaskNotifications).then(() => {
return this.makePostRequest('nx-insights-record-end-command', {
commandId: this.commandId,
endTime: new Date().toISOString(),
result
});
});
}
endTask(taskId: string, result: string, log: string) {
this.endTaskNotifications.push(
this.makePostRequest('nx-insights-record-end-task', {
taskId,
endTime: new Date().toISOString(),
result,
log
})
);
}
private get envOptions() {
return {
branchId: process.env.NX_INSIGHTS_BRANCH_ID,
runId: process.env.NX_INSIGHTS_RUN_ID,
authToken: process.env.NX_INSIGHTS_AUTH_TOKEN
};
}
private startTask(task: Task) {
return this.makePostRequest('nx-insights-record-start-task', {
commandId: this.commandId,
taskId: task.id,
startTime: new Date().toISOString(),
target: task.target.target,
projectName: task.target.project
});
}
private collectGlobalFiles() {
return {
nxJson: fs.readFileSync('nx.json').toString(),
workspaceJson: fs.readFileSync('workspace.json').toString(),
packageJson: fs.readFileSync('package.json').toString()
};
}
private generateCommandId() {
return `${this.envOptions.runId}-${this.context.target}-${Math.floor(
Math.random() * 100000
)}`;
}
private makePostRequest(endpoint: string, post: Record<any, any>) {
return this.axiosInstance.post(endpoint, post).catch(e => {
this.errors.push(e.message);
});
}
}
export default insightsTaskRunner;

View File

@ -0,0 +1,14 @@
import { updateJsonInTree } from '@nrwl/workspace';
export default function init() {
return updateJsonInTree('nx.json', json => {
return {
...json,
tasksRunnerOptions: {
default: {
runner: '@nrwl/insights'
}
}
};
});
}

View File

@ -0,0 +1 @@
export interface Schema {}

View File

@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/schema",
"id": "NxInsightsInit",
"title": "Add Nx Insights Configuration to the workspace",
"type": "object",
"properties": {}
}

View File

@ -7,27 +7,33 @@ describe('getRunner', () => {
let nxJson: NxJson;
let mockRunner: TasksRunner;
let targetArgs: any;
let runner: string | undefined;
beforeEach(() => {
nxJson = {
npmScope: 'proj',
projects: {}
};
runner = undefined;
mockRunner = jest.fn();
targetArgs = { foo: 'bar' };
});
it('gets a default runner when runner is not defined in the nx json', () => {
const { tasksRunner, tasksOptions } = getRunner(runner, nxJson, targetArgs);
const { tasksRunner, tasksOptions } = getRunner(
undefined,
nxJson,
targetArgs
);
expect(tasksRunner).toEqual(defaultTasksRunner);
expect(tasksOptions).toEqual(targetArgs);
});
it('gets a default runner when default options are not configured', () => {
const { tasksRunner, tasksOptions } = getRunner(runner, nxJson, targetArgs);
const { tasksRunner, tasksOptions } = getRunner(
undefined,
nxJson,
targetArgs
);
expect(tasksRunner).toEqual(defaultTasksRunner);
expect(tasksOptions).toEqual(targetArgs);
@ -38,39 +44,42 @@ describe('getRunner', () => {
virtual: true
});
runner = 'custom';
nxJson.tasksRunnerOptions = {
custom: {
runner: 'custom-runner'
}
};
const { tasksRunner, tasksOptions } = getRunner(runner, nxJson, targetArgs);
const { tasksRunner, tasksOptions } = getRunner(
'custom',
nxJson,
targetArgs
);
expect(tasksRunner).toEqual(mockRunner);
expect(tasksOptions).toEqual(targetArgs);
});
it.only('gets a custom task runner with options', () => {
jest.mock('custom-runner', () => mockRunner, {
it('gets a custom task runner with options', () => {
jest.mock('custom-runner2', () => mockRunner, {
virtual: true
});
nxJson.tasksRunnerOptions = {
custom: {
runner: 'custom-runner',
runner: 'custom-runner2',
options: {
runnerOption: 'runner-option'
}
}
};
runner = 'custom';
const { tasksRunner, tasksOptions } = getRunner(runner, nxJson, targetArgs);
expect(tasksRunner).toEqual(mockRunner);
const { tasksRunner, tasksOptions } = getRunner(
'custom',
nxJson,
targetArgs
);
expect(tasksRunner).toBe(mockRunner);
expect(tasksOptions).toEqual({
runnerOption: 'runner-option',
foo: 'bar'
@ -88,7 +97,7 @@ describe('getRunner', () => {
}
};
const { tasksRunner } = getRunner(runner, nxJson, targetArgs);
const { tasksRunner } = getRunner(undefined, nxJson, targetArgs);
expect(tasksRunner).toEqual(mockRunner);
});

View File

@ -17,7 +17,7 @@ import {
} from './utils';
import * as yargs from 'yargs';
interface TasksMap {
export interface TasksMap {
[projectName: string]: { [targetName: string]: Task };
}
@ -66,6 +66,7 @@ export function runCommand<T extends RunArgs>(
targetArgs
);
tasksRunner(tasks, tasksOptions, {
target: nxArgs.target,
dependencyGraph: dependencyGraph,
tasksMap
}).subscribe({
@ -168,8 +169,16 @@ export function getRunner(
if (isRelativePath(modulePath)) {
modulePath = join(appRootPath, modulePath);
}
let tasksRunner = require(modulePath);
// to support both babel and ts formats
if (tasksRunner.default) {
throw new Error('boom');
tasksRunner = tasksRunner.default;
}
return {
tasksRunner: require(modulePath),
tasksRunner,
tasksOptions: {
...nxJson.tasksRunnerOptions[runner].options,
...targetArgs

View File

@ -27,6 +27,7 @@ export type TasksRunner<T = unknown> = (
tasks: Task[],
options?: T,
context?: {
target?: string;
dependencyGraph: DependencyGraph;
tasksMap: {
[projectName: string]: {

View File

@ -54,6 +54,7 @@ cp README.md build/packages/cli
cp README.md build/packages/tao
cp README.md build/packages/eslint-plugin-nx
cp README.md build/packages/linter
cp README.md build/packages/insights
cp LICENSE build/packages/builders
cp LICENSE build/packages/schematics
@ -74,6 +75,7 @@ cp LICENSE build/packages/cli
cp LICENSE build/packages/tao
cp LICENSE build/packages/eslint-plugin-nx
cp LICENSE build/packages/linter
cp LICENSE build/packages/insights
echo "Nx libraries available at build/packages:"
ls build/packages

View File

@ -1,6 +1,11 @@
#!/usr/bin/env bash
./scripts/link.sh fast
./scripts/link.sh
rm -rf tmp/nx/proj/node_modules/@nrwl
rm -rf tmp/angular/proj/node_modules/@nrwl
cp -r node_modules/@nrwl tmp/nx/proj/node_modules/@nrwl
cp -r node_modules/@nrwl tmp/angular/proj/node_modules/@nrwl
if [ -n "$1" ]; then
jest --maxWorkers=1 ./build/e2e/$1.test.js

View File

@ -162,7 +162,8 @@ const options = {
'build/npm/cli/package.json',
'build/npm/tao/package.json',
'build/npm/eslint-plugin-nx/package.json',
'build/npm/linter/package.json'
'build/npm/linter/package.json',
'build/npm/insights/package.json'
],
increment: parsedVersion.version,
requireUpstream: false,

View File

@ -17,13 +17,13 @@ cd build/packages
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i "" "s|exports.nxVersion = '\*';|exports.nxVersion = '$NX_VERSION';|g" {react,next,web,jest,node,express,nest,cypress,storybook,angular,workspace}/src/utils/versions.js
sed -i "" "s|\*|$NX_VERSION|g" {schematics,react,next,web,jest,node,express,nest,cypress,storybook,angular,workspace,cli,linter,tao,eslint-plugin-nx,create-nx-workspace}/package.json
sed -i "" "s|\*|$NX_VERSION|g" {schematics,react,next,web,jest,node,express,nest,cypress,storybook,angular,workspace,cli,linter,insights,tao,eslint-plugin-nx,create-nx-workspace}/package.json
sed -i "" "s|NX_VERSION|$NX_VERSION|g" create-nx-workspace/bin/create-nx-workspace.js
sed -i "" "s|ANGULAR_CLI_VERSION|$ANGULAR_CLI_VERSION|g" create-nx-workspace/bin/create-nx-workspace.js
sed -i "" "s|TYPESCRIPT_VERSION|$TYPESCRIPT_VERSION|g" create-nx-workspace/bin/create-nx-workspace.js
else
sed -i "s|exports.nxVersion = '\*';|exports.nxVersion = '$NX_VERSION';|g" {react,next,web,jest,node,express,nest,cypress,storybook,angular,workspace}/src/utils/versions.js
sed -i "s|\*|$NX_VERSION|g" {schematics,react,next,web,jest,node,express,nest,cypress,storybook,angular,workspace,cli,linter,tao,eslint-plugin-nx,create-nx-workspace}/package.json
sed -i "s|\*|$NX_VERSION|g" {schematics,react,next,web,jest,node,express,nest,cypress,storybook,angular,workspace,cli,linter,insights,tao,eslint-plugin-nx,create-nx-workspace}/package.json
sed -i "s|NX_VERSION|$NX_VERSION|g" create-nx-workspace/bin/create-nx-workspace.js
sed -i "s|ANGULAR_CLI_VERSION|$ANGULAR_CLI_VERSION|g" create-nx-workspace/bin/create-nx-workspace.js
sed -i "s|TYPESCRIPT_VERSION|$TYPESCRIPT_VERSION|g" create-nx-workspace/bin/create-nx-workspace.js
@ -31,9 +31,9 @@ fi
if [[ $NX_VERSION == "*" ]]; then
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -E -i "" "s|\"@nrwl\/([^\"]+)\": \"\\*\"|\"@nrwl\/\1\": \"file:$PWD\/\1\"|" {schematics,jest,web,react,next,node,express,nest,cypress,storybook,angular,workspace,linter,cli,tao,eslint-plugin-nx,create-nx-workspace}/package.json
sed -E -i "" "s|\"@nrwl\/([^\"]+)\": \"\\*\"|\"@nrwl\/\1\": \"file:$PWD\/\1\"|" {schematics,jest,web,react,next,node,express,nest,cypress,storybook,angular,workspace,insights,linter,cli,tao,eslint-plugin-nx,create-nx-workspace}/package.json
else
echo $PWD
sed -E -i "s|\"@nrwl\/([^\"]+)\": \"\\*\"|\"@nrwl\/\1\": \"file:$PWD\/\1\"|" {schematics,jest,web,react,next,node,express,nest,cypress,storybook,angular,workspace,linter,cli,tao,eslint-plugin-nx,create-nx-workspace}/package.json
sed -E -i "s|\"@nrwl\/([^\"]+)\": \"\\*\"|\"@nrwl\/\1\": \"file:$PWD\/\1\"|" {schematics,jest,web,react,next,node,express,nest,cypress,storybook,angular,workspace,insights,linter,cli,tao,eslint-plugin-nx,create-nx-workspace}/package.json
fi
fi

View File

@ -3680,7 +3680,7 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
axios@0.19.0:
axios@0.19.0, axios@^0.19.0:
version "0.19.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8"
integrity sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==