2019-04-25 00:00:10 +02:00
#!/usr/bin/env node -r esm
import { spawn , exec } from 'child_process' ;
import inquirer from 'inquirer' ;
import chalk from 'chalk' ;
import detectFreePort from 'detect-port' ;
2019-09-27 14:53:07 +02:00
import dedent from 'ts-dedent' ;
2019-06-03 20:26:24 +02:00
import fs from 'fs' ;
2019-04-25 00:00:10 +02:00
import nodeCleanup from 'node-cleanup' ;
2019-04-25 11:44:33 +02:00
const logger = console ;
2020-03-27 20:04:50 +01:00
const freePort = ( port ) => detectFreePort ( port ) ;
2019-04-25 00:00:10 +02:00
2019-04-25 11:44:33 +02:00
let verdaccioProcess ;
2020-03-27 20:04:50 +01:00
const startVerdaccio = ( port ) => {
2019-04-25 00:00:10 +02:00
let resolved = false ;
2019-06-03 20:26:24 +02:00
return Promise . race ( [
2020-03-27 20:04:50 +01:00
new Promise ( ( res ) => {
2019-06-03 20:26:24 +02:00
verdaccioProcess = spawn ( 'npx' , [
'verdaccio@4.0.1' ,
'-c' ,
'scripts/verdaccio.yaml' ,
'-l' ,
port ,
] ) ;
2020-03-27 20:04:50 +01:00
verdaccioProcess . stdout . on ( 'data' , ( data ) => {
2019-06-03 20:26:24 +02:00
if ( ! resolved && data && data . toString ( ) . match ( /http address/ ) ) {
const [ url ] = data . toString ( ) . match ( /(http:.*\d\/)/ ) ;
res ( url ) ;
resolved = true ;
}
2020-03-27 20:04:50 +01:00
fs . appendFile ( 'verdaccio.log' , data , ( err ) => {
2019-06-03 20:26:24 +02:00
if ( err ) {
throw err ;
}
} ) ;
} ) ;
} ) ,
new Promise ( ( res , rej ) => {
setTimeout ( ( ) => {
if ( ! resolved ) {
rej ( new Error ( ` TIMEOUT - verdaccio didn't start within 60s ` ) ) ;
resolved = true ;
verdaccioProcess . kill ( ) ;
}
} , 60000 ) ;
} ) ,
] ) ;
2019-04-25 00:00:10 +02:00
} ;
const registryUrl = ( command , url ) =>
new Promise ( ( res , rej ) => {
const args = url ? [ 'config' , 'set' , 'registry' , url ] : [ 'config' , 'get' , 'registry' ] ;
exec ( ` ${ command } ${ args . join ( ' ' ) } ` , ( e , stdout ) => {
if ( e ) {
rej ( e ) ;
} else {
res ( url || stdout . toString ( ) . trim ( ) ) ;
}
} ) ;
} ) ;
const registriesUrl = ( yarnUrl , npmUrl ) =>
Promise . all ( [ registryUrl ( 'yarn' , yarnUrl ) , registryUrl ( 'npm' , npmUrl || yarnUrl ) ] ) ;
2019-04-25 11:44:33 +02:00
nodeCleanup ( ( ) => {
try {
verdaccioProcess . kill ( ) ;
} catch ( e ) {
//
}
} ) ;
2019-04-25 00:00:10 +02:00
const applyRegistriesUrl = ( yarnUrl , npmUrl , originalYarnUrl , originalNpmUrl ) => {
2019-04-25 11:44:33 +02:00
logger . log ( ` ↪️ changing system config ` ) ;
2019-04-25 00:00:10 +02:00
nodeCleanup ( ( ) => {
registriesUrl ( originalYarnUrl , originalNpmUrl ) ;
2019-09-27 14:53:07 +02:00
logger . log ( dedent `
2019-04-25 00:00:10 +02:00
Your registry config has been restored from :
npm : $ { npmUrl } to $ { originalNpmUrl }
yarn : $ { yarnUrl } to $ { originalYarnUrl }
` );
} ) ;
return registriesUrl ( yarnUrl , npmUrl ) ;
} ;
2020-03-27 20:04:50 +01:00
const addUser = ( url ) =>
2019-04-25 00:00:10 +02:00
new Promise ( ( res , rej ) => {
2019-04-25 11:44:33 +02:00
logger . log ( ` 👤 add temp user to verdaccio ` ) ;
2019-04-25 00:00:10 +02:00
2020-03-27 20:04:50 +01:00
exec ( ` npx npm-cli-adduser -r " ${ url } " -a -u user -p password -e user@example.com ` , ( e ) => {
2019-04-25 00:00:10 +02:00
if ( e ) {
rej ( e ) ;
} else {
res ( ) ;
}
} ) ;
} ) ;
const currentVersion = async ( ) => {
const { version } = ( await import ( '../lerna.json' ) ) . default ;
return version ;
} ;
const publish = ( packages , url ) =>
packages . reduce ( ( acc , { name , location } ) => {
return acc . then ( ( ) => {
return new Promise ( ( res , rej ) => {
2019-04-25 11:44:33 +02:00
logger . log ( ` 🛫 publishing ${ name } ( ${ location } ) ` ) ;
2019-04-25 00:00:10 +02:00
const command = ` cd ${ location } && npm publish --registry ${ url } --force --access restricted ` ;
2020-03-27 20:04:50 +01:00
exec ( command , ( e ) => {
2019-04-25 00:00:10 +02:00
if ( e ) {
rej ( e ) ;
} else {
2019-04-25 11:44:33 +02:00
logger . log ( ` 🛬 successful publish of ${ name } ! ` ) ;
2019-04-25 00:00:10 +02:00
res ( ) ;
}
} ) ;
} ) ;
} ) ;
} , Promise . resolve ( ) ) ;
const listOfPackages = ( ) =>
new Promise ( ( res , rej ) => {
const command = ` ./node_modules/.bin/lerna list --json ` ;
exec ( command , ( e , result ) => {
if ( e ) {
rej ( e ) ;
} else {
const data = JSON . parse ( result . toString ( ) . trim ( ) ) ;
res ( data ) ;
}
} ) ;
} ) ;
const askForPermission = ( ) =>
inquirer
. prompt ( [
{
type : 'confirm' ,
message : ` ${ chalk . red ( 'BE WARNED' ) } do you want to change your ${ chalk . underline (
'system'
) } default registry to the temp verdacio registry ? ` ,
name : 'sure' ,
} ,
] )
. then ( ( { sure } ) => sure ) ;
const askForReset = ( ) =>
inquirer
. prompt ( [
{
type : 'confirm' ,
message : ` ${ chalk . red (
'THIS IS BAD'
2019-06-03 20:26:24 +02:00
) } looks like something bad happened , OR you ' re already using a local registry , shall we reset to the default registry https : //registry.npmjs.org/ ?`,
2019-04-25 00:00:10 +02:00
name : 'sure' ,
} ,
] )
. then ( ( { sure } ) => {
if ( sure ) {
2019-04-25 11:44:33 +02:00
logger . log ( ` ↩️ changing system config ` ) ;
2019-04-25 00:00:10 +02:00
return registriesUrl ( 'https://registry.npmjs.org/' ) ;
}
return process . exit ( 1 ) ;
} ) ;
2019-04-25 11:44:33 +02:00
const askForPublish = ( packages , url , version ) =>
2019-04-25 00:00:10 +02:00
inquirer
. prompt ( [
{
type : 'confirm' ,
message : ` ${ chalk . green ( 'READY TO PUBLISH' ) } shall we kick off a publish? ` ,
name : 'sure' ,
} ,
] )
. then ( ( { sure } ) => {
if ( sure ) {
2019-04-25 11:44:33 +02:00
logger . log ( ` 🚀 publishing version ${ version } ` ) ;
return publish ( packages , url ) . then ( ( ) => askForPublish ( packages , url , version ) ) ;
2019-04-25 00:00:10 +02:00
}
return false ;
} ) ;
2020-03-27 20:04:50 +01:00
const askForSubset = ( packages ) =>
2019-04-25 00:00:10 +02:00
inquirer
. prompt ( [
{
type : 'checkbox' ,
message : 'which packages?' ,
name : 'subset' ,
pageSize : packages . length ,
2020-03-27 20:04:50 +01:00
choices : packages . map ( ( p ) => ( { name : p . name , checked : true } ) ) ,
2019-04-25 00:00:10 +02:00
} ,
] )
2020-03-27 20:04:50 +01:00
. then ( ( { subset } ) => packages . filter ( ( p ) => subset . includes ( p . name ) ) ) ;
2019-04-25 00:00:10 +02:00
const run = async ( ) => {
const port = await freePort ( 4873 ) ;
2019-04-25 11:44:33 +02:00
logger . log ( ` 🌏 found a open port: ${ port } ` ) ;
2019-04-25 00:00:10 +02:00
2019-04-25 11:44:33 +02:00
logger . log ( ` 🔖 reading current registry settings ` ) ;
2019-04-25 00:00:10 +02:00
let [ originalYarnRegistryUrl , originalNpmRegistryUrl ] = await registriesUrl ( ) ;
if (
originalYarnRegistryUrl . includes ( 'localhost' ) ||
originalNpmRegistryUrl . includes ( 'localhost' )
) {
await askForReset ( ) ;
originalYarnRegistryUrl = 'https://registry.npmjs.org/' ;
originalNpmRegistryUrl = 'https://registry.npmjs.org/' ;
}
2019-04-25 11:44:33 +02:00
logger . log ( ` 📐 reading version of storybook ` ) ;
logger . log ( ` 🚛 listing storybook packages ` ) ;
logger . log ( ` 🎬 starting verdaccio (this takes ±20 seconds, so be patient) ` ) ;
2019-04-25 00:00:10 +02:00
const [ shouldOverwrite , verdaccioUrl , packages , version ] = await Promise . all ( [
askForPermission ( ) ,
startVerdaccio ( port ) ,
listOfPackages ( ) ,
currentVersion ( ) ,
] ) ;
2019-04-25 11:44:33 +02:00
logger . log ( ` 🌿 verdaccio running on ${ verdaccioUrl } ` ) ;
2019-04-25 00:00:10 +02:00
if ( shouldOverwrite ) {
2019-09-27 14:53:07 +02:00
logger . log ( dedent `
2019-04-25 00:00:10 +02:00
You have chosen to change your system 's default registry url. If this process fails for some reason and doesn' t exit correctly , you may be stuck with a npm / yarn config that ' s broken .
To fix this you can revert back to the registry urls you had before by running :
> npm config set registry $ { originalNpmRegistryUrl }
> yarn config set registry $ { originalYarnRegistryUrl }
You can now use regular install procedure anywhere on your machine and the storybook packages will be installed from this local registry
The registry url is : $ { verdaccioUrl }
` );
} else {
2019-09-27 14:53:07 +02:00
logger . log ( dedent `
2019-04-25 00:00:10 +02:00
You have chosen to NOT change your system ' s default registry url .
The registry is running locally , but you ' ll need to add a npm / yarn config file in your project in that points to the registry .
Here ' s a documentation for npm : https : //docs.npmjs.com/files/npmrc
Yarn is able to read this file as well
The registry url is : $ { verdaccioUrl }
` );
}
2019-04-25 11:44:33 +02:00
if ( shouldOverwrite ) {
await applyRegistriesUrl (
verdaccioUrl ,
verdaccioUrl ,
originalYarnRegistryUrl ,
originalNpmRegistryUrl
) ;
}
2019-04-25 00:00:10 +02:00
await addUser ( verdaccioUrl ) ;
2019-04-25 11:44:33 +02:00
logger . log ( ` 📦 found ${ packages . length } storybook packages at version ${ chalk . blue ( version ) } ` ) ;
2019-04-25 00:00:10 +02:00
2019-04-25 11:44:33 +02:00
const subset = await askForSubset ( packages ) ;
2019-04-25 00:00:10 +02:00
2019-04-25 11:44:33 +02:00
await askForPublish ( subset , verdaccioUrl , version ) ;
2019-04-25 00:00:10 +02:00
2019-09-27 14:53:07 +02:00
logger . log ( dedent `
2019-04-25 11:44:33 +02:00
The verdaccio registry will now be terminated ( this can take ± 15 seconds , please be patient )
2019-04-25 00:00:10 +02:00
` );
2019-04-25 11:44:33 +02:00
verdaccioProcess . kill ( ) ;
2019-04-25 00:00:10 +02:00
} ;
run ( ) ;