Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds analytics and user conf #32

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
# Changelog

## 2.3.4 - May 18, 2020
## 2.4.0 - 2020-06-03

* Documents and prioritizes the existing `apos` command alias as the CLI command.
* Documents and prioritizes the existing `apos` command alias as the CLI
command.
* Adds the opt-in option to send basic, anonymous usage information to the
maintainers. This involves the CLI creating a configuration file on the user's
machine to save the user's approval response and a random, unique ID.
* More colors! Command logs are no longer all an ❗️alarming❗️ shade of red. Red
now means something.

## 2.3.3 - May 6, 2020
## 2.3.3 - 2020-05-06

* Cleans up CLI output spacing. Also adds ESLint testing and documents the `--boilerplate` option.

Expand Down
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,26 @@ apos create-module <module name>

Remember to register the module in `apps.js` with the other module types.

## CLI analytics

To help us better understand how devs are using the CLI, you will have the option to *opt-in* to share basic usage information with the maintainers. This is limited to:
1. the command you have executed (only "create-project," "create-piece," "create-module," "create-widget," **not** any Apostrophe task commands);
2. if that command succeeded ("exec" or "error");
3. a random, anonymous user ID (IP addresses are anonymized to avoid connecting this ID with an individual).

**If you do not explicitly agree to participate, no infomation will be tracked.** Through the `universal-analytics` package, we use the option to anonymize your IP address, so we know as little as possible about you other than what is listed above. See the `sendAnalyticsEvent` utility method if you're curious how this all works.
abea marked this conversation as resolved.
Show resolved Hide resolved

### Why are asking to collect this information?

As we work to grow the Apostrophe community, knowing if, and how much, people are finding the CLI useful can tell us about the community's growth. It can also help us know if our work to make the CLI *more* useful for everyone is working!

## Run other Apostrophe-flavored command-line tasks

To run an Apostrophe command-line task with the Apostrophe CLI, which are conventionally run like this: `node app.js <namespace>:<task name>`, you may instead execute the following from any location within a project's directory:
```bash
apos <namespace>:<task name>
```



The Apostrophe CLI assumes the `apostrophe` namespace when executing tasks. This means that if a task is in the `apostrophe` namespace (such as the `apostrophe:generation` task), you can simply execute:
```bash
apos <task name>
Expand Down
83 changes: 45 additions & 38 deletions bin/apostrophe
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,67 @@

require('shelljs/global');
require('colors');
var program = require('commander');
var { program } = require('commander');
var util = require('../lib/util');
const confUtils = require('../lib/conf-utils');
var fs = require('fs');

program.version(getVersion());
async function execute () {
program.version(getVersion());

// Get version of apostrophe-cli and apostrophe.
function getVersion() {
var pkginfo = require('pkginfo')(module, 'version');
var cwd = process.cwd();
// Get version of apostrophe-cli and apostrophe.
function getVersion() {
require('pkginfo')(module, 'version');
var cwd = process.cwd();

var output = 'apostrophe-cli: v' + module.exports.version + '\n';
var aposPath = cwd + '/node_modules/apostrophe/';
var output = 'apostrophe-cli: v' + module.exports.version + '\n';
var aposPath = cwd + '/node_modules/apostrophe/';

// Append message for apostrophe.
if (fs.existsSync(aposPath)) {
var package = require(aposPath + 'package.json');
// Append message for apostrophe.
if (fs.existsSync(aposPath)) {
var aposPkg = require(aposPath + 'package.json');

output += 'apostrophe v' + package.version + ' is installed in this project.';
} else {
var request = require('sync-request');
var response = request('GET', 'https://raw.githubusercontent.com/apostrophecms/apostrophe-boilerplate/master/package.json');
output += 'apostrophe v' + aposPkg.version + ' is installed in this project.';
} else {
var request = require('sync-request');
var response = request('GET', 'https://raw.githubusercontent.com/apostrophecms/apostrophe-boilerplate/master/package.json');

if (response.statusCode == 200) {
var packageJSON = JSON.parse(response.body);
if (response.statusCode === 200) {
var packageJSON = JSON.parse(response.body);

output += 'apostrophe ' + packageJSON.dependencies.apostrophe + ' will be installed with a new project, according to the dependencies of apostrophe-boilerplate.';
} else {
var execSync = require('child_process').execSync;
var aposVersion = execSync('npm view apostrophe version').toString();
output += 'apostrophe ' + packageJSON.dependencies.apostrophe + ' will be installed with a new project, according to the dependencies of apostrophe-boilerplate.';
} else {
var execSync = require('child_process').execSync;
var aposVersion = execSync('npm view apostrophe version').toString();

output += 'apostrophe v' + aposVersion.trim() + ' (latest) will be installed with a new project, unless the boilerplate project\'s dependencies specify otherwise.';
output += 'apostrophe v' + aposVersion.trim() + ' (latest) will be installed with a new project, unless the boilerplate project\'s dependencies specify otherwise.';
}
}

return output;
}

return output;
}
util.checkDependencies();

util.checkDependencies();
require('../lib/commands/create')(program);
require('../lib/commands/create-widget')(program);
require('../lib/commands/create-piece')(program);
require('../lib/commands/create-module')(program);
var aposCmd = require('../lib/apostrophe')(program);
abea marked this conversation as resolved.
Show resolved Hide resolved

var createCmd = require('../lib/commands/create')(program);
var createWidgetCmd = require('../lib/commands/create-widget')(program);
var createPieceCmd = require('../lib/commands/create-piece')(program);
var createModuleCmd = require('../lib/commands/create-module')(program);
var aposCmd = require('../lib/apostrophe')(program);
program.on('--help', function() {
aposCmd.help();
});

program.on('--help', function() {
aposCmd.help();
});
await confUtils.checkConf();

program.parse(process.argv);
program.parseAsync(process.argv);

if (process.argv.length <= 2) {
// This means user passed no args, so display help
// Needs to come after parse, or command name won't register in help text
program.help();
if (process.argv.length <= 2) {
// This means user passed no args, so display help
// Needs to come after parse, or command name won't register in help text
program.help();
}
}

execute();
9 changes: 6 additions & 3 deletions lib/apostrophe.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ module.exports = function (program) {
.action(function(commandName, options) {
util.nlog(commandName, 'Running the task [1/1]');

var appPath = util.getAppPath();
var appPath = util.getAppPath(commandName);
var command = getCommand(commandName);

if (appPath) {
Expand All @@ -47,10 +47,13 @@ module.exports = function (program) {
var retVal = {};

retVal.help = function() {
var appPath = util.getAppPath();
var appPath = util.getAppPath('help');

if (appPath) {
console.log(' Apostrophe Tasks:');
// eslint-disable-next-line no-console
console.info(' Apostrophe Tasks:');
// eslint-disable-next-line no-console
console.info(' *****************');
exec('node ' + appPath + '/app.js apostrophe:tasks');
}
};
Expand Down
4 changes: 3 additions & 1 deletion lib/commands/create-module.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module.exports = function (program) {
.command('create-module <module-name>')
.description('Bootstrap a subclass of apostrophe-module with all the configuration you need to get started')
.action(function(moduleName, options) {
if (!util.getAppPath()) {
if (!util.getAppPath('create-module')) {
return false;
}

Expand All @@ -31,6 +31,8 @@ module.exports = function (program) {
fs.writeFileSync(path + '/index.js', moduleConfig);

util.success('create-module');

return true;

});
};
2 changes: 1 addition & 1 deletion lib/commands/create-piece.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module.exports = function (program) {
.option('--widgets', 'Configure a corresponding apostrophe-pieces-widgets module')
.action(function(pieceName, options) {

if (!util.getAppPath()) {
if (!util.getAppPath('create-piece')) {
return false;
}

Expand Down
3 changes: 2 additions & 1 deletion lib/commands/create-widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module.exports = function (program) {
.option('--player', 'Also add a public/js folder to your new apostrophe-widgets module directory with an always.js set up with an apostrophe player.')
.description('Bootstrap a subclass of apostrophe-widgets with all the configuration you need to get started')
.action(function(widgetName, options) {
if (!util.getAppPath()) {
if (!util.getAppPath('create-widget')) {
return false;
}

Expand Down Expand Up @@ -51,6 +51,7 @@ module.exports = function (program) {
}

util.success('create-widget');

return true;
});
};
33 changes: 14 additions & 19 deletions lib/commands/create.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require('shelljs/global');
// Utilities from shelljs
/* globals exec cd rm */
var prompt = require('prompt');
const prompts = require('prompts');
var util = require('../util');
var config = require('../../config');
var fs = require('fs');
Expand All @@ -12,7 +12,7 @@ module.exports = function (program) {
.description('Create a boilerplate Apostrophe 2.x project')
.option('--setup', 'Will setup the project further by installing dependencies and creating an admin user')
.option('--boilerplate [url]', 'Use a custom boilerplate to create the project with')
.action(function(shortName, options) {
.action(async function(shortName, options) {
var count = (options.install || options.setup) ? 3 : 2;
var boilerplateUrl = (options.boilerplate) ? options.boilerplate : config.APOSTROPHE_BOILERPLATE;

Expand Down Expand Up @@ -40,32 +40,27 @@ module.exports = function (program) {
secret = util.secret();
util.replaceInFiles(['./app.js'], /disabledFileKey: undefined/, 'disabledFileKey: \'' + secret + '\'');
// if we catch an install flag, do some stuff

if (options.install || options.setup) {
// update, not install, so package-lock.json gets updated
// & we're not married to a very old version of Apostrophe
exec('npm update');
// Create an admin user (note this will prompt for password)
util.nlog('create-project', 'Creating an admin user [3/3]');
util.nlog('create-project', 'Choose a password for the admin user');
prompt.start();
prompt.get({
properties: {
password: {
required: true,
hidden: true
}
}
}, function(err, result) {
if (err) {
util.error('create');
return false;
}
exec('echo "' + result.password + '" | node app.js apostrophe-users:add admin admin');
util.nlog('create', 'Login as "admin"');
util.success('create-project');
return true;

const response = await prompts({
type: 'password',
name: 'pw',
message: 'Please enter a password:'
});

exec('echo "' + response.pw + '" | node app.js apostrophe-users:add admin admin');
util.nlog('create', 'Login as "admin"');
}

util.success('create-project');
return true;
});
};

Expand Down
76 changes: 76 additions & 0 deletions lib/conf-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
const confUtils = {};
const prompts = require('prompts');
const Conf = require('conf');
const { v4: uuidv4 } = require('uuid');

const conf = new Conf({
configName: 'Apostrophe CLI',
projectName: 'apostrophe-cli',
projectSuffix: '',
schema: {
uid: {
type: 'string'
},
enableAnalytics: {
type: 'boolean',
default: false
},
prompted: {
type: 'boolean',
default: false
},
versionNotified: {
type: 'string'
}
}
});

module.exports = confUtils;

confUtils.getConf = function (propertyName) {
return conf.get(propertyName);
};

confUtils.setConf = function (propertyName, value) {
return conf.set(propertyName, value);
};

confUtils.checkConf = async function () {
let details = conf.store;

if (!details.prompted) {
details = await askPermission();
}

return details;
};

confUtils.clearConf = function () {
// Just in case there are permissions issues with deleting the file, let's
// clear it out first.
conf.clear();
};

confUtils.getPath = function () {
return conf.path;
};

async function askPermission () {
// eslint-disable-next-line no-console
console.info('\n👋 It looks like this might be your first time using the Apostrophe CLI on this computer.\n');
const response = await prompts({
type: 'confirm',
name: 'enableAnalytics',
message: 'Would you be willing to share basic, anonymous usage information with us? (This will have no effect on what you can do with the CLI)'
});

const uid = uuidv4();
conf.set('prompted', true);
conf.set('uid', uid);

if (response.enableAnalytics === true) {
conf.set('enableAnalytics', true);
}

return conf.store;
}
Loading