diff --git a/.npmignore b/.npmignore index c6429e75c..1d3e4b403 100644 --- a/.npmignore +++ b/.npmignore @@ -9,10 +9,12 @@ CONTRIBUTING.md appveyor.yml babel.config.js dist/test +dist/typings header.png npm-debug.log* npm-shrinkwrap.json package-lock.json +scripts src test_apps tsconfig.json diff --git a/.npmrc b/.npmrc index ad50df662..e031d3432 100644 --- a/.npmrc +++ b/.npmrc @@ -1,3 +1,4 @@ engine-strict = true package-lock = false save-exact = true +scripts-prepend-node-path = true diff --git a/.travis.yml b/.travis.yml index 63f1fc3ad..bd496c30a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ os: - linux - osx node_js: - - "8.11.3" - "8" - "10" before_install: @@ -11,10 +10,13 @@ before_install: - export PATH="$HOME/.yarn/bin:$HOME/.config/yarn/global/node_modules/.bin:$PATH" cache: yarn: true +env: + - EMBARK_NO_PREPARE=true install: - yarn install - cd embark-ui && yarn install && cd .. - - git status && test -z "$(git status --porcelain)" + - npm run check-working-tree script: - npm run build:node - npm test + - npm run check-working-tree diff --git a/.yarnrc b/.yarnrc new file mode 100644 index 000000000..277017f65 --- /dev/null +++ b/.yarnrc @@ -0,0 +1 @@ +--install.check-files true diff --git a/appveyor.yml b/appveyor.yml index d6ed05391..9157e49a3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,8 +1,8 @@ environment: matrix: - - nodejs_version: "8.11.3" - nodejs_version: "8" - nodejs_version: "10" + EMBARK_NO_PREPARE: true cache: - "%LOCALAPPDATA%\\Yarn" install: @@ -11,11 +11,13 @@ install: - cmd /c start /wait msiexec.exe /i yarn-1.12.3.msi /quiet /qn /norestart - rm yarn-1.12.3.msi - node --version + - npm --version - yarn --version - yarn install - cd embark-ui && yarn install && cd .. - - git status && node -e "process.exit(require('child_process').execSync('git status --porcelain').toString().trim()===''?0:1)" + - npm run check-working-tree test_script: - npm run build:node - npm test + - npm run check-working-tree build: off diff --git a/embark-ui/.npmrc b/embark-ui/.npmrc index ad50df662..e031d3432 100644 --- a/embark-ui/.npmrc +++ b/embark-ui/.npmrc @@ -1,3 +1,4 @@ engine-strict = true package-lock = false save-exact = true +scripts-prepend-node-path = true diff --git a/embark-ui/.yarnrc b/embark-ui/.yarnrc new file mode 100644 index 000000000..277017f65 --- /dev/null +++ b/embark-ui/.yarnrc @@ -0,0 +1 @@ +--install.check-files true diff --git a/embark-ui/package.json b/embark-ui/package.json index 78bd054ae..03b174331 100644 --- a/embark-ui/package.json +++ b/embark-ui/package.json @@ -72,6 +72,7 @@ "redux": "4.0.1", "redux-saga": "0.16.2", "resolve": "1.8.1", + "rimraf": "2.6.2", "sass-loader": "7.1.0", "simple-line-icons": "2.4.1", "style-loader": "0.23.0", @@ -85,6 +86,7 @@ }, "scripts": { "build": "node scripts/build.js", + "clean": "rimraf build", "lint": "eslint src/", "start": "node scripts/start.js", "test": "node scripts/test.js" diff --git a/embark-ui/yarn.lock b/embark-ui/yarn.lock index e2c981032..1c779f019 100644 --- a/embark-ui/yarn.lock +++ b/embark-ui/yarn.lock @@ -9364,7 +9364,7 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= -rimraf@2, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@~2.6.2: +rimraf@2, rimraf@2.6.2, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@~2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" integrity sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w== diff --git a/package.json b/package.json index e398e7464..96bf8f4be 100644 --- a/package.json +++ b/package.json @@ -32,28 +32,33 @@ "build": "npm-run-all build:*", "build:node": "npm run babel:node", "build:ui": "cd embark-ui && npm run build", - "clean": "rimraf dist embark-*.tgz package embark-ui/build", + "check-no-prepare": "node scripts/check-no-prepare.js", + "check-working-tree": "node scripts/check-working-tree.js", + "clean": "npm-run-all clean:*", + "clean:core": "rimraf dist embark-*.tgz package", + "clean:ui": "cd embark-ui && npm run clean", "eslint": "eslint", - "install:core": "yarn install", + "install:core": "cross-env EMBARK_NO_PREPARE=t yarn install", "install:ui": "cd embark-ui && yarn install", "install_all": "npm-run-all install:*", "lint": "npm-run-all lint:*", "lint:js": "npm-run-all lint:js:*", - "lint:js:core": "eslint babel.config.js bin/embark src/bin/ src/lib/", + "lint:js:core": "eslint babel.config.js bin/embark scripts/ src/bin/ src/lib/", "lint:js:ui": "cd embark-ui && npm run lint", "lint:ts": "tslint -c tslint.json 'src/**/*.ts'", - "prepublishOnly": "npm-run-all clean build test", - "test": "npm-run-all lint test:*", - "test:core": "mocha dist/test/ --exit --no-timeouts --require source-map-support/register", - "test:test_app": "cross-env DAPP=\"test_app\" npm run test_dapp", - "test:contracts_app": "cross-env DAPP=\"contracts_app\" npm run test_dapp", - "test_dapp": "cross-env-shell \"cd test_apps/$DAPP && npm install && npm test\"", - "release": "./scripts/release", + "prepare": "npm run --silent check-no-prepare && npm-run-all install_all clean build || exit 0", + "prepublishOnly": "npm-run-all test check-working-tree", + "release": "node scripts/release.js", "start": "run-p start:*", "start:embark": "run-p start:embark:*", "start:embark:babel": "npm run babel:watch", "start:embark:type-check": "npm run type-check:watch", "start:ui": "cd embark-ui && npm run start", + "test": "npm-run-all lint test:*", + "test:core": "mocha dist/test/ --exit --no-timeouts --require source-map-support/register", + "test:test_app": "cross-env DAPP=test_app npm run test_dapp", + "test:contracts_app": "cross-env DAPP=contracts_app npm run test_dapp", + "test_dapp": "cross-env-shell \"cd test_apps/$DAPP && npm install && npm test\"", "tsc": "tsc", "tslint": "tslint", "type-check": "tsc", diff --git a/scripts/check-no-prepare.js b/scripts/check-no-prepare.js new file mode 100644 index 000000000..3774f9f7b --- /dev/null +++ b/scripts/check-no-prepare.js @@ -0,0 +1,3 @@ +/* global process */ + +process.exit(process.env.EMBARK_NO_PREPARE ? 1 : 0); diff --git a/scripts/check-working-tree.js b/scripts/check-working-tree.js new file mode 100644 index 000000000..a23fe4453 --- /dev/null +++ b/scripts/check-working-tree.js @@ -0,0 +1,12 @@ +/* global process require */ + +const {execSync} = require('child_process'); + +try { + execSync('git status', {stdio: 'inherit'}); + process.exit( + execSync('git status --porcelain').toString().trim() === '' ? 0 : 1 + ); +} catch (e) { + process.exit(1); +} diff --git a/scripts/release b/scripts/release deleted file mode 100755 index b46109c4d..000000000 --- a/scripts/release +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env node - -const execSync = require('child_process').execSync; -const minimist = require('minimist'); -const standardVersion = require('standard-version'); -const chalk = require('chalk'); -const args = minimist(process.argv.slice(2)); - -const DEFAULT_UPSTREAM_REPO_ORIGIN = 'origin'; -const DEFAULT_UPSTREAM_REPO_BRANCH = 'master'; -const origin = args['repo-origin'] || DEFAULT_UPSTREAM_REPO_ORIGIN; -const branch = args['repo-branch'] || DEFAULT_UPSTREAM_REPO_BRANCH; -const distTag = args['npm-dist-tag']; - -console.log(chalk.blue('ℹ'), `Fetching from origin '${origin}' to read upstream version...`); -try { - console.log(execSync(`git fetch ${origin}`).toString()); -} catch (e) { - console.log(chalk.red('✘'), 'Couldn\'t fetch latest commits. Please check the error above.'); - return; -} - -const localRef = execSync(`git rev-parse ${branch}`).toString(); -const originRef = execSync(`git rev-parse ${origin}/${branch}`).toString(); - -if (localRef !== originRef) { - console.log(chalk.red('✘'), `Local branch '${branch}' is not up to date with '${origin}/${branch}'. Please update your local branch first.`); - return; -} - -console.log(chalk.green('✔'), 'Release branch is up to date with remote branch.'); - -console.log(chalk.blue('ℹ'), 'Reinstalling node_modules just to make sure that everything is up to date...'); - -try { - console.log(execSync('npm run install_all').toString()); - console.log(chalk.green('✔'), 'Depencencies installed.'); -} catch (e) { - console.log(chalk.red('✘'), 'A problem occured. Please check the error above'); - return; -} - -standardVersion({ - dryRun: args['dry-run'], - prerelease: args.prerelease, - sign: args.sign, - releaseAs: args['release-as'] -}).then(() => { - if (!args['dry-run']) { - console.log(chalk.blue('ℹ'), `Pushing release commit to origin '${origin}' on branch '${branch}'...`); - try { - const output = execSync(`git push ${origin} ${branch} --follow-tags`).toString(); - console.log(chalk.green(output)); - console.log(chalk.green('✔'), 'Successfully pushed release commit'); - } catch (e) { - console.log(chalk.red('✘'), 'Couldn\'t push release commit. Please check the error above.'); - return { error: true }; - } - } else { - console.log(chalk.blue('ℹ'), 'This is a dry run. Nothing\'s being pushed.'); - } -}).then(error => { - if (error) { - return error; - } - console.log(chalk.blue('ℹ'), 'Publishing new Embark version on npm...'); - - let npmPublishCommand = distTag ? `npm publish --tag ${distTag}` : 'npm publish'; - - npmPublishCommand += args['dry-run'] ? ' --dry-run' : ''; - - try { - const output = execSync(npmPublishCommand).toString(); - console.log(chalk.green(output)); - if (args['dry-run']) { - console.log(chalk.blue('ℹ'), 'This was a dry run: Successfuly published latest version.'); - } else { - console.log(chalk.green('✔'), 'Successfully published latest version.'); - } - } catch (e) { - console.log(chalk.red('✘'), 'Couldn\'t publish version on npm. Please check the error above.'); - return { error: true }; - } -}).then(error => { - if (error) { - console.log(chalk.red('✘'), 'Stopping right here. Make sure to clean up commits and tags if needed.'); - } else { - console.log(chalk.green('✔'), 'Woohoo! Done.'); - } -}); diff --git a/scripts/release.js b/scripts/release.js new file mode 100644 index 000000000..b6d32cd63 --- /dev/null +++ b/scripts/release.js @@ -0,0 +1,189 @@ +/* global process require */ + +const chalk = require('chalk'); +const {execSync} = require('child_process'); +const minimist = require('minimist'); +const {prompt} = require('promptly'); +const standardVersion = require('standard-version'); + +const args = minimist(process.argv.slice(2)); + +const DEFAULT_UPSTREAM_REPO_BRANCH = 'master'; +const DEFAULT_UPSTREAM_REPO_ORIGIN = 'origin'; +const branch = args['repo-branch'] || DEFAULT_UPSTREAM_REPO_BRANCH; +const origin = args['repo-origin'] || DEFAULT_UPSTREAM_REPO_ORIGIN; + +const distTag = args['npm-dist-tag']; +const dryRun = args['dry-run']; +const prerelease = args.prerelease; +const releaseAs = args['release-as']; +const sign = args.sign; + +// eslint-disable-next-line no-confusing-arrow +const dryRunMaybe = () => dryRun ? leftPad1(chalk.yellow('(DRY RUN)')) : ''; +const execSyncInherit = (cmd) => execSync(cmd, {stdio: 'inherit'}); +// eslint-disable-next-line no-confusing-arrow +const leftPad1 = (str) => ' ' + str; +// eslint-disable-next-line no-confusing-arrow +const leftPad1Maybe = (str) => str ? leftPad1(str) : str; +const log = (mark, str) => console.log(mark, str.filter(s => !!s).join(' ')); +const logError = (...str) => log(chalk.red('✘'), str); +const logInfo = (...str) => log(chalk.blue('ℹ'), str); +const logSuccess = (...str) => log(chalk.green('✔'), str); +const logWarning = (...str) => log(chalk.yellow('‼︎'), str); + +const yesNoValidator = (input) => { + const _input = input && input[0].toLowerCase(); + if (!['y', 'n'].includes(_input)) { + throw new Error(chalk.red('✘') + leftPad1(`Please answer [y]es or [n]o.`)); + } + return _input; +}; + +const promptOpts = {default: 'blank', validator: yesNoValidator}; + +const proceedAnywayPrompt = async () => { + let answer = await prompt( + `${chalk.yellow('⁉︎')} Proceed anyway? [y/n]`, + promptOpts + ); + if (answer === 'n') { + logWarning(`Stopping right here.`); + process.exit(0); + } +}; + +const dryRunPrompt = async () => { + let answer = await prompt( + `${chalk.blue('⁇')} This is ${chalk.yellow('NOT')} a --dry-run.` + + leftPad1(`Did you complete a successful --dry-run first? [y/n]`), + promptOpts + ); + if (answer === 'n') await proceedAnywayPrompt(); +}; + +(async () => { + try { + if (!dryRun) await dryRunPrompt(); + + logInfo(`Determining the current branch...`); + + let currentBranch; + try { + currentBranch = execSync(`git rev-parse --abbrev-ref HEAD`) + .toString() + .trim(); + } catch (e) { + logError(`Couldn't determine the branch. Please check the error above.`); + throw new Error(); + } + + if (currentBranch !== branch) { + logError( + `Current branch '${currentBranch}' is not the same as release branch`, + `'${branch}'. Please checkout the release branch before rerunning this`, + `script or rerun with '--repo-branch ${currentBranch}'.` + ); + throw new Error(); + } + + logSuccess(`Current branch and release branch are the same.`); + logInfo(`Checking the working tree...`); + + try { + execSyncInherit(`npm run --silent check-working-tree`); + logSuccess(`Working tree is clean.`); + } catch (e) { + logError( + `Working tree is dirty or has untracked files. Please make necessary`, + `changes or commits before rerunning this script.` + ); + throw new Error(); + } + + logInfo( + `Fetching from origin '${origin}' to compare local and remote branches...` + ); + + try { + execSyncInherit(`git fetch ${origin}`); + } catch (e) { + logError(`Couldn't fetch latest commits. Please check the error above.`); + throw new Error(); + } + + let localRef, originRef; + try { + localRef = execSync(`git rev-parse ${branch}`).toString(); + originRef = execSync(`git rev-parse ${origin}/${branch}`).toString(); + } catch (e) { + logError(`A problem occured. Please check the error above.`); + throw new Error(); + } + + if (localRef !== originRef) { + logError( + `Local branch '${branch}' is not in sync with '${origin}/${branch}'.`, + `Please sync branches before rerunning this script.` + ); + throw new Error(); + } + + logSuccess(`Local branch is in sync with remote branch.`); + logInfo(`Running Standard Version${dryRunMaybe()}...`); + + await standardVersion({ + dryRun, + prerelease, + releaseAs, + sign + }); + + logInfo(`Publishing new Embark version on npm${dryRunMaybe()}...`); + + const npmPublishCommand = [ + `npm publish`, + leftPad1Maybe(`${distTag ? `--tag ${distTag}` : ''}`), + leftPad1Maybe(`${dryRun ? '--dry-run' : ''}`) + ].join(''); + + try { + execSyncInherit(npmPublishCommand); + logSuccess(`Successfully published latest version${dryRunMaybe()}.`); + } catch (e) { + logError( + `Couldn't publish version on npm${dryRunMaybe()}.`, + `Please check the error above.` + ); + throw new Error(); + } + + logInfo( + `Pushing release commit and tag to origin '${origin}' on branch`, + `'${branch}'${dryRunMaybe()}...` + ); + + const gitPushCommand = [ + `git push --follow-tags ${origin} ${branch}`, + leftPad1Maybe(`${dryRun ? '--dry-run' : ''}`) + ].join(''); + + try { + execSyncInherit(gitPushCommand); + logSuccess(`Successfully pushed${dryRunMaybe()}.`); + } catch (e) { + logError( + `Couldn't push${dryRunMaybe()}. Please check the error above.` + ); + throw new Error(); + } + + logSuccess(`Woohoo! Done${dryRunMaybe()}.`); + } catch (e) { + logError( + `Stopping right here${dryRunMaybe()}.`, + dryRun ? '' : `Make sure to clean up commits and tags as necessary.` + ); + process.exit(1); + } +})();