From 3b4ec1cdc39b62549ab34a07bab8a1b592596ddb Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Fri, 9 Mar 2018 20:49:37 +0100 Subject: [PATCH 1/4] improve the deploy process using circleci --- .circleci/config.yml | 174 +++++++++++++++++++++++++++++ .npmignore | 1 + maintenance/find-unused-exports.ts | 24 ---- maintenance/release.ts | 118 +++++++++++++++++++ maintenance/tsconfig.json | 14 +++ package.json | 14 ++- src/lib/logger.test.ts | 4 +- tsconfig.json | 2 +- 8 files changed, 320 insertions(+), 31 deletions(-) create mode 100644 .circleci/config.yml delete mode 100644 maintenance/find-unused-exports.ts create mode 100644 maintenance/release.ts create mode 100644 maintenance/tsconfig.json diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..b6adc63f --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,174 @@ +# Javascript Node CircleCI 2.0 configuration file +# +# Check https://circleci.com/docs/2.0/language-javascript/ for more details +# + +# Define the used images +node6: &node6 + working_directory: &workdir_node6 ~/node-tradfri-client/node6 + docker: + - image: circleci/node:6 + +# use "latest" for lint and publish +latest: &latest + working_directory: &workdir_latest ~/node-tradfri-client/latest + docker: + - image: circleci/node:8 + +# some job filters +tags_and_branches: &tags_and_branches + filters: + tags: + only: /^v.*/ + +only_tags: &only_tags + filters: + tags: + only: /^v.*/ + branches: + ignore: /.*/ + +# Which files to preserve between jobs +whitelist: &whitelist + paths: + - build/* + - node_modules/* + - src/* + - test/* + - coverage/* + - .npmignore + - package.json + - README.md + - tsconfig.json + - tslint.json + +version: 2 +jobs: +# ---------------------------------------------------- + test_node6: + <<: *node6 + + steps: + - checkout + + # Download and cache dependencies + - restore_cache: + keys: + - v1-dependencies-node6-{{ checksum "package.json" }} + + - run: + name: Install Dependencies + command: npm install + - run: + name: Remove unnecessary Dependencies + command: npm ls || true ; npm prune + + - save_cache: + paths: + - node_modules + key: v1-dependencies-node6-{{ checksum "package.json" }} + + - run: + name: Run component tests + command: npm run test + +# ---------------------------------------------------- + test_latest: + <<: *latest + + steps: + - checkout + + # Download and cache dependencies + - restore_cache: + keys: + - v1-dependencies-latest-{{ checksum "package.json" }} + + - run: + name: Install Dependencies + command: npm install + - run: + name: Remove unnecessary Dependencies + command: npm ls || true ; npm prune + + - save_cache: + paths: + - node_modules + key: v1-dependencies-latest-{{ checksum "package.json" }} + + # on latest also lint the code + - run: + name: Lint TypeScript code + command: npm run lint + + - run: + name: Run component tests + command: npm run test + + # persist the build files, so we can pick them up in the deploy job + - persist_to_workspace: + root: *workdir_latest + <<: *whitelist + +# ---------------------------------------------------- +# generates a clean build of the TypeScript sources +# to make sure there were no leftover build files + build: + <<: *latest + + steps: + - attach_workspace: + at: *workdir_latest + + - run: + name: Clean previous builds + command: npm run prebuild + + - run: + name: Build the source files + command: npm run build + + - persist_to_workspace: + root: *workdir_latest + <<: *whitelist + +# ---------------------------------------------------- +# Deploys the final package to NPM + deploy: + <<: *latest + + steps: + - attach_workspace: + at: *workdir_latest + + - run: + name: Login to npm + command: npm config set //registry.npmjs.org/:_authToken=$NPM_TOKEN + - run: + name: Publish package to npm + command: npm publish + +# ---------------------------------------------------- +# Fit it all together +workflows: + version: 2 + + test-and-deploy: + jobs: + - test_node6: + <<: *tags_and_branches + - test_latest: + <<: *tags_and_branches + + # build only on tagged builds and if all tests succeeded + - build: + requires: + - test_node6 + - test_latest + <<: *only_tags + + # deploy only on tagged builds and if all tests succeeded + - deploy: + requires: + - build + <<: *only_tags diff --git a/.npmignore b/.npmignore index 7ea32406..1ba4cbff 100644 --- a/.npmignore +++ b/.npmignore @@ -15,3 +15,4 @@ obj/ coverage/ maintenance/ .travis.yml +.circleci/ diff --git a/maintenance/find-unused-exports.ts b/maintenance/find-unused-exports.ts deleted file mode 100644 index 9457170a..00000000 --- a/maintenance/find-unused-exports.ts +++ /dev/null @@ -1,24 +0,0 @@ -// This script helps find unused exports. -// It has some false positives, so be careful what you remove - -// run with `npm run find-unused-exports` - -import * as fs from "fs"; -import * as path from "path"; -import analyzeTsConfig from "ts-unused-exports"; - -const files = ["./src/index.ts", "./src/tradfri-client.ts"]; -files.push(... - fs - .readdirSync("./src/lib") - .filter(p => p.endsWith(".ts") && !p.endsWith(".test.ts")) - .map(f => path.join("./src/lib", f)), -); - -const result = analyzeTsConfig("./tsconfig.json", files); -for (const file of Object.keys(result)) { - if (file === "src") continue; - const unused = result[file]; - console.warn(`${file}.ts has ${unused.length} unused symbols:`); - unused.forEach(u => console.warn(" " + u)); -} diff --git a/maintenance/release.ts b/maintenance/release.ts new file mode 100644 index 00000000..df64de95 --- /dev/null +++ b/maintenance/release.ts @@ -0,0 +1,118 @@ +// tslint:disable:no-var-requires +// tslint:disable:no-console + +/* + + Bumps the package version and releases a new tag + to set off a CI and npm release run + + CALL THIS WITH: + npm run release -- [ [ [--dry] + +*/ + +import { execSync } from "child_process"; +import * as fs from "fs"; +import * as path from "path"; +import * as readline from "readline"; +const semver = require("semver"); +const colors = require("colors/safe"); +import { argv } from "yargs"; + +const rootDir = path.resolve(__dirname, "../"); + +const packPath = path.join(rootDir, "package.json"); +const pack = require(packPath); +// const ioPackPath = path.join(rootDir, "io-package.json"); +// const ioPack = require(ioPackPath); + +function fail(reason: string) { + console.error(""); + console.error(colors.red(reason)); + console.error(""); + process.exit(0); +} + +// check if there are untracked changes +const gitStatus = execSync("git status", {cwd: rootDir, encoding: "utf8"}); +if (/have diverged/.test(gitStatus)) { + if (!argv.dry) fail(colors.red("Cannot continue, the local branch has diverged from the git repo!")); + else console.log(colors.yellow("This is a dry run. The full run would fail due to a diverged branch\n")); +} else if (!/working tree clean/.test(gitStatus)) { + if (!argv.dry) fail(colors.red("Cannot continue, the local branch has uncommited changes!")); + else console.log(colors.yellow("This is a dry run. The full run would fail due to uncommited changes\n")); +} else if (/Your branch is behind/.test(gitStatus)) { + if (!argv.dry) fail(colors.red("Cannot continue, the local branch is behind the remote changes!")); + else console.log(colors.yellow("This is a dry run. The full run would fail due to the local branch being behind\n")); +} else if (/Your branch is up\-to\-date/.test(gitStatus) || /Your branch is ahead/.test(gitStatus)) { + // all good +} + +const releaseTypes = ["major", "premajor", "minor", "preminor", "patch", "prepatch", "prerelease"]; + +const releaseType = argv._[0] || "patch"; +let newVersion = releaseType; +const oldVersion = pack.version as string; +if (releaseTypes.indexOf(releaseType) > -1) { + if (releaseType.startsWith("pre") && argv._.length >= 2) { + // increment to pre-release with an additional prerelease string + newVersion = semver.inc(oldVersion, releaseType, argv._[1]); + } else { + newVersion = semver.inc(oldVersion, releaseType); + } + console.log(`bumping version ${colors.blue(oldVersion)} to ${colors.gray(releaseType)} version ${colors.green(newVersion)}\n`); +} else { + // increment to specific version + newVersion = semver.clean(newVersion); + if (newVersion == null) { + fail(`invalid version string "${newVersion}"`); + } else { + // valid version string => check if its actually newer + if (!semver.gt(newVersion, pack.version)) { + fail(`new version ${newVersion} is NOT > than package.json version ${pack.version}`); + } + // if (!semver.gt(newVersion, ioPack.common.version)) { + // fail(`new version ${newVersion} is NOT > than io-package.json version ${ioPack.common.version}`); + // } + } + console.log(`bumping version ${oldVersion} to specific version ${newVersion}`); +} + +if (argv.dry) { + console.log(colors.yellow("dry run:") + " not updating package files"); +} else { + console.log(`updating package.json from ${colors.blue(pack.version)} to ${colors.green(newVersion)}`); + pack.version = newVersion; + fs.writeFileSync(packPath, JSON.stringify(pack, null, 2)); + + // console.log(`updating io-package.json from ${colors.blue(ioPack.common.version)} to ${colors.green(newVersion)}`); + // ioPack.common.version = newVersion; + // fs.writeFileSync(ioPackPath, JSON.stringify(ioPack, null, 4)); +} + +const gitCommands = [ + `git add -A`, + `git commit -m "release v${newVersion}"`, + `git push`, + `git tag v${newVersion}`, + `git push origin --tags`, +]; +if (argv.dry) { + console.log(colors.yellow("dry run:") + " I would execute this:"); + for (const command of gitCommands) { + console.log(" " + command); + } +} else { + for (const command of gitCommands) { + console.log(`executing "${colors.blue(command)}" ...`); + execSync(command, {cwd: rootDir}); + } +} + +console.log(""); +console.log(colors.green("done!")); +console.log(""); + +process.exit(0); diff --git a/maintenance/tsconfig.json b/maintenance/tsconfig.json new file mode 100644 index 00000000..bed3dad0 --- /dev/null +++ b/maintenance/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compileOnSave": true, + "compilerOptions": { + "declaration": false, + "noEmit": true, + "module": "commonjs", + "moduleResolution": "node", + "noImplicitAny": true, + "target": "es2015", + }, + "include": [ + "./*.ts" + ] +} diff --git a/package.json b/package.json index 0660189f..e1320adb 100644 --- a/package.json +++ b/package.json @@ -42,19 +42,23 @@ "@types/proxyquire": "^1.3.28", "@types/sinon": "^4.1.3", "@types/sinon-chai": "^2.7.29", + "@types/yargs": "^11.0.0", "chai": "^4.1.2", "chai-as-promised": "^7.1.1", + "colors": "^1.1.2", "coveralls": "^3.0.0", "mocha": "^5.0.0", "nyc": "^11.4.1", "proxyquire": "^1.8.0", + "rimraf": "^2.6.2", + "semver": "^5.5.0", "sinon": "^4.2.2", "sinon-chai": "^2.14.0", "source-map-support": "^0.5.3", "ts-node": "^4.1.0", - "ts-unused-exports": "^2.0.4", "tslint": "^5.9.1", - "typescript": "^2.7.1" + "typescript": "^2.7.1", + "yargs": "^11.0.0" }, "dependencies": { "bonjour": "^3.5.0", @@ -63,15 +67,17 @@ "reflect-metadata": "^0.1.12" }, "scripts": { + "build": "tsc", + "prebuild": "rimraf ./build", "watch": "tsc --watch", "test:ts": "node_modules/.bin/mocha", "test": "npm run test:ts", "coverage": "node_modules/.bin/nyc npm test", - "show-coverage": "npm run coverage && start ./coverage/index.html", "coveralls": "node_modules/.bin/nyc report --reporter=text-lcov | coveralls -v", + "show-coverage": "npm run coverage && start ./coverage/index.html", "lint:ts": "tslint", "lint": "npm run lint:ts \"src/**/*.ts\"", - "find-unused-exports": "node --require ts-node/register ./maintenance/find-unused-exports.ts" + "release": "node --require ts-node/register maintenance/release.ts" }, "nyc": { "all": true, diff --git a/src/lib/logger.test.ts b/src/lib/logger.test.ts index 8e31962b..38088586 100644 --- a/src/lib/logger.test.ts +++ b/src/lib/logger.test.ts @@ -9,7 +9,7 @@ import * as debugPackage from "debug"; const debugSpy = stub(); import * as proxyquire from "proxyquire"; const { log, setCustomLogger } = proxyquire("./logger", { - "debug": stub().callsFake(namespace => { + debug: stub().callsFake(namespace => { if (namespace === "node-tradfri-client") return debugSpy; return debugPackage(namespace); }), @@ -46,7 +46,7 @@ describe("lib/logger => ", () => { log("message"); debugSpy.should.have.been.calledOnce; debugSpy.should.have.been.calledWith("message"); - }) + }); it(`using the default logger prepends the severity to the message in UPPERCASE`, () => { // except for info diff --git a/tsconfig.json b/tsconfig.json index 94ac5ba1..65452b66 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,7 @@ "stripInternal": true, "target": "es2015", "strictPropertyInitialization": true, - "watch": true + "watch": false // true breaks circleci }, "include": [ "src/**/*.ts" From d8647df5b2981ddfe925fdd6da28745b859fa7c0 Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Fri, 9 Mar 2018 22:14:32 +0100 Subject: [PATCH 2/4] finalize circleci branch --- .circleci/config.yml | 23 +++++++++++++++++++++++ .travis.yml | 12 ------------ README.md | 2 +- maintenance/release.ts | 2 +- 4 files changed, 25 insertions(+), 14 deletions(-) delete mode 100644 .travis.yml diff --git a/.circleci/config.yml b/.circleci/config.yml index b6adc63f..bb6dfc5e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -110,6 +110,24 @@ jobs: root: *workdir_latest <<: *whitelist +# ---------------------------------------------------- +# generates a clean build of the TypeScript sources +# to make sure there were no leftover build files + coverage: + <<: *latest + + steps: + - attach_workspace: + at: *workdir_latest + + - run: + name: Generate coverage report + command: npm run coverage + + - run: + name: Upload it to coveralls.io + command: npm run coveralls + # ---------------------------------------------------- # generates a clean build of the TypeScript sources # to make sure there were no leftover build files @@ -160,6 +178,11 @@ workflows: - test_latest: <<: *tags_and_branches + - coverage: + requires: + - test_latest + <<: *tags_and_branches + # build only on tagged builds and if all tests succeeded - build: requires: diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e7cc85b6..00000000 --- a/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: node_js -node_js: - - '6' - - '8' -os: - - linux -script: - - npm run coverage -after_success: - - npm run coveralls -notifications: - email: false diff --git a/README.md b/README.md index e4666539..b833b9dd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # node-tradfri-client Library to talk to IKEA Trådfri Gateways without external binaries -[![Build Status](https://travis-ci.org/AlCalzone/node-tradfri-client.svg?branch=master)](https://travis-ci.org/AlCalzone/node-tradfri-client) +[![Build Status](https://img.shields.io/circleci/project/github/AlCalzone/node-tradfri-client.svg)](https://circleci.com/gh/AlCalzone/node-tradfri-client) [![Coverage Status](https://img.shields.io/coveralls/github/AlCalzone/node-tradfri-client.svg)](https://coveralls.io/github/AlCalzone/node-tradfri-client) *Requires NodeJS >= 6.x* diff --git a/maintenance/release.ts b/maintenance/release.ts index df64de95..989db2ac 100644 --- a/maintenance/release.ts +++ b/maintenance/release.ts @@ -94,7 +94,7 @@ if (argv.dry) { const gitCommands = [ `git add -A`, - `git commit -m "release v${newVersion}"`, + `git commit -m "release v${newVersion} [skip ci]"`, `git push`, `git tag v${newVersion}`, `git push origin --tags`, From 02b97e0966cd66552b4259d493377311024a053c Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Fri, 9 Mar 2018 22:34:09 +0100 Subject: [PATCH 3/4] fix coveralls (hopefully) --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index bb6dfc5e..16c8674a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -126,7 +126,7 @@ jobs: - run: name: Upload it to coveralls.io - command: npm run coveralls + command: COVERALLS_SERVICE_NAME=CircleCI COVERALLS_SERVICE_JOB_ID=$CIRCLE_JOB npm run coveralls # ---------------------------------------------------- # generates a clean build of the TypeScript sources From 1da7d3ff78a4ca7ef5ba4fe758a7da61d3fa8743 Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Fri, 9 Mar 2018 22:45:30 +0100 Subject: [PATCH 4/4] update coveralls job data --- .circleci/config.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 16c8674a..5d6f9de0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -124,9 +124,12 @@ jobs: name: Generate coverage report command: npm run coverage + - run: export COVERALLS_SERVICE_NAME="CircleCI" + - run: export COVERALLS_SERVICE_JOB_ID="$CIRCLE_BUILD_NUM" + - run: name: Upload it to coveralls.io - command: COVERALLS_SERVICE_NAME=CircleCI COVERALLS_SERVICE_JOB_ID=$CIRCLE_JOB npm run coveralls + command: npm run coveralls # ---------------------------------------------------- # generates a clean build of the TypeScript sources