Add interactive UI #97

Merged
merged 7 commits into from Nov 4, 2016

Projects

None yet

9 participants

@zinserjan
Contributor
zinserjan commented Oct 9, 2016 edited

This PR adds a interactive ui when np runs without arguments as suggested in #52.

features:

  • choose increment type (e.g. patch) or specify own version (e.g. 0.1.3)
  • choose NPM dist-tag for pre-releases (except packages with "private": true)
    • user can choose from a list of already existing tags for this package or
    • specifiy a custom tag except latest
  • show info about which version you're going from and to.
  • user have to confirm the release to prevent accidental publishing of wrong versions

Edit(sindresorhus):

You can install this PR with: $ npm i -g 'zinserjan/np#interactive-ui' and run it with $ np.

screen shot 2016-10-11 at 03 03 21

@zinserjan zinserjan interactive ui when no version is provided
* choose increment type or specify own version
* choose NPM dist-tag for pre-releases
* private packages don't need dist-tags
* version helper for shared stuff
d9582ec
cli.js
@@ -2,14 +2,16 @@
'use strict';
const meow = require('meow');
const updateNotifier = require('update-notifier');
+const version = require('./lib/version');
+const interactiveui = require('./lib/interactiveui');
@sindresorhus
sindresorhus Oct 9, 2016 Owner

Just call the file and import: ui

@zinserjan
zinserjan Oct 9, 2016 Contributor

What do mean exacly with "just call the file and import ui" ?

const ui = require('./lib/interactiveui');

this?

@sindresorhus
sindresorhus Oct 9, 2016 Owner
const ui = require('./lib/ui');
const np = require('./');
const cli = meow(`
Usage
$ np <version>
Version can be:
- patch | minor | major | prepatch | preminor | premajor | prerelease | 1.2.3
+ ${version.SEMVER_INCREMENTS.join(' | ')} | 1.2.3
@sindresorhus
sindresorhus Oct 9, 2016 Owner

We should have a description here that running just np starts the interactive UI.

lib/version.js
+
+exports.satisfies = function satisfies(version, range) {
+ return semver.satisfies(version, range);
+};
@sindresorhus
sindresorhus Oct 9, 2016 Owner

Use arrow functions (inline ones when possible) for all these.

test/version.js
@@ -0,0 +1,114 @@
+import test from 'ava';
+import {SEMVER_INCREMENTS, PRERELEASE_VERSIONS, isValidVersionInput, isPrereleaseVersion, getNewVersion, isVersionGreater, isVersionLower, satisfies} from '../lib/version';
@sindresorhus
sindresorhus Oct 9, 2016 Owner

Too verbose. Just import version and use them as properties.

lib/interactiveui.js
+ type: 'list',
+ name: 'version',
+ message: 'Select semver increment or specify new version',
+ choices: version.SEMVER_INCREMENTS.concat([
@sindresorhus
sindresorhus Oct 9, 2016 Owner

I think we should show what the version would end up being with each increment:

patch (2.3.1)
minor (2.4.0)
major (3.0.0)

And the (2.3.1) should be dimmed with chalk.gray.dim(text).

https://github.com/chalk/chalk

@zinserjan
zinserjan Oct 9, 2016 Contributor

Good idea.

lib/interactiveui.js
+ value: null
+ }
+ ]),
+ filter(input) {
@sindresorhus
sindresorhus Oct 9, 2016 Owner

Use inline arrow function.

filter => version.isValidVersionInput(input) ? version.getNewVersion(pkg.version, input) : input,
@sindresorhus
sindresorhus Oct 9, 2016 Owner

Same for all the other prompt functions.

lib/interactiveui.js
+ },
+ validate(input) {
+ if (!version.isValidVersionInput(input)) {
+ return 'Please specify a valid semver, e.g. 1.2.3. See http://semver.org/';
@sindresorhus
sindresorhus Oct 9, 2016 Owner

http://semver.org/ => http://semver.org

lib/interactiveui.js
+ {
+ type: 'list',
+ name: 'tag',
+ message: 'How should this pre-release version be tagged in NPM?',
@sindresorhus
sindresorhus Oct 9, 2016 Owner

NPM => npm

lib/interactiveui.js
+ .then(stdout => {
+ const existingPreleaseTags = stdout.split('\n')
+ .map(line => line.split(':')[0].replace(/^\s|\s$/, ''))
+ .filter(line => line)
@sindresorhus
sindresorhus Oct 9, 2016 Owner

This doesn't do anything.

@SamVerschueren
SamVerschueren Oct 9, 2016 Collaborator

Maybe you mean .filter(Boolean)?

@zinserjan
zinserjan Oct 9, 2016 Contributor

Yep, that way my intention. But it seems to work fine without it.

lib/interactiveui.js
+ return !pkg.private && version.isPrereleaseVersion(answers.version) && !options.tag && !answers.tag;
+ },
+ validate(input) {
+ if (!input.length) {
@sindresorhus
sindresorhus Oct 9, 2016 Owner
if (input.length === 0) {
lib/interactiveui.js
+ },
+ validate(input) {
+ if (!input.length) {
+ return `Please specify a tag, e.g. next.`;
@sindresorhus
sindresorhus Oct 9, 2016 Owner
return 'Please specify a tag. For example: next';

And generally, don't use template literals when not needed.

lib/interactiveui.js
+ name: 'confirm',
+ message(answers) {
+ const tag = answers.tag || options.tag;
+ const msg = `Will bump from ${pkg.version} to ${answers.version}${tag ? ` and tag this release in NPM as ${tag}` : ''}. Continue?`;
@sindresorhus
Owner

Wow. ❤️ This is a really good pull request!

@sindresorhus sindresorhus changed the title from interactive ui when no version is provided to Add interactive UI Oct 9, 2016
@sindresorhus
Owner

Can you also document the interactive UI feature in the readme? Could have a new top-level section about it.

I think it would be useful to see the current version before the Inquirer questions:

$ np
Current version: 1.4.3
? Select semver increment or specify new version (Use arrow keys)
❯ patch 
  minor 
  major 

What do you think?

@sindresorhus
Owner

choose NPM dist-tag for pre-releases (except private package)

Why except for private packages?

@SamVerschueren
Collaborator

I think it would be useful to see the current version before the Inquirer questions

Could be useful, but necessary if we show the new version after patch, minor and major? Just wondering though :).

@sindresorhus
Owner

@SamVerschueren It gives a clear picture of where you're at without having to calculate backwards.

@SamVerschueren
Collaborator

Fair point :)

@zinserjan
Contributor

Can you also document the interactive UI feature in the readme? Could have a new top-level section about it.

I think a gif like in the intro section would be cool. How do you created that?

I think it would be useful to see the current version before the Inquirer questions

Makes sense for me.

Why except for private packages?

Private packages are not published to npm, that's why we don't need the "choose your dist-tag" step.
Have a look at this already existing code in index.js

@sindresorhus
Owner

I think a gif like in the intro section would be cool. How do you created that?

Yeah. I was planning to create that after this PR. I prefer to do it myself to ensure it's retina and the same style as the existing one. Here's how I do it: sindresorhus/ama#382

Private packages are not published to npm, that's why we don't need the "choose your dist-tag" step.
Have a look at this already existing code in index.js

Right. I was thinking of private packages, which is a thing, not "private": true in package.json.

@zinserjan
Contributor

Yeah. I was planning to create that after this PR. I prefer to do it myself to ensure it's retina and the same style as the existing one.

Can you also add the desired instructions about the interactive ui when you do this? I have no clear idea about this 😁

@zinserjan
Contributor

@sindresorhus please have a look again. I've made the requested changes except the instruction things.

@sindresorhus
Owner
sindresorhus commented Oct 10, 2016 edited

Looks great to me. I'm gonna use it for some days to ensure it's working fine and look for possible improvements.

@SamVerschueren @kevva @silverwind @scott113341 @unkillbob @developit @Hypercubed Would be awesome if you could try this out and provide some feedback if anything ;)

You can install this PR with: $ npm i -g 'zinserjan/np#interactive-ui' and run it with $ np.

Comment your npm username if you want to get added to https://www.npmjs.com/package/sindre-playground to be able to test publishing with a test package.

@sgulseth

should perhaps be [version], since it's an optional argument.

@sgrif
sgrif commented Oct 10, 2016

What is the difference between prepatch and prerelease?

@ragingwind

Please add me. npm-id: ragingwind

@unkillbob
Contributor

Tested on a couple of real publishes, worked awesome, love it 👌 💯

@sindresorhus
Owner

What is the difference between prepatch and prerelease?

That is a very good question. I don't even know. We copied those from npm version --help. The best explanation I could find is in the semver code and it's not even clear: https://github.com/npm/node-semver/blob/d21444a0658224b152ce54965d02dbe0856afb84/semver.js#L411-L413

@sindresorhus
Owner

Please add me. npm-id: ragingwind

Done

@SamVerschueren
Collaborator

It turns out the Node.js check is broken

screen shot 2016-10-11 at 08 56 40

@sindresorhus
Owner

should perhaps be [version], since it's an optional argument.

👍

@zinserjan
Contributor

should perhaps be [version], since it's an optional argument.

Yep, makes sense.

What is the difference between prepatch and prerelease?

I asked myself the same question a few days ago. I figured out that the option prerelease is basically just a way to increment the last pre release number. When the current version of the package isn't a prerelease (e.g. 1.0.0) it acts like prepatch.

Some examples how each option affects the version:

# starting from a normal release version
1.0.0 => prerelease=> 1.0.1-0
1.0.0 => prepatch => 1.0.1-0
1.0.0 => preminor=> 1.1.0-0
1.0.0 => premajor=> 2.0.0-0

# starting from a pre release version
1.0.1-0 => prerelease=> 1.0.1-1
1.1.0-0 => prerelease=> 1.1.0-1
2.0.0-0 => prerelease=> 2.0.0-1

It turns out the Node.js check is broken

Good catch. The version check is in the wrong order.

@SamVerschueren
Collaborator

Should we put the versions in a different color (e.g. 0.2.0 and 0.3.0)?

screen shot 2016-10-12 at 12 40 57

@SamVerschueren
Collaborator

Ran this for a couple of my releases and it works like a charm. Great work @zinserjan !

@Hypercubed
Contributor

Works great for me as well. In fact so well I wonder if it could be it's own package. version-chooser or something.

lib/ui.js
+ message: 'How should this pre-release version be tagged in npm?',
+ when: answers => !pkg.private && version.isPrereleaseVersion(answers.version) && !options.tag,
+ choices: () => {
+ return execa.stdout('npm', ['dist-tag', 'ls'])
@SamVerschueren
SamVerschueren Oct 18, 2016 Collaborator

Return execa immediately behind the arrow function

choices: () => execa.stdout()

lib/ui.js
+ choices: () => {
+ return execa.stdout('npm', ['dist-tag', 'ls'])
+ .then(stdout => {
+ const existingPreleaseTags = stdout.split('\n')
@SamVerschueren
SamVerschueren Oct 18, 2016 Collaborator

call it existringPrereleaseTags

lib/ui.js
+ .map(line => line.split(':')[0].replace(/^\s|\s$/, ''))
+ .filter(line => line.toLowerCase() !== 'latest');
+
+ if (!existingPreleaseTags.length) {
@SamVerschueren
SamVerschueren Oct 18, 2016 Collaborator

I would explicitely check for 0

existingPreleaseTags.length === 0

lib/ui.js
+ message: answers => {
+ const tag = answers.tag || options.tag;
+ const msg = `Will bump from ${oldVersion} to ${answers.version}${tag ? ` and tag this release in npm as ${tag}` : ''}. Continue?`;
+ return msg;
@SamVerschueren
SamVerschueren Oct 18, 2016 Collaborator

I think this would be clearer if it was written like this

const tag = answers.tag || options.tag;
const tagPart = tag ? ` and tag this release in npm as ${tag}` : '';

return `Will bump from ${oldVersion} to ${answers.version}${tagPart}. Continue?`;
@Hypercubed
Contributor

Hi @zinserjan , sorry, this is very tangential but are you interested in publishing your work on ./lib/ui as a separate module.

@sindresorhus
Owner

@Hypercubed That's too premature. Better to figure out what works and iterate on this here. We can explore what can be extracted as reusable at a later point.

@sindresorhus
Owner
sindresorhus commented Oct 22, 2016 edited

@zinserjan Any chance you could address @SamVerschueren's feedback so we could get this merged?

@zinserjan
Contributor

@sindresorhus @SamVerschueren
Done. There's an lint issue with a require statements in index.js. I just disabled this for now, cause assign to a unused variable leads to another eslint error.

@Hypercubed I don't see any reason at the moment to publish this under a separate module. When you see a need for this, feel free to do this. I personally don't need this in any other projects.

@zinserjan
Contributor

Should we put the versions in a different color (e.g. 0.2.0 and 0.3.0)?

@SamVerschueren Any suggestions?

@sindresorhus sindresorhus merged commit df32774 into sindresorhus:master Nov 4, 2016

1 check passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details
@sindresorhus
Owner

Woot. Landed! Thanks so much for working on this @zinserjan :)

superhighfive

@SamVerschueren
Collaborator

Great job @zinserjan !

@sindresorhus sindresorhus added a commit that referenced this pull request Nov 4, 2016
@sindresorhus follow-up to #97 53a7786
@sindresorhus
Owner

Dogfooding ftw!

screen shot 2016-11-04 at 15 56 46

@zinserjan
Contributor

Awesome! Thanks for merging ;)

@zckrs
zckrs commented Nov 4, 2016

Just fetched and ported to internal tool.

Thanks :-)

@revelt revelt referenced this pull request Nov 7, 2016
Closed

Interactive UI #52

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment