Skip to content

Instantly share code, notes, and snippets.

@Comandeer
Last active June 29, 2019 18:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Comandeer/15de05e8363894ad75306ba5789e6855 to your computer and use it in GitHub Desktop.
Save Comandeer/15de05e8363894ad75306ba5789e6855 to your computer and use it in GitHub Desktop.
Simple utility to update supported Node versions in a project

update-node.js

Installation and usage

cd dir-with-cloned-update-node
npm i --no-save external-editor yaml
cd your-project
node path/to/update-node <min> <max> <branch>

where:

  • min – minimal supported version of Node.js,
  • max – maximal supported version of Node.js,
  • branch – branch, on which all actions should take place.

What does it do?

  1. Checkouts master and creates new branch.
  2. Updates all dependencies.
  3. Updates/adds engines field in package.json.
  4. Update CI config (GitLab, AppVeyor, Travis).
  5. Update .babelrc file.
  6. Opens visual editor to allow to add changelog entry.
  7. Run tests to see if everything went smoothly.
const { execSync } = require( 'child_process' );
const { resolve: resolvePath } = require( 'path' );
const { writeFileSync } = require( 'fs' );
const { existsSync } = require( 'fs' );
const { readFileSync } = require( 'fs' );
const yaml = require( 'yaml' );
const { edit } = require( 'external-editor' );
const cwd = process.cwd();
const minVersion = parseInt( process.argv[ 2 ], 10 );
const maxVersion = parseInt( process.argv[ 3 ], 10 );
const branch = process.argv[ 4 ];
execSync( 'git checkout master && git pull', { cwd } );
try {
execSync( `git checkout -b ${ branch }`, { cwd } );
} catch( e ) {
execSync( `git checkout ${ branch }`, { cwd } );
}
try {
execSync( 'npx npm-check -y', { cwd } );
} catch( e ) {}
execSync( 'rm -rf node_modules && npm i', { cwd } );
execSync( 'git add package.json && git add package-lock.json && git commit -m "chore(package): update dependencies"', { cwd } );
const packagePath = resolvePath( cwd, 'package.json' );
if ( existsSync( packagePath ) ) {
const package = require( packagePath );
const initial = readFileSync( packagePath, 'utf8' );
if ( !package.engines ) {
package.engines = {
node: ''
};
}
package.engines.node = `>=${ minVersion }.0.0`;
const edited = `${ JSON.stringify( package, null, 2 ) }\n`;
if ( initial !== edited ) {
writeFileSync( packagePath, edited, 'utf8' );
execSync( 'git add package.json && git commit -m "chore(package): update engines info"', { cwd } );
}
}
let ci = 0;
const gitLabPath = resolvePath( cwd, '.gitlab-ci.yml' );
if ( existsSync( gitLabPath ) ) {
const initial = readFileSync( gitLabPath, 'utf8' );
const gitLabConfig = yaml.parse( initial );
const min = Object.keys( gitLabConfig ).reduce( ( min, current ) => {
if ( !current.startsWith( 'node' ) ) {
return min;
}
const version = parseInt( current.substring( 4 ), 10 );
return version < min ? version : min;
}, Infinity );
const max = Object.keys( gitLabConfig ).reduce( ( max, current ) => {
if ( !current.startsWith( 'node' ) ) {
return max;
}
const version = parseInt( current.substring( 4 ), 10 );
return version > max ? version : max;
}, -Infinity );
let i = min;
while ( i < minVersion ) {
const current = `node${ i }`;
if ( gitLabConfig[ current ] ) {
delete gitLabConfig[ current ];
}
i++;
}
i = max < minVersion ? minVersion : max;
while ( i <= maxVersion ) {
if ( i % 2 !== 0 ) {
i++;
continue;
}
const current = `node${ i }`;
if ( !gitLabConfig[ current ] ) {
gitLabConfig[ current ] = {
stage: 'test',
image: `node:${ i }`,
before_script: [
'npm install'
],
script: [
'npm test'
]
};
}
i++;
}
const edited = yaml.stringify( gitLabConfig );
if ( initial !== edited ) {
writeFileSync( gitLabPath, edited, 'utf8' );
execSync( `git add ${ gitLabPath }` );
++ci;
}
}
const appVeyorPath = resolvePath( cwd, 'appveyor.yml' );
if ( existsSync( appVeyorPath ) ) {
const initial = readFileSync( appVeyorPath, 'utf8' );
const appVeyorConfig = yaml.parse( initial );
const matrix = appVeyorConfig.environment.matrix;
const max = parseInt( matrix[ 0 ].nodejs_version, 10 );
while ( true ) {
const el = matrix[ matrix.length - 1 ];
const current = el && parseInt( el.nodejs_version, 10 );
if ( !el || current >= minVersion ) {
break;
}
matrix.pop();
}
let i = max < minVersion ? minVersion : max + 1;
while ( i <= maxVersion ) {
if ( i % 2 !== 0 ) {
i++;
continue;
}
matrix.unshift( {
nodejs_version: String( i )
} );
i++;
}
const edited = yaml.stringify( appVeyorConfig ).replace( /"/g, `'` );
if ( initial !== edited ) {
writeFileSync( appVeyorPath, edited, 'utf8' );
execSync( `git add ${ appVeyorPath }` );
++ci;
}
}
const travisPath = resolvePath( cwd, '.travis.yml' );
if ( existsSync( travisPath ) ) {
const initial = readFileSync( travisPath, 'utf8' );
const travisConfig = yaml.parse( initial );
const node = travisConfig.node_js;
const max = parseInt( node[ 0 ], 10 );
while ( true ) {
const current = parseInt( node[ node.length - 1 ], 10 );
if ( current >= minVersion || Number.isNaN( current ) ) {
break;
}
node.pop();
}
let i = max < minVersion ? minVersion : max + 1;
while ( i <= maxVersion ) {
if ( i % 2 !== 0 ) {
i++;
continue;
}
node.unshift( i );
i++;
}
const edited = yaml.stringify( travisConfig ).replace( /"/g, `'` );
if ( initial !== edited ) {
writeFileSync( travisPath, edited, 'utf8' );
execSync( `git add ${ travisPath }` );
++ci;
}
}
if ( ci > 0 ) {
execSync( 'git commit -m "ci(*): update Node versions"', { cwd } );
}
const babelRcPath = resolvePath( cwd, '.babelrc' );
if ( existsSync( babelRcPath ) ) {
const initial = readFileSync( babelRcPath, 'utf8' );
const config = JSON.parse( initial );
config.presets[ 0 ][ 1 ].targets.node = `${ minVersion }.0.0`;
const edited = `${ JSON.stringify( config, null, '\t' ) }\n`;
if ( initial !== edited ) {
writeFileSync( babelRcPath, edited, 'utf8' );
execSync( 'git add .babelrc && git commit -m "chore(babel): update Node target"', { cwd } );
}
}
const changelogPath = resolvePath( cwd, 'CHANGELOG.md' );
if ( existsSync( changelogPath ) ) {
const initial = readFileSync( changelogPath, 'utf8' );
const edited = edit( initial );
if ( initial !== edited ) {
writeFileSync( changelogPath, edited, 'utf8' );
execSync( 'git add CHANGELOG.md && git commit -m "docs(changelog): add entry"' );
}
}
execSync( 'npm test', { cwd } );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment