Skip to content

Commit

Permalink
feat: combine CLI package.
Browse files Browse the repository at this point in the history
  • Loading branch information
adamhenson committed Sep 23, 2019
1 parent 6a89ca1 commit 8080726
Show file tree
Hide file tree
Showing 12 changed files with 1,419 additions and 738 deletions.
12 changes: 12 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# cmon alpine https://github.com/GoogleChrome/lighthouse/issues/7246
FROM node:10.16.3-alpine

RUN apk update
RUN apk add chromium

# Grrrrr... https://support.circleci.com/hc/en-us/articles/360016505753-Resolve-Certificate-Signed-By-Unknown-Authority-error-in-Alpine-images?flash_digest=39b76521a337cecacac0cc10cb28f3747bb5fc6a
RUN apk add ca-certificates

RUN npm install @foo-software/lighthouse-check -g

CMD ["lighthouse-check"]
27 changes: 24 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# `@foo-software/lighthouse-check`

> An NPM module to run Lighthouse audits programatically. This project aims to extend base functionality of simply running an audit by providing bells and whistles for DevOps workflows. Easily implement in your Continuous Integration or Continuous Delivery pipeline.
> An NPM module and CLI to run Lighthouse audits programatically. This project aims to extend base functionality of simply running an audit by providing bells and whistles for DevOps workflows. Easily implement in your Continuous Integration or Continuous Delivery pipeline.
<img src="https://s3.amazonaws.com/foo.software/images/marketing/screenshots/lighthouse-audit-report.png" />

Expand All @@ -13,8 +13,8 @@
- Optionally save an HTML report locally.
- Optionally save an HTML report in an AWS S3 bucket.
- Easy setup with Slack Webhooks. Just add your Webhook URL and `lighthouse-check` will send results with details about authors and links to change sets if applicable (on GitHub).
- A CLI - see [`lighthouse-check-cli`](https://github.com/foo-software/lighthouse-check-cli)
- A Docker image - see the [`lighthouse-check-cli` project](https://github.com/foo-software/lighthouse-check-cli#docker) for details.
- A CLI - see [usage](#cli-usage).
- A Docker image - see [usage](#docker-usage).

## Install

Expand Down Expand Up @@ -216,6 +216,27 @@ import { lighthouseCheck } from '@foo-software/lighthouse-check';
</tr>
</table>

## CLI Usage

Running `lighthouse-check` in the example below will run Lighthouse audits against `https://www.foo.software` and `https://www.foo.software/contact`.

```bash
$ lighthouse-check --urls "https://www.foo.software,https://www.foo.software/contact"
```

## CLI Options

All options mirror [the NPM module](#options). The only difference is that array options like `urls` are passed in as a comma-separated string as an argument using the CLI.

## Docker Usage

```bash
$ docker pull foosoftware/lighthouse-check:latest
$ docker run foosoftware/lighthouse-check:latest \
lighthouse-check --verbose \
--urls "https://www.foo.software,https://www.foo.software/contact"
```

## Credits

> <img src="https://s3.amazonaws.com/foo.software/images/logo-200x200.png" width="100" height="100" align="left" /> This package was brought to you by [Foo - a website performance monitoring tool](https://www.foo.software). Create a **free account** with standard performance testing. Automatic website performance testing, uptime checks, charts showing performance metrics by day, month, and year. Foo also provides real time notifications when performance and uptime notifications when changes are detected. Users can integrate email, Slack and PagerDuty notifications.
1,776 changes: 1,043 additions & 733 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@foo-software/lighthouse-check",
"version": "0.0.21",
"description": "An NPM module for automated Lighthouse audits.",
"description": "An NPM module and CLI for automated Lighthouse audits.",
"main": "dist/index.js",
"repository": {
"type": "git",
Expand Down Expand Up @@ -52,7 +52,9 @@
"@foo-software/lighthouse-persist": "0.0.10",
"@slack/webhook": "^5.0.1",
"lodash.get": "^4.4.2",
"node-fetch": "^2.6.0"
"node-fetch": "^2.6.0",
"ora": "^3.4.0",
"table": "^5.4.6"
},
"devDependencies": {
"@babel/cli": "^7.4.4",
Expand Down
3 changes: 3 additions & 0 deletions scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Usage

- `./scripts/docker-publish.sh -v latest`
40 changes: 40 additions & 0 deletions scripts/docker-publish.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/bin/bash

DOCKER_TAG_NAME="lighthouse-check"
DOCKER_VERSION="production"
DOCKER_USERNAME="foosoftware"
DOCKERFILE_NAME="Dockerfile"

# set values from flags -v (version)
while getopts "v:" opt; do
case $opt in
v)
DOCKER_VERSION=$OPTARG
;;
esac
done

# if [ "$DOCKER_VERSION" == "base" ] ; then
# DOCKERFILE_NAME="Dockerfile"
# fi

# if [ "$DOCKER_VERSION" == "dev" ] ; then
# DOCKERFILE_NAME="Dockerfile-dev"
# fi

BUILD_COMMAND="docker build --no-cache -t ${DOCKER_TAG_NAME} . -f ${DOCKERFILE_NAME}"

echo "${BUILD_COMMAND}"
eval $BUILD_COMMAND

TAG_COMMAND="docker tag ${DOCKER_TAG_NAME} ${DOCKER_USERNAME}/${DOCKER_TAG_NAME}:${DOCKER_VERSION}"

echo "${TAG_COMMAND}"
eval $TAG_COMMAND

PUBLISH_COMMAND="docker push ${DOCKER_USERNAME}/${DOCKER_TAG_NAME}:${DOCKER_VERSION}"

echo "${PUBLISH_COMMAND}"
eval $PUBLISH_COMMAND

exit 0
137 changes: 137 additions & 0 deletions src/bin/lighthouse-check.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#! /usr/bin/env node
import ora from 'ora';
import fs from 'fs';
import path from 'path';
import { table, getBorderCharacters } from 'table';
import getLighthouseAuditTitlesByKey from '../helpers/getLighthouseAuditTitlesByKey';
import getHelpText from '../helpers/getHelpText';
import lighthouseCheck from '../lighthouseCheck';
import { NAME } from '../constants';
import { convertOptionsFromArguments } from '../helpers/arguments';

// config for the `table` module (for console logging a table)
const tableConfig = {
border: getBorderCharacters('ramac')
};

const defaultOptions = {
author: undefined,
apiToken: undefined,
awsAccessKeyId: undefined,
awsBucket: undefined,
awsRegion: undefined,
awsSecretAccessKey: undefined,
branch: undefined,
configFile: undefined,
emulatedFormFactor: undefined,
locale: undefined,
help: undefined,
outputDirectory: undefined,
pr: undefined,
sha: undefined,
slackWebhookUrl: undefined,
tag: undefined,
timeout: undefined,
throttling: undefined,
throttlingMethod: undefined,
urls: undefined,
verbose: false,
wait: undefined
};

// override options with any that are passed in as arguments
let params = convertOptionsFromArguments(defaultOptions);

const init = async () => {
const spinner = ora(`${NAME}: Running...\n`);

try {
if (params.configFile) {
const configFile = path.resolve(params.configFile);
const configJsonString = fs.readFileSync(configFile).toString();
const configJson = JSON.parse(configJsonString);

// extend params with config json file contents
params = {
...params,
...configJson
};
}

if (!params.verbose) {
console.log('\n');
spinner.start();
}

// if urls are in string format, we need to split them int an array,
// otherwise they may already be an array from a config json file.
const urls =
typeof params.urls !== 'string' ? params.urls : params.urls.split(',');

const result = await lighthouseCheck({
...params,
urls
});

if (!params.verbose) {
spinner.stop();
} else {
console.log('\n');
}

// log the header
const headerTable = [['Lighthouse Audit']];
const headerTableConfig = {
...tableConfig,
columns: {
0: {
paddingLeft: 29,
paddingRight: 29
}
}
};
console.log(table(headerTable, headerTableConfig));

// log results
result.data.forEach(result => {
console.log(`URL: ${result.url}`);

if (result.report) {
console.log(`Report: ${result.report}`);
}

if (result.localReport) {
console.log(`Local Report: ${result.localReport}`);
}

const tableData = [
getLighthouseAuditTitlesByKey(Object.keys(result.scores)),
Object.values(result.scores)
];
console.log('\n');
console.log(table(tableData, tableConfig));
console.log('\n');
});

process.exit();
} catch (error) {
if (!params.verbose) {
spinner.stop();
} else {
console.log('\n');
}

console.log(
'❌ Something went wrong while attempting to enqueue URLs for Lighthouse. See the error below.\n\n',
error
);
console.log('\n');
process.exit(1);
}
};

if (params.help) {
console.log(getHelpText(NAME));
} else {
init();
}
44 changes: 44 additions & 0 deletions src/helpers/arguments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// if the value is assigned as an argument get that... otherwise `undefined`
export const getArgument = name => {
const index = process.argv.indexOf(`--${name}`);

if (index < 0) {
return undefined;
}

// if our flag is the last `argv` or the one after has a flag
// assume the value should be `true`
if (
index === process.argv.length - 1 ||
process.argv[index + 1].includes('--')
) {
return true;
}

return process.argv[index + 1];
};

export const convertOptionsFromArguments = options =>
Object.keys(options).reduce((accumulator, current) => {
// get the argument value
const argumentValue = getArgument(current);

// if the value doesn't exist from an argument, use the existing option / value
let value =
typeof argumentValue !== 'undefined' ? argumentValue : options[current];

// convert string boolean to boolean
if (typeof value === 'string') {
const lowerCaseValue = value.toLowerCase();
if (lowerCaseValue === 'true') {
value = true;
} else if (lowerCaseValue === 'false') {
value = false;
}
}

return {
...accumulator,
[current]: value
};
}, {});
81 changes: 81 additions & 0 deletions src/helpers/arguments.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { getArgument, convertOptionsFromArguments } from './arguments';

describe('getArgument', () => {
const ARGV = process.argv;

beforeEach(() => {
// clear the cache
jest.resetModules();
process.argv = ['--ipsum', 'lorem', '--hello', 'world', '--foo', 'bar'];
});

afterEach(() => {
process.argv = ARGV;
});

it('should return the correct value of a command argument', () => {
const expected = 'world';
const actual = getArgument('hello');
expect(actual).toEqual(expected);
});

it('should return undefined of a non-existent command argument', () => {
const expected = undefined;
const actual = getArgument('non-existent');
expect(actual).toEqual(expected);
});

it('should return "true" of a flag without a value', () => {
process.argv = ['--ipsum', 'lorem', '--without-a-value', '--foo', 'bar'];
const expected = true;
const actual = getArgument('without-a-value');
expect(actual).toEqual(expected);
});

it('should return "true" of a flag without a value an at the end of a command.', () => {
process.argv.push('--without-a-value');
const expected = true;
const actual = getArgument('without-a-value');
expect(actual).toEqual(expected);
});
});

describe('convertOptionsFromArguments', () => {
const ARGV = process.argv;

beforeEach(() => {
// clear the cache
jest.resetModules();
process.argv = ['--ipsum', 'lorem', '--hello', 'world', '--foo', 'bar'];
});

afterEach(() => {
process.argv = ARGV;
});

it('should use default values when arguments are non-existent', () => {
const expected = {
nonexistent: 'lorem',
nonexistent2: false
};
const actual = convertOptionsFromArguments({
nonexistent: 'lorem',
nonexistent2: false
});
expect(actual).toEqual(expected);
});

it('should override options from arguments', () => {
const expected = {
ipsum: 'lorem',
another: 'one',
hello: 'world'
};
const actual = convertOptionsFromArguments({
ipsum: 'foobar',
another: 'one',
hello: 'something'
});
expect(actual).toEqual(expected);
});
});
5 changes: 5 additions & 0 deletions src/helpers/getHelpText.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default name => `
Usage: ${name} --option <argument>
See all options on GitHub: https://github.com/foo-software/lighthouse-check-cli#options
`;
Loading

0 comments on commit 8080726

Please sign in to comment.